很多情况下我们需要将代码和操作封装为一个单独的可以复用的组件(component)。
组件是通过Ractive.extent()方法创建的,这个方法可以自定义模板、初始化自定义方法、默认参数等等。
初始化选项(Initiallisation Options)
大部分的Ractive的常规的初始化选项对组件都是使用的,特别是生命周期事件,包括但不限于:
- onconstruct:在实例创建后立刻并只触发一次,将所有参数传入初始化的组建中
- onconfig:在所有配置选项执行后立刻并只触发一次
- oninit:当组件渲染前立刻并只触发一次,可以利用这个事件为实例创建事件监听器(proxy event listeners or onservers)
- onrender:在组件渲染后触发
- oncompleate:在组件渲染之后,任何转换(transition)完成后触发,此时组件DOM是可用的
- isolated:上述事件都是函数,这个选项是一个布尔值,默认为fasle。默认情况下组件可以调用父辈组件的变量,如果设置为true则不可以。
创建组件并插入
创建组件使用Ractive.extent()方法,插入组件有两种方法:
- 父级模板的Ractive实例对象中注册组件,在父级模板中直接使用组件,这种方法模板传入的数据通过在模板中定义相关的属性来传入(也就是说标签中的定义属性与组件实例的data中的变量是一致的)
<div class='application'>
<h1>This is my widget</h1>
<widget message='click me' />
</div>
var ractive = new Ractive({
el : '#container',
template : "#template",
//注册组件
components :{
widget : MyWidget
}
});
- 实例化组件构造函数,在函数中指明插入对象(可以使用querySelector或者jQuery选择器),这种方法的好处就是可以再实例中对组件的一些公共方法和参数进行改写,但是没有注册的方法更直接易读
具体步骤
1 首先创建一个组件并通过oninit初始化方法和参数
// 创建一个组件
var MyWidget = Ractive.extend({
template : '<div on-click="activate">{{message}}</div>',
// 初始化方法
oninit : function(){
this.on('activate', function(){
alert("ok")
});
},
// 默认参数
data : {
message : 'No message specified, using the default'
}
});
2 然后将组件加入到父级模板中(注册组件)
//创建一个Ractive实例
var ractive = new Ractive({
el : '#container',
template : "#template",
//注册组件
components :{
widget : MyWidget
}
});
3 在父级模板中使用组件
<div class='application'>
<h1>This is my widget</h1>
<widget message='click me' />
</div>
成功。
也可以单独声明一个组件Mywidget的实例,然后插入到父级模板中(这样就不用再Ractive实例中注册组件了:
<script type="text/ractive" id='template'>
<div class='application'>
<h1>This is my widget</h1>
<widget message='click me' />
</div>
</script>
<script>
// 创建一个组件
var MyWidget = Ractive.extend({
template : '<div on-click="activate">{{message}}</div>',
// 初始化方法
oninit : function(){
this.on('activate', function(){
alert("ok")
});
},
// 默认参数
data : {
message : 'No message specified, using the default'
}
});
//创建一个Ractive实例
var ractive = new Ractive({
el : '#container',
template : "#template",
//注册组件
components :{
widget : MyWidget
}
});
//实例化组件对象
var d1 = new MyWidget({
el : $(".application"),
append : true,//插入模式,而非改写模式
data:{
message : 'click to activate'
}
});
var d2 = new MyWidget({
el : $(".application"),
append : true,
oninit : function(){
this.on('activate', function(){
alert('special click')
})
},
data:{
message : 'special click'
}
})
</script>
组件的数据绑定
可以为组件传入一个模板变量:
<widget message='{{foo}}' />
这样就将组件的变量与父级模板中变量联系起来了。
ar ractive = new Ractive({
el : '#container',
template : "#template",
//注册组件
components :{
widget : MyWidget
},
data : {
foo : 'fool'
}
});
数据作用域
组件会创建一个新的数据作用域,所以参数不会污染父级模板的原始参数
==这里我不是太理解,官方文档的例子我看不明白怎么能不说明组件的作用域和父级作用域的关系,我只能看出组件的作用域是向上找到父级作用域的,看不懂==
<script>
var data = {
name: 'Colors',
colors: ['red', 'blue', 'yellow'],
style: 'tint'
};
var MyWidget = Ractive.extend({});
var ractive = new Ractive({
template : "<widget items='{{colors}}' option1='A' option2='{{style}}' />",
//注册组件
components :{
widget : MyWidget
},
data : data
});
function logData(){
console.log('main', JSON.stringify(ractive.get()));
console.log('widget', JSON.stringify(ractive.findComponent('widget').get()))
}
logData();
//main {"style":"tint","colors":["red","blue","yellow"],"name":"Colors"}
//widget {"option1":"A","option2":"tint","items":["red","blue","yellow"]}
ractive.set('colors.1', 'green');
logData()
//main {"style":"tint","colors":["red","green","yellow"],"name":"Colors"}
//widget {"option1":"A","option2":"tint","items":["red","green","yellow"]}
事件
可以像对普通元素一样为组件创建事件,向上面的例子,组件本身有一个事件
on-click="activate"
所以在父级模板中使用组件时就可以创建一个事件,并且在父级的ractive实例中添加都something事件,这样就会先触发组件自己的activate事件,然后触发父级添加的doSomething事件(即使模板自己本身没有为activate添加事件也可以,那样直接触发doSomething事件)
<widget message='{{foo}}' on-activate='doSomething' />
ractive.on('doSomething', function(){
alert('3333333333')
})
注意不能再为组件在父级模板中添加on-click事件,无效
==我理解为组件的事件是一级一级传递的,无论传递多少层时间,最终都会传导到Ractive自定义的例如on-click事件,并且传递是但线路的,如果传导过程中出现两种原始事件,就会都不触发==
冒泡
组件事件的向上冒泡可以通过该组件的命名空间进行引用,例如一个模板引用的组件Foo触发了一个myFoo事件,那么不同层次的所有Ractive实例都可以触发Foo.myFoo事件。这种机制可以避免在一个多层嵌套的组件结构中事件需要被反复创建。
命名空间的事件的定义方式有两种:
- 显式定义
ractive.on('Foo.myFoo', function() {
// ...
});
- 通配符定义
ractive.on('*.myFoo', function() {
// ...
});
==使用通配符能够定义所有给定名字的任意事件而不考虑其命名空间。==(啥意思?)同一层级模板的同一个组件的不同实例能够通过增加不同的名字来区分,例如:
var MyWidget = Ractive.extend({});
Ractive.components.Foo = MyWidget;
Ractive.components.Bar = MyWidget;
// 另一种方式
var Foo = new MyWidget({});
var Bar = new MyWidget({});
这里面和都是组件MyWidget的实例,所有组件里的事件都会以Foo.作为命名空间来冒泡,同样。
停止冒泡
如果想停止冒泡,只需要在事件的处理函数里面return false即可,或者在原始事件中使用stopPropagation和preventDefault方法也可以。