Object Lifecycle
Lifecycle Methods in Enyo
A Trivial Kind
Enyo的kind使用常规的js原型。一个kind有简单的生命周期。
1 var MyKind = enyo.kind({ 2 kind: null, // otherwise it will default to 'Control' 3 constructor: function() { 4 // do any initialization tasks 5 } 6 });
现在MyKind是一个可以使用new方法创建实例的函数了。MyKind函数是一个调用构造函数的副本。
myInstance = new MyKind();
和所有的js对象一样,当它不再被使用时会被垃圾回收。
注意:尽管Component和Control 类型的kind有一些高级特性,enyo.kind()本身非常简单。
Inheritance: this.inherited()
可以以一个kind为基础构造另一个kind。新的kind会继承父kind的所有属性和方法。如果你重写了父kind的方法,你可以使用inherited()方法调用被重写的方法。
1 var MyNextKind = enyo.kind({ 2 kind: MyKind, 3 constructor: function() { 4 // do any initialization tasks before MyKind initializes 5 // 6 // do inherited initialization (optional, but usually a good idea) 7 this.inherited(arguments); 8 // 9 // do any initialization tasks after MyKind initializes 10 } 11 });
MyNextKind刚开始拥有MyKind的所有属性和方法,然后我们重写构造函数。我们写的新构造函数使用this.inherited(arguments)调用父类的构造函数。MyNextKind可以在调用父类构造函数前或调用后做一些其他的事也可以不调用父类的构造方法。
所有方法的重写都和构造函数的重写一样。
注意,你可以像这样使用原始js调用继承方法:
MyKind.prototype.<method name>.apply(this, arguments);
this.inherited的语法更简短,而且不需要指定父kind的名字。另外,arguments是js内置的参数,它作为一个伪数组传递给当前执行的函数。
Components: create() and destroy()
如上所述,所有的kind都有构造方法。Component kind添加了一些新的生命周期方法,主要是create()和destroy():
1 var myComponent = enyo.kind({ 2 kind: enyo.Component, 3 constructor: function() { 4 // low-level or esoteric initialization, usually not needed at all 5 this.inherited(arguments); 6 }, 7 create: function() { 8 // create is called *after* the constructor chain is finished 9 this.inherited(arguments); 10 // this.$ hash available only *after* calling inherited create 11 }, 12 destroy: function() { 13 // do inherited teardown 14 this.inherited(arguments); 15 } 16 });
严格来讲,create()不同于构造函数,在create()方法调用前构造函数已经调用执行完毕,但使用这两种方法都可以正常初始化。
方便起见,大部分components可以忽略构造函数,使用create()即可。
Create()方法的另一个重要特性是 在Component.create()方法执行后this.$ hash值就可用了。这意味着你重写create()一般是用来初始化自定义component的。
1 components: [ 2 {kind: "MyWorker"} 3 ], 4 create: function() { 5 this.inherited(arguments); 6 // put MyWorker to work 7 this.$.myWorker.work(); 8 }
component组件经常引用其他组件。在上面的例子中,MyWorker实例引用this.myWorker.$.myWorker。destroy()方法清理这些引用,并允许组件被垃圾回收。组件类型kind一般也使用destroy()方法清楚不在使用的垃圾组件。
1 destroy: function() { 2 // stop MyWorker from working 3 this.$.myWorker.stop(); 4 // standard cleanup 5 this.inherited(arguments); 6 // this.$ hash is empty now 7 }
注意,如果你已经创造了一个不会被destroy()方法销毁的component的自定义引用,那么当调用destroy()方法时并不会真正将该对象垃圾回收。因此,每个component都有一个this.destroyed标志,如果为true那么该Component已经被销毁,引用也已被移除。
Controls: hasNode() and rendered()
Control也是一种component,所有control拥有如上所述的constructor()、create()、destroy()方法,另外Control还有一些控制DOM节点的方法。
Control的大部分方法和属性都是可用的,与是否存在相应的DOM节点无关。
this.$.control.applyStyle("color", "blue");
如果control已经渲染了,那么样式会应用在DOM上。如果没有,样式会在DOM节点创建时应用。无论如何,当你看到文字时已经是蓝色的了。
有一些事情只有在DOM创建后才能做。显然,如果没有DOM节点不能直接操作DomNode。一些方法(如getBounds())返回DOM的测量值,如果没有DOM就得不到准确值。
在control里,如果你需要在DOM创建后立即执行一些操作,你可以使用rendered()方法。
1 rendered: function() { 2 // important! must call the inherited method 3 this.inherited(arguments); 4 // this is the first moment bounds are available 5 this.firstBounds = this.getBounds(); 6 }
另外要注意的是,在rendered()方法被调用时即使DOM节点已经创建,这些节点在浏览器中通常还不能立即看到。在enyo交出js线程的控制权之前,大部分浏览器不会刷新页面。这样你可以在rendered()函数中改变DOM而不会引起浏览器的闪烁。
当你需要直接操作control的DOM节点时,可使用hasNode()方法。
1 twiddle: function() { 2 if (this.hasNode()) { 3 buffNode(this.node); // buffNode is made-up 4 } 5 }
即使DOM已经渲染了,this.node也可能没有值。hasNode()的结果为true意味着this.node已经初始化。即使在rendered()内部,也需要首先调用hasNode()方法。
1 rendered: function() { 2 this.inherited(arguments); // important! must call the inherited method 3 if (this.hasNode()) { 4 buffNode(this.node); 5 } 6 });
When Controls Are Rendered
一般,只有在你显示调用control时它才会进行渲染。大部分应用都有一个顶级renderInto()方法或write()方法来渲染应用中的control。
当动态创建control时,enyo只有在你调用它们的render()方法或者调用它们容器类的render()方法时才会渲染它们。例:
1 updateControls: function() { 2 // destroy controls we made last time 3 // ('destroyClientControls' destroys only dynamic controls; 4 // 'destroyControls' destroys everything) 5 this.destroyClientControls(); 6 // create new controls 7 for (var i=0; i<this.count; i++) { 8 // created but not rendered 9 this.createComponent({kind: "myCoolControl", index: i}); 10 } 11 // render everything in one shot 12 this.render(); 13 });
销毁一个control会使它马上从DOM中移除,这是清理程序必须执行的部分。没有unrender()方法。
A Note About Kind Names
之前的例子使用这样的语法创建kind:
var MyKind = enyo.kind(...);
但在大部分的enyo代码中,你会看到下面这样的代码:
enyo.kind({
name: "MyKind"
...
});
这里的name属性是可选的。如果包含它,enyo会在全局中使用该name来引用你的kind,它的实例可以通过this.kindName来引用。为了操作方便可经常使用name语法。例如,我们可以把
1 var foo = { 2 kinds: { 3 MyKind: enyo.kind(...); 4 } 5 }
写成这样
1 enyo.kind({ 2 name: "foo.kinds.MyKind", 3 ... 4 });