LOGO

组件API

编辑本文

该文档描述了组件的 API,在 San 主模块上暴露的 API 请参考文档 主模块API。

初始化参数

data

解释:

组件初始化数据。通常在组件反解的场景下使用。

类型: Object

用法:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
var MyComponent = san.defineComponent({});

var myComponent = new MyComponent({
el: document.getElementById('my-label'),
data: {
email: 'errorrik@gmail.com',
name: 'errorrik'
}
});

/* html:
<label id="my-label">
<span title="errorrik@gmail.com" prop-title="{{email}}">errorrik</span>
</label>

el

解释:

组件根元素。传入此参数意味着不使用组件的 template 作为视图模板,组件视图由 San 自动反解。详情可参考组件反解文档。

类型: HTMLElement

用法:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
var MyComponent = san.defineComponent({});

var myComponent = new MyComponent({
el: document.getElementById('my-label'),
data: {
email: 'errorrik@gmail.com',
name: 'errorrik'
}
});

/* html:
<label id="my-label">
<span title="errorrik@gmail.com" prop-title="{{email}}">errorrik</span>
</label>
*/

owner

版本:>= 3.7.0

类型: Object

解释:

指定组件所属的 owner 组件。指定 owner 组件后:

  • 组件无需手工 dispose,owner dispose 时会自动释放
  • 组件及其子组件 dispatch 的消息,owner 组件可以接收

更多说明请参考owner与parent文档。

注意:

指定 owner 后,不允许将组件 push 到 owner 的 children 中

用法:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
san.defineComponent({
mainClick: function () {
if (!this.layer) {
// 为动态创建的子组件指定 owner
this.layer = new Layer({
owner: this
});

this.layer.attach(document.body);
}

this.layer.show();
}
});

source

版本:>= 3.7.0

类型: string|Object

解释:

通过 HTML 格式的一个标签,声明组件与 owner 之间的数据绑定和事件。指定 source 同时需要指定 owner。更详细的用法请参考 手动创建子组件 文档,更多声明格式细节请参考 模板 与 事件 文档。

提醒:

source 串的标签名称通常没什么用,除了以下情况:组件本身根节点为 template 时,以 source 的标签名称为准。

用法:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
san.defineComponent({
mainClick: function () {
if (!this.calendar) {
this.calendar = new Calendar({
owner: this,
source: '<x-cal value="{{birthday}}" on-change="birthdayChange($event)"/>'
});

this.calendar.attach(document.body);
}
},

birthdayChange: function (value) {
this.data.set('birthday', value);
}
});

transition

版本:>= 3.6.0

类型: Object

解释:

组件的过渡动画控制器。可参考 动画控制器 和 动画控制器 Creator 文档。

用法:

1
2
3
4
5
6
7
8
9
10
var MyComponent = san.defineComponent({
template: '<span>transition</span>'
});

var myComponent = new MyComponent({
transition: {
enter: function (el, done) { /* 进入时的过渡动画 */ },
leave: function (el, done) { /* 离开时的过渡动画 */ },
}
});

生命周期钩子

生命周期代表组件的生存过程,在每个过程到达时将触发钩子函数。具体请参考生命周期文档。

compiled

解释:

组件视图模板编译完成。组件上的 compiled 方法将会被调用。

inited

解释:

组件实例初始化完成。组件上的 inited 方法将会被调用。

created

解释:

组件元素创建完成。组件上的 created 方法将会被调用。

attached

解释:

组件已被附加到页面中。组件上的 attached 方法将会被调用。

detached

解释:

组件从页面中移除。组件上的 detached 方法将会被调用。

disposed

解释:

组件卸载完成。组件上的 disposed 方法将会被调用。

updated

解释:

组件由于数据变化,视图完成一次刷新。组件上的 updated 方法将会被调用。

error

版本:>= 3.10.7

解释:

当捕获一个来自子孙组件的抛出异常时被调用。此钩子会收到三个参数:错误对象、发生错误的组件实例以及一个包含异常来源信息的字符串。此钩子捕获异常以后,不会再继续向父节点传递。

定义组件成员

template

解释:

组件的视图模板。详细描述请参考视图模板文档。

类型: string

用法:

1
2
3
san.defineComponent({
template: '<span title="{{text}}">{{text}}</span>'
});

filters

解释:

声明组件视图模板中可以使用哪些过滤器。详细描述请参考过滤器文档。

类型: Object

用法:

1
2
3
4
5
6
7
8
9
san.defineComponent({
template: '<a>{{createTime | dateFormat("yyyy-MM-dd")}}</a>',

filters: {
dateFormat: function (value, format) {
return moment(value).format(format);
}
}
});

