定义组件

使用Intact.extend(properties)方法就可以定义一个组件

  • properties 定义组件原型链属性和方法
  1. var Component = Intact.extend({ });

上例中定义的组件,并不能直接使用,因为没有个template的组件不能实例化,它更像一个抽象类, 可供其他组件继承。

以继承父组件的方式定义组件:

  1. var SubComponent = Component.extend({
  2. template: '<div>sub-component</div>'
  3. });

默认数据 defaults

组件有个关键属性defaults,它用来定义组件的默认数据。在组件实例化时,会和传入组件的属性 进行合并Object.assign()赋给组件实例的props属性,作为组件的最终数据。

defaults支持两种取值类型:Object & Function

Object类型

@deprecated

  1. var Component;
  2. Component = Intact.extend({
  3. template: '<div>a = {self.get("a")}</div>',
  4. defaults: {
  5. a: 1
  6. }
  7. });

如果以Object类型定义defaults,在组件继承时,会自动合并。

  1. var SubComponent;
  2. SubComponent = Component.extend({
  3. template: '<div>a = {self.get("a")}<br />b = {self.get("b")}</div>',
  4. defaults: {
  5. b: 2
  6. }
  7. });

可以看到,SubComponent组件并没有定义a属性,但是在模板中却取到了a,这是因为继承Component时, 也继承了它的默认数据。

这种方式在定义组件时非常方便,但如果使用不当,会存在以下问题。

  1. var Component = Intact.extend({
  2. template: '<div>\
  3. <button ev-click={self.add}>+1</button>\
  4. a.a = {self.get("a.a")}\
  5. </div>',
  6. defaults: {
  7. a: {a: 1}
  8. },
  9. add: function() {
  10. this.set('a.a', this.get('a.a') + 1);
  11. }
  12. });
  1. var Component = self.Component;
  2. <div>
  3. <Component />
  4. <Component />
  5. </div>
  1. Intact.extend({
  2. template: template,
  3. _init: function() {
  4. this.Component = Component;
  5. }
  6. });

当我们点击第一个+1按钮增加第一个组件的的a.a的值,然后点击第二个+1按钮,发现第二个组件a.a的值并没有从1开始增加, 而是从一个组件最后的值开始增加,看起来两个组件的数据共用了。这显然不是我们想要的,保持组件数据的独立性,才是我们的目的。

其实这一切的根源是由于Intact合并数据时,使用的Object.assign(),而这只是一个浅拷贝函数,对于深层嵌套的引用类型, 仍然拷贝的是引用。在组件继承时,也存在同样的问题。

所以在你的数据存在引用嵌套时,我们应该使用Function定义defaults,它每次都会返回一份新数据。

Object定义方式,将被废弃,请使用Function的定义方式

Function类型

使用Function定义defaults,应该返回一个Object

  1. var Component = Intact.extend({
  2. template: '<div>\
  3. <button ev-click={self.add}>+1</button>\
  4. a.a = {self.get("a.a")}\
  5. </div>',
  6. defaults: function() {
  7. return {
  8. a: {a: 1}
  9. };
  10. },
  11. add: function() {
  12. this.set('a.a', this.get('a.a') + 1);
  13. }
  14. });
  1. var Component = self.Component;
  2. <div>
  3. <Component />
  4. <Component />
  5. </div>
  1. Intact.extend({
  2. template: template,
  3. _init: function() {
  4. this.Component = Component;
  5. }
  6. });

此时,每个组件的数据是独立的了。

采用Function类型定义defaults,在组件继承时,并不会自动合并数据, 如果有需要你可以显式地调用父类的defaults()方法,获取到父类定义的数据, 然后再手动合并返回。关于如何在子类调父类方面,下面会说明。

数据验证

@since v2.3.0

组件会在初始化和更新时验证属性合法性,我们只需要通过组件的静态属性propTypes定义数据验证方式即可。 当组件验证失败时,会在打印错误信息,但不会终端程序运行。数据的验证只会在开发环境进行。

  1. var Component = Intact.extend({
  2. template: `<div>test</div>`,
  3. });
  4. // 定义组件的数据格式
  5. Component.propTypes = {
  6. boolean: Boolean,
  7. regexp: RegExp,
  8. string: String,
  9. number: Number,
  10. array: Array,
  11. function: Function,
  12. object: Object,
  13. symbol: Symbol,
  14. date: Date,
  15. vnode: Intact.VNode,
  16. stringOrNumber: [String, Number],
  17. requiredAny: {
  18. required: true
  19. },
  20. requiredNumber: {
  21. type: Number,
  22. required: true,
  23. },
  24. requiredNumberOrString: {
  25. type: [Number, String],
  26. required: true,
  27. },
  28. enum: ['left', 'bottom', 'right', 'top'],
  29. enumWithObject: ['left', 'bottom', 'right', 'top', Object],
  30. customValidator: {
  31. validator: function(value) {
  32. return ['default', 'primary', 'danger'].indexOf(value) > -1;
  33. }
  34. },
  35. };

类型验证

支持任意原生构造函数作为类型检测

  1. String
  2. Number
  3. Boolean
  4. Array
  5. Object
  6. Function
  7. Date
  8. RegExp

对于自定义构造函数,使用instanceof进行检测,例如Intact.VNode用于检测是否时虚拟dom对象vnode

  1. Component.propTypes = {
  2. vnode: Intact.VNode
  3. };

枚举类型

通过数组可以指定枚举类型的数据,甚至可以和构造函数搭配使用,它们之间时”或“的关系

  1. Component.propTypes = {
  2. enum: ['left', 'bottom', 'right', 'top'],
  3. enumWithObject: ['left', 'bottom', 'right', 'top', Object],
  4. }

自定义验证函数

对于复杂的验证方法我们可以指定validator函数进行验证,该函数返回布尔值true则验证通过,false 则验证失败,或者返回字符串来作为错误提示信息。

  1. Component.propTypes = {
  2. value: {
  3. validator: function(value) {
  4. if (value > 100 || value < 0) {
  5. return "the value must be between 0 and 100";
  6. }
  7. return true;
  8. }
  9. }
  10. }

自定义验证函数validatorthisundefined

必填属性

添加required: true即可指定该属性为必填项

  1. Component.propTypes = {
  2. value: {
  3. type: Number,
  4. required: true
  5. }
  6. }

ES6定义方式

使用ES6/7 Classstatic语法,可以如下定义

  1. class Component extends Intact {
  2. @Intact.template()
  3. static template = `<div>test</div>`;
  4. static propTypes = {
  5. number: Number,
  6. stringOrNumber: [String, Number]
  7. ...
  8. }
  9. defaults() {
  10. return {
  11. number: 1,
  12. stringOrNumber: undefined
  13. };
  14. }
  15. }

模板 template

另一个组件的重要属性便是template,它用来定义组件的模板。它既可以传入模板字符串, 又可以传入模板函数,如果传入模板字符串,会调用Intact.Vdt.compile()方法将之编译成模板函数。 模板语法请参考模板语法章节。

模板必须返回一个(仅一个)元素

对于模板函数,你也可以像下面一样手动编译生成。

  1. Intact.extend({
  2. template: Intact.Vdt.compile('<div>component</div>')
  3. });

将模板字符串写入组件会使组件看起来混乱,不利于维护。 借助webpack和vdt-loader我们可以将模板拆分成单独的文件,详见webpack实践