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) { /* 根节点离开时的过渡动画 */ },
}
});

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
});
}
});