警告:

filter 方法在运行时通过 this.data 可以触及组件的数据。但是,这么干会造成对数据的隐式依赖,导致数据变化时,视图不会随着更新。所以,filter 方法应该是无副作用的 pure function。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
var Bad = san.defineComponent({
template: '<u>{{num | enhance}}</u>',

filters: {
enhance: function (n) {
return n * this.data.get('times');
}
},

initData: function () {
return {
num: 2,
times: 3
};
}
});

var Good = san.defineComponent({
template: '<u>{{num | enhance(times)}}</u>',

filters: {
enhance: function (n, times) {
return n * times;
}
},

initData: function () {
return {
num: 2,
times: 3
};
}
});

components

解释:

声明组件中可以使用哪些类型的子组件。详细描述请参考components文档。

类型: Object

用法:

1
2
3
4
5
6
var AddForm = san.defineComponent({
components: {
'ui-timepicker': TimePicker,
'ui-calendar': Calendar
}
});

computed

解释:

声明组件中的计算数据。详细描述请参考计算数据文档。

类型: Object

用法:

1
2
3
4
5
6
7
8
9
10
san.defineComponent({
template: '<a>{{name}}</a>',

// name 数据项由 firstName 和 lastName 计算得来
computed: {
name: function () {
return this.data.get('firstName') + ' ' + this.data.get('lastName');
}
}
});

messages

解释:

声明处理子组件派发消息的方法。详细描述请参考消息文档。

类型: Object

用法:

1
2
3
4
5
6
7
8
9
10
11
var Select = san.defineComponent({
template: '<ul><slot></slot></ul>',

messages: {
'UI:select-item-selected': function (arg) {
// arg.target 可以拿到派发消息的组件
var value = arg.value;
this.data.set('value', value);
}
}
});

initData

解释:

返回组件实例的初始数据。详细描述请参考初始数据文档。

类型: function():Object

用法:

1
2
3
4
5
6
7
8
9
var MyApp = san.defineComponent({
template: '<ul><li s-for="item in list">{{item}}</li></ul>',

initData: function () {
return {
list: ['san', 'er', 'esui', 'etpl', 'esl']
};
}
});

trimWhitespace

定义组件模板解析时对空白字符的 trim 模式。

  • 默认为 none,不做任何事情
  • blank 时将清除空白文本节点
  • all 时将清除所有文本节点的前后空白字符

版本:>= 3.2.5

类型: string

用法:

1
2
3
4
5
6
var MyApp = san.defineComponent({
trimWhitespace: 'blank'

// ,
// ......
});

delimiters

解释:

定义组件模板解析时插值的分隔符。值为2个项的数组,分别为起始分隔符和结束分隔符。默认为:

1
['{{', '}}']

版本:>= 3.5.0

类型: Array

用法:

1
2
3
4
var MyComponent = san.defineComponent({
delimiters: ['{%', '%}'],
template: '<a><span title="Good {%name%}">Hello {%name%}</span></a>'
});

transition

解释:

定义组件根节点的过渡动画控制器。已废弃。

版本:>= 3.3.0, < 3.6.0

类型: Object

用法:

1
2
3
4
5
6
7
var MyComponent = san.defineComponent({
template: '<span>transition</span>',
transition: {
enter: function (el) { /* 根节点进入时的过渡动画 */ },
leave: function (el, done) { /* 根节点离开时的过渡动画 */ },
}
});

inheritAttrs

解释:

属性透传功能是否生效。默认值为 true。当属性透传功能关闭时,id / class / style 的透传也将失效。

版本:>= 3.14.1

类型: boolean

用法:

1
2
3
4
var MyComponent = san.defineComponent({
inheritAttrs: false,
template: '<span><slot/></span>'
});

autoFillStyleAndId

解释:

组件根节点是否应用外部通过绑定传入的 id / class / style。默认为 true。

版本:>= 3.7.5

类型: boolean

用法:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
var AComponent = san.defineComponent({
template: '<a class="inner">my component</a>',
autoFillStyleAndId: false
});

var BComponent = san.defineComponent({
template: '<b class="inner">my component</b>'
});

var MyApp = san.defineComponent({
component: {
'a-cmpt': AComponent,
'b-cmpt': BComponent
},

// element a class: "inner"
// element b class: "inner outer"
template: '<div><a-cmpt class="outer"/><b-cmpt class="outer"/></div>'
});

updateMode

解释:

可控制视图刷新的逻辑,当前仅支持 optimized。当取值为 optimized 时,对 s-for 指令的视图更新会根据不同浏览器环境进行优化,更新过程对 DOM 的操作和数据变化可能无法对应。

