定义组件
使用Intact.extend(properties)
方法就可以定义一个组件
properties
定义组件原型链属性和方法
var Component = Intact.extend({ });
上例中定义的组件,并不能直接使用,因为没有个template
的组件不能实例化,它更像一个抽象类,
可供其他组件继承。
以继承父组件的方式定义组件:
var SubComponent = Component.extend({
template: '<div>sub-component</div>'
});
默认数据 defaults
组件有个关键属性defaults
,它用来定义组件的默认数据。在组件实例化时,会和传入组件的属性
进行合并Object.assign()
赋给组件实例的props
属性,作为组件的最终数据。
defaults
支持两种取值类型:Object & Function
。
Object
类型
@deprecated
var Component;
Component = Intact.extend({
template: '<div>a = {self.get("a")}</div>',
defaults: {
a: 1
}
});
如果以Object
类型定义defaults,在组件继承时,会自动合并。
var SubComponent;
SubComponent = Component.extend({
template: '<div>a = {self.get("a")}<br />b = {self.get("b")}</div>',
defaults: {
b: 2
}
});
可以看到,SubComponent
组件并没有定义a
属性,但是在模板中却取到了a
,这是因为继承Component
时,
也继承了它的默认数据。
这种方式在定义组件时非常方便,但如果使用不当,会存在以下问题。
var Component = Intact.extend({
template: '<div>\
<button ev-click={self.add}>+1</button>\
a.a = {self.get("a.a")}\
</div>',
defaults: {
a: {a: 1}
},
add: function() {
this.set('a.a', this.get('a.a') + 1);
}
});
var Component = self.Component;
<div>
<Component />
<Component />
</div>
Intact.extend({
template: template,
_init: function() {
this.Component = Component;
}
});
当我们点击第一个+1按钮增加第一个组件的的a.a
的值,然后点击第二个+1按钮,发现第二个组件a.a
的值并没有从1开始增加,
而是从一个组件最后的值开始增加,看起来两个组件的数据共用了。这显然不是我们想要的,保持组件数据的独立性,才是我们的目的。
其实这一切的根源是由于Intact合并数据时,使用的Object.assign()
,而这只是一个浅拷贝函数,对于深层嵌套的引用类型,
仍然拷贝的是引用。在组件继承时,也存在同样的问题。
所以在你的数据存在引用嵌套时,我们应该使用Function
定义defaults
,它每次都会返回一份新数据。
Object
定义方式,将被废弃,请使用Function
的定义方式
Function
类型
使用Function
定义defaults
,应该返回一个Object
。
var Component = Intact.extend({
template: '<div>\
<button ev-click={self.add}>+1</button>\
a.a = {self.get("a.a")}\
</div>',
defaults: function() {
return {
a: {a: 1}
};
},
add: function() {
this.set('a.a', this.get('a.a') + 1);
}
});
var Component = self.Component;
<div>
<Component />
<Component />
</div>
Intact.extend({
template: template,
_init: function() {
this.Component = Component;
}
});
此时,每个组件的数据是独立的了。
采用
Function
类型定义defaults
,在组件继承时,并不会自动合并数据, 如果有需要你可以显式地调用父类的defaults()
方法,获取到父类定义的数据, 然后再手动合并返回。关于如何在子类调父类方面,下面会说明。
数据验证
@since v2.3.0
组件会在初始化和更新时验证属性合法性,我们只需要通过组件的静态属性propTypes
定义数据验证方式即可。
当组件验证失败时,会在打印错误信息,但不会终端程序运行。数据的验证只会在开发环境进行。
var Component = Intact.extend({
template: `<div>test</div>`,
});
// 定义组件的数据格式
Component.propTypes = {
boolean: Boolean,
regexp: RegExp,
string: String,
number: Number,
array: Array,
function: Function,
object: Object,
symbol: Symbol,
date: Date,
vnode: Intact.VNode,
stringOrNumber: [String, Number],
requiredAny: {
required: true
},
requiredNumber: {
type: Number,
required: true,
},
requiredNumberOrString: {
type: [Number, String],
required: true,
},
enum: ['left', 'bottom', 'right', 'top'],
enumWithObject: ['left', 'bottom', 'right', 'top', Object],
customValidator: {
validator: function(value) {
return ['default', 'primary', 'danger'].indexOf(value) > -1;
}
},
};
类型验证
支持任意原生构造函数作为类型检测
- String
- Number
- Boolean
- Array
- Object
- Function
- Date
- RegExp
对于自定义构造函数,使用instanceof
进行检测,例如Intact.VNode
用于检测是否时虚拟dom对象vnode
Component.propTypes = {
vnode: Intact.VNode
};
枚举类型
通过数组可以指定枚举类型的数据,甚至可以和构造函数搭配使用,它们之间时”或“的关系
Component.propTypes = {
enum: ['left', 'bottom', 'right', 'top'],
enumWithObject: ['left', 'bottom', 'right', 'top', Object],
}
自定义验证函数
对于复杂的验证方法我们可以指定validator
函数进行验证,该函数返回布尔值true
则验证通过,false
则验证失败,或者返回字符串来作为错误提示信息。
Component.propTypes = {
value: {
validator: function(value) {
if (value > 100 || value < 0) {
return "the value must be between 0 and 100";
}
return true;
}
}
}
自定义验证函数
validator
的this
为undefined
必填属性
添加required: true
即可指定该属性为必填项
Component.propTypes = {
value: {
type: Number,
required: true
}
}
ES6定义方式
使用ES6/7
Class
的static
语法,可以如下定义
class Component extends Intact {
@Intact.template()
static template = `<div>test</div>`;
static propTypes = {
number: Number,
stringOrNumber: [String, Number]
...
}
defaults() {
return {
number: 1,
stringOrNumber: undefined
};
}
}
模板 template
另一个组件的重要属性便是template
,它用来定义组件的模板。它既可以传入模板字符串,
又可以传入模板函数,如果传入模板字符串,会调用Intact.Vdt.compile()
方法将之编译成模板函数。
模板语法请参考模板语法章节。
模板必须返回一个(仅一个)元素
对于模板函数,你也可以像下面一样手动编译生成。
Intact.extend({
template: Intact.Vdt.compile('<div>component</div>')
});
将模板字符串写入组件会使组件看起来混乱,不利于维护。 借助webpack和
vdt-loader
我们可以将模板拆分成单独的文件,详见webpack实践