Upload
sencha
View
207
Download
1
Embed Size (px)
Citation preview
Modernizing the Ext JS Class System
Don Griffin@dongryphon
Agenda
• Background- Goals for language and tools
- Tool landscape
• Class system content- Modules
- Classes & Mixins
- Life-cycle
• Timeline- WARNING: Code names ahoy!
Goals
Future-proofing
Balance Between…
Compatibility
Future-proofing
Balance Between…
Compatibility• Ext.define works as before
• Existing code organization is supported
• Ext.require works as before
• callParent must be available
• Ext namespace is globally available
• class syntax can derive from any class
• Module-based structure is first-class
• import and export syntax is supported
• super calls must work (strict mode)
• Ext is scoped to its module
Sencha will provide an upgrade tool to transform a code base to ES6 form!
Language Standards
ES6 Score Cards
Windows 7http://kangax.github.io/compat-table/es6/
?
Tools Enable Language Adoption
Build Tool Trends
https://www.google.com/trends/explore?cat=5&q=grunt,gulp,webpack
Node.js
Sencha Build (*)Open tooling platform
Scanner
Optimizer
Indexer
Compressor
…
webpackplugins
rollup
gulp
SystemJS
??
Transpilers and Source Maps
Transpilers
• Transpilers (like Babel) are JavaScript-to-JavaScript compilers
• Transpilers go beyond “polyfills” by providing missing language syntax!
• Transpilers are currently the only platforms that accept imports, exports and decorators
• Transpilers allow us to future-proof our code (unblocking the language adoption curve)- No more “least common denominator” development!
- Mostly…
- Not all API’s can be polyfilled (Proxy)
Source Maps
• Source Maps enable the debugger to display the original source code…- …not inscrutable transpiler output!
• Source Maps have been around since 2012- This is great because they are widely supported
• Source Maps are supported by:- Chrome, Firefox, Edge, Safari
- IE11 (with Windows 8.1 Update)
Trust But Verify
• Transpilers can produce some scary code- Only transpile away what your target browser(s) do not support
- Check what is produced if performance or code size is a concern
- Especially if you need to support browsers like IE 11… or Safari 9 (or older)
• Even natively supported idioms can have surprising performance- The “for…of” loop construct – awesome syntax! Not-so-great performance
https://kpdecker.github.io/six-speed/
Trust But Verifyfor...of
var data = [1, 2, 3];var ret = '';
for (var value of data) { ret += value;}
var data = [1, 2, 3];var ret = '';
for (var i = 0; i < data.length; ++i) { ret += data[i];}
Class System Contents
Namespaces vs Modules
Ext = {};Ext.global = window;Ext.global.Ext = Ext;
Ext.define('App.some.Thing', { extend: 'App.foo.Bar', requires: [ 'Ext.grid.Panel' ],
Namespaces
• Namespaces are nested objects
• Ext.define() adds to the namespace
• The Loader maps names to URL’s
• Files are loaded at global scope
Ext = { global: { Ext: Ext, App: { foo: { Bar: constructor }, some: { Thing: constructor } } }, grid: { Panel: constructor } }
Ext.Loader.setPath({ Ext: '../ext/src', App: './app' });
// Ext.grid.Panel // => '../ext/src/grid/Panel.js'
// App.foo.Bar // => './app/foo/Bar.js'
var foo = 'bar'; // private, not global
export var abc = 'abc';
export default class Bar { // ...}
Modules
• Modules are files
• Modules execute in a private scope (not global)
• Modules publish values using export
• Modules can have one, unnamed “default” export value
• Modules use import to acquire values exported by other modules
• Modules are imported by path or name
• Named modules are resolved by the module loader
import { abc } from '../path/file';
import Bar from '../path/file.js';
import Ext from '@extjs/kernel';
import { define } from '@extjs/kernel';
./path/file.js
./other/thing.js
import { button } from '@extjs/modern';
import { hbox } from '@extjs/modern/layout';
import { Ext_Button } from '@extjs/modern';
Importing Components
• Classes are exported in several ways:- By xtype
- By alias family
- By class name
• These would be used where strings might be used today
• These exports can be generated by Sencha Build
Sencha Cmd Understands Namespaces
Sencha Build Understands Modules
Classes
Ext.define('App.some.Thing', { extend: 'App.foo.Bar', requires: [ 'Ext.grid.Panel' ],
alias: 'thing', text: 'Hello',
constructor: function (config) { this.callParent([config]); },
method: function (x) { return this.callParent([x]) * 42; },
statics: { create: function (cfg) { cfg.type = 'thing'; return this.callParent([cfg]); } }});
Classes Today
Ext.define()
• Inheritance
• Directives
• Properties
• Constructors
• Methods
• Static methods
• Super calls / callParent
Classes Today
__proto__
prototype
create
App.some.Thing
__proto__constructormethodself
App.some.Thing.prototype
{fn} {fn}{fn}
Function.prototype App.foo.Bar.prototype
Object.prototype
__proto__
foo 42
new App.some.Thing()
import { define } from '@extjs/kernel';import { grid } from '@extjs/modern';import Bar from 'app/foo';
@define('App.some.Thing', { alias: 'thing', text: 'Hello'})class Thing extends Bar { method (x) { return super.method(x) * 42; }
static create (cfg) { cfg.type = 'thing'; return super.create(cfg); }}export default Thing;
Classes in ES6+
import - export - class - @define
• Inheritance
• Directives
• Properties
• Methods
• Static methods
• Super calls
Decorator
function define (className, body) { return T => { T.define(className, body); };}
@define('App.some.Thing', { alias: 'thing', text: 'Hello'})class Thing extends Bar {}
define('App.some.Thing', { alias: 'thing', text: 'Hello'})(Thing);
Decorators
• Decorators are functions that manipulate classes (or methods)
• Decorators can accept arguments…- But must return a function to call as if no
arguments were provided
• The @decorator syntax is just a different way to call these functions
• Decorators are currently a Stage 2 proposal (not yet standardized)
// @define('App.some.Thing', {// alias: 'thing',// text: 'Hello'// })
https://github.com/tc39/proposals
Classes in ES6+
__proto__
prototype
create
App.some.Thing
__proto__constructmethodconstructor
App.some.Thing.prototype
{fn} {fn}{fn}
Function.prototype
App.foo.Bar.prototype
Object.prototype
App.foo.Bar Same meaningas constructor
was previously
Classes Before
__proto__
prototype
create
App.some.Thing
__proto__constructormethodself
App.some.Thing.prototype
{fn} {fn}{fn}
Function.prototypeApp.foo.Bar.prototype
Object.prototype
Classes After
__proto__
prototype
create
App.some.Thing
__proto__constructmethodconstructor
App.some.Thing.prototype
{fn} {fn}{fn}
Function.prototype
App.foo.Bar.prototype
Object.prototype
App.foo.Bar
class Foo { constructor (x) { this.x = 42; }}
new Foo(42);
let obj = {};Foo.call(obj, 42);
Restrictions on constructor
• A constructor can only be called with the new operator.
• Calling a constructor with call() or apply() throws a TypeError.
Mixins
Ext.define('App.mixin.Foo', { method: function (x) { return x * 42; }});
Ext.define('App.some.Thing', { extend: 'App.foo.Bar', mixins: [ 'App.mixin.Foo' ],});
Ext.define('App.some.OtherThing', { extend: 'App.foo.Bar', mixins: [ 'App.mixin.Foo' ],
method: function (x) { return 10 * this.mixins.foo.method.call(this, x); }});
Mixins Today
• Mixins ≈ multiple inheritance
• Mixins are classes
• Methods from the mixin prototype are copied to the target class prototype
• Unless there is already a method present
• The mixins object references the various mixin prototypes
thing.method(2);
this.mixins.observable.constructor.call(this);this.mixins.observable.construct.call(this);
import { Ext, define } from '@extjs/kernel';import Bar from 'app/foo';
class Mixable extends Ext.Base { method (x) { return x * 42; }}
@define({ mixins: [ Mixable ] })class Thing extends Bar { method (x) { return 10 * this.mixins.mixable.method.call(this, x); }}
@define({ extend: [ Mixable ] })class OtherThing extends Bar { method (x) { return 10 * super.method(x); }}
Mixins in ES6+
• Mixins are classes (that extend Ext.Base)
• Use @define to mix in the mixin(s)
• Mixins will be able to act more like a true base- The extend directive accepts mixins
- Calls to overlapping methods are handled in the super call
Common Object Life-cycle
The Life-cycle of Ext.BaseCreation• Instances are created with operator new- or Ext.create() or other factory
• The native constructor is available to ES6 classes- Not recommended in most cases
• The construct method is available to all classes- The new name for the old constructor
The Life-cycle of Ext.BaseConfiguration• Use the config directive to define configuration properties
• Config system calls your apply and update methods
• Use the built-in, single-object-parameter form of the construct method
• Ext.Base#construct calls initConfig (if needed)
The Life-cycle of Ext.BaseDestruction• Cleanup is handled by destroy• Sets flags like destroying and destroyed• Ignores multiple calls
• Calls destruct (just once)
• Clears instance properties to avoid memory leaks
The Life-cycle of Ext.BaseTemplate methods (*)• ctor- Called to initialize an instance
- Called after the ctor of all base classes and mixins
• dtor - Called to cleanup an instance
- Called before the dtor of bases or mixins
• No super calls required- All implementations of ctor and dtor are called automatically
import Ext from '@extjs/kernel';
class A extends Ext.Base { construct (x) { super.construct(); this.x = 42; this.x2 = this.x * 10; }}
class B extends A { construct (x, y) { super.construct(x); this.y = y; this.y2 = y * 427; }}
@define({ mixins: [ B ] })class C extends Ext.Base { construct (x, y) { super.construct(); this.mixins.b.construct(x, y); this.c = this.x + this.y + this.x2 + this.y2; }}
new C(4, 42);
Life-cycles & Diamonds
A
Ext.Base
B
C
import { Ext, define } from '@extjs/kernel';
@define({ config: { x: 0 }})class A extends Ext.Base { updateX (x) { this.x2 = x * 10; }}
class B extends A { ctor () { this.y2 = this.y * 427; }}
@define({ mixins: [ B ] })class C extends Ext.Base { ctor () { this.c = this.getX() + this.y + this.x2 + this.y2; }}
new C({ x: 2, y: 42 });
Life-cycles & Diamonds
A
Ext.Base
B
C
Life-cycles & Diamonds
A
Ext.Base
B
C
• ctor methods are called once per class- In base-to-derived order (including mixins!)
• dtor methods are called once per class- In derived-to-base order
• Ext.Base#construct calls ctor
• Ext.Base#destruct calls dtor
• ctor/dtor methods are gathered lazily- Never unless construct/destruct are used
ctor
ctor
ctor
ctor
dtor
dtor
dtor
dtor
The Road Ahead
The Road AheadSencha Cmd 6.5
• Available now (Early Access)!
• Uses Google Closure Compiler
• Supports much of the ES6 syntax (http://kangax.github.io/compat-table/es6/)
• Provides polyfills as well
• Transpiling is optional
• New compressor (replaces YUI) can process native ES6 code- Only compressor currently to do so!
The Road AheadExt JS.Next
• Sencha Build and Sencha Cmd
• Upgrade tool to convert from projects from Cmd to Build- See Ross’s talk!
• Estimated at 2nd half of 2017