版本:>= 3.7.4

类型: string

用法:

1
2
3
4
var MyComponent = san.defineComponent({
updateMode: 'optimized',
template: '<ul><li s-for="item in list">{{item}}</li></ul>'
});

aNode

解释:

template 编译结果。包含该属性时,组件实例化时不会执行编译行为,可节省初始时间。通常用于组件预编译,开发时不直接编写,通过工具编译生成。ANode 处理可使用 san-anode-utils。

类型: Object

用法:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
var MyComponent = san.defineComponent({
// equal to "template: '<p>Hello {{name}}</p>'"
aNode: {
"directives": {},
"props": [
{
"name": "class",
"expr": {
"type": 5,
"expr": {
"type": 4,
"paths": [
{
"type": 1,
"value": "class"
}
]
},
"filters": [
{
"type": 6,
"args": [],
"name": {
"type": 4,
"paths": [
{
"type": 1,
"value": "_class"
}
]
}
}
]
}
},
{
"name": "style",
"expr": {
"type": 5,
"expr": {
"type": 4,
"paths": [
{
"type": 1,
"value": "style"
}
]
},
"filters": [
{
"type": 6,
"args": [],
"name": {
"type": 4,
"paths": [
{
"type": 1,
"value": "_style"
}
]
}
}
]
}
},
{
"name": "id",
"expr": {
"type": 4,
"paths": [
{
"type": 1,
"value": "id"
}
]
}
}
],
"events": [],
"children": [
{
"textExpr": {
"type": 7,
"segs": [
{
"type": 1,
"value": "Hello "
},
{
"type": 5,
"expr": {
"type": 4,
"paths": [
{
"type": 1,
"value": "name"
}
]
},
"filters": []
}
]
}
}
],
"tagName": "p"
}
});

aPack

解释:

aNode 的压缩结果。aPack 相比 aNode 在体积和传输上有较大优势,在解压性能上也比 template 解析要快很多。通常用于组件预编译,开发时不直接编写,通过工具编译生成。APack 处理可使用 san-anode-utils。

版本:>= 3.9.0

类型: Array

用法:

1
2
3
4
var MyComponent = san.defineComponent({
// equal to "template: '<p>Hello {{name}}</p>'"
aPack: [1,"p",4,2,"class",7,,6,1,3,"class",1,8,6,1,3,"_class",,2,"style",7,,6,1,3,"style",1,8,6,1,3,"_style",,2,"id",6,1,3,"id",,9,,2,3,"Hello ",7,,6,1,3,"name",]
});

组件方法

fire

描述: fire({string}eventName, {*}eventArgument)

解释:

派发一个自定义事件。San 为组件提供了自定义事件功能,组件开发者可以通过该方法派发事件。事件可以在视图模板中通过 on- 的方式绑定监听,也可以通过组件实例的 on 方法监听。可参考Event文档。

用法:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
var Label = san.defineComponent({
template: '<template class="ui-label"><a on-click="clicker" title="{{text}}">{{text}}</a></template>',

clicker: function () {
this.fire('customclick', this.data.get('text') + ' clicked');
}
});

var MyComponent = san.defineComponent({
initData: function () {
return {name: 'San'};
},

components: {
'ui-label': Label
},

template: '<div><ui-label text="{{name}}" on-customclick="labelClicker($event)"></ui-label></div>',

labelClicker: function (doneMsg) {
alert(doneMsg);
}
});

on

描述: on({string}eventName, {Function}eventListener)

解释:

添加自定义事件监听器。 on 一般仅用在使用 JavaScript 动态创建的组件中,通过视图模板创建的子组件应通过 on- 的方式绑定监听。可参考动态子组件文档

un

描述: un({string}eventName, {Function=}eventListener)

解释:

移除事件监听器。 当 eventListener 参数为空时,移除所有 eventName 事件的监听器。

dispatch

描述: dispatch({string}name, {*}value)

解释:

派发一个消息。消息将沿着组件树向上传递,直到遇到第一个处理该消息的组件。上层组件通过 messages 声明组件要处理的消息。消息主要用于组件与非 owner 的上层组件进行通信。可参考消息文档。

用法:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
var SelectItem = san.defineComponent({
template:
'<li on-click="select" class="{{value === selectValue ? \'selected\' : \'\'">'
+ '<slot></slot>'
+ '</li>',

// 子组件在各种时机派发消息
select: function () {
var value = this.data.get('value');
this.dispatch('UI:select-item-selected', value);
},

attached: function () {
this.dispatch('UI:select-item-attached');
},

detached: function () {
this.dispatch('UI:select-item-detached');
}
});

var Select = san.defineComponent({
template: '<ul><slot></slot></ul>',

// 上层组件处理自己想要的消息
messages: {
'UI:select-item-selected': function (arg) {
var value = arg.value;
this.data.set('value', value);

// 原则上上层组件允许更改下层组件的数据,因为更新流是至上而下的
var len = this.items.length;
while (len--) {
this.items[len].data.set('selectValue', value);
}
},

'UI:select-item-attached': function (arg) {
this.items.push(arg.target);
arg.target.data.set('selectValue', this.data.get('value'));
},

'UI:select-item-detached': function (arg) {
var len = this.items.length;
while (len--) {
if (this.items[len] === arg.target) {
this.items.splice(len, 1);
}
}
}
},

inited: function () {
this.items = [];
}
});

var MyComponent = san.defineComponent({
components: {
'ui-select': Select,
'ui-selectitem': SelectItem
},

template: ''
+ '<div>'
+ ' <ui-select value="{=value=}">'
+ ' <ui-selectitem value="1">one</ui-selectitem>'
+ ' <ui-selectitem value="2">two</ui-selectitem>'
+ ' <ui-selectitem value="3">three</ui-selectitem>'
+ ' </ui-select>'
+ '</div>'
});

watch

描述: watch({string}dataName, {function({*}value)}listener)

解释:

监听组件的数据变化。通常我们使用绑定,在子组件数据变化时自动更新父组件的对应数据。 watch 一般仅用在使用 JavaScript 动态创建的组件中。可参考动态子组件文档

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
san.defineComponent({
// ...

initLayer: function () {
if (!this.monthView) {
var monthView = new MonthView();
this.monthView = monthView;

this.monthView.watch('value', (function (value) {
this.data.set('value', value);
}).bind(this));

this.watch('value', function (value) {
monthView.data.set('value', value);
});

this.monthView.attach(document.body);
}
}
});

ref

描述: ref({string}name)

解释:

获取定义了 s-ref 的子组件或 HTMLElement。详细请参考组件层级文档。

注意:组件根元素即使定义了 s-ref,也无法通过 ref 方法获得。获取组件根元素请使用 this.el。

用法:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
var AddForm = san.defineComponent({
// template

components: {
'ui-timepicker': require('../ui/TimePicker'),
'ui-calendar': require('../ui/Calendar')
},

submit: function () {
this.ref('endDate');
this.ref('endHour');

this.ref('rootNode'); // undefined
this.el; //根元素 <div class="form"> ... </div>
}
});

/* template:
<div class="form" s-ref="rootNode">
<div>预期完成时间:
<ui-calendar bindx-value="endTimeDate" s-ref="endDate"></ui-calendar>
<ui-timepicker bindx-value="endTimeHour" s-ref="endHour"></ui-timepicker>
</div>

<div class="form-op">
<button type="button" on-click="submit">ok</button>
</div>
</div>
*/

slot

版本:>= 3.3.0

描述: {Array} slot({string=}name)

解释:

获取组件插槽的节点信息。返回值是一个数组,数组中的项是节点对象。通常只有一项,当 slot 声明中应用了 if 或 for 时可能为 0 项或多项。节点对象包含 isScoped 、 isInserted 和 children。

插槽详细用法请参考 slot 文档。

注意:不要对返回的 slot 对象进行任何修改。如果希望操作视图变更,请操作数据。

用法:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
var Panel = san.defineComponent({
template: '<div><slot s-if="!hidden"/></div>',
});

var MyComponent = san.defineComponent({
components: {
'x-panel': Panel
},

template: ''
+ '<div>'
+ '<x-panel hidden="{{folderHidden}}" s-ref="panel"><p>{{desc}}</p></x-panel>'
+ '</div>',

attached: function () {
// 1
this.ref('panel').slot().length

var contentSlot = this.ref('panel').slot()[0];

// truthy
contentSlot.isInserted

// falsy
contentSlot.isScoped
}
});


var myComponent = new MyComponent({
data: {
desc: 'MVVM component framework',
}
});

nextTick

解释:

San 的视图更新是异步的。组件数据变更后,将在下一个时钟周期更新视图。如果你修改了某些数据,想要在 DOM 更新后做某些事情,则需要使用 nextTick 方法。

用法:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
const Component = san.defineComponent({
template: `
<div>
<div s-ref="name">{{name}}</div>
<button on-click="clicker">change name</button>
</div>
`,

initData() {
return {name: 'erik'};
},

clicker() {
this.data.set('name', 'leeight');
console.log(this.ref('name').innerHTML); // erik
this.nextTick(() => {
console.log(this.ref('name').innerHTML); // leeight
});
}
});