本文介绍Polymer框架中dom-repeat指令的使用,重点解析如何通过items属性遍历数组,并深入探讨Polymer数据更新机制。针对数组动态增删场景,说明原生push无效的原因,并推荐使用Polymer提供的this.push等访问路径操作方法来实现视图同步更新。
Polymer中dom-repeat指令基础
主流前端框架通常都具备将列表数据绑定至DOM的功能,例如Angular使用ng-repeat指令实现该功能。在Polymer中,类似的功能被归类为扩展指令级别。Angular的ng-repeat本质上是一个Directive,而Polymer中的dom-repeat也属于同一层级。在Polymer的设计哲学中,一切皆视为Directive。例如,dom-module用于定义模块,它本身也是一个Directive;而dom-repeat同样如此,但它并非独立的HTML标签,而是基于template标签实现的Directive。其基本使用方式如下:
<script> var Polymer = { dom: 'shadow' }; </script>
<base href="http://www.web-tinker.com/share/" />
<link rel="import" href="polymer/polymer.html" />
<dom-module id="demo-test">
<template>
<ul>
<template is="dom-repeat" items="[[data]]">
<li>
第 <strong>[[index]]</strong> 项,
值为 <strong>[[item]]</strong>
</li>
</template>
</ul>
</template>
<script>
Polymer({
is: 'demo-test',
properties: {
data: {
type: Array,
value: [ 'a', 'b', 'c', 'd' ]
}
}
});
</script>
</dom-module>
<demo-test></demo-test>
dom-repeat数据遍历与更新机制
上述代码通过将template元素的is属性设置为dom-repeat,使得该template内部的内容能够进行循环渲染。循环遍历的依据是template元素上提供的items属性。需要注意的是,items必须是一个数组。尽管这种限制可能在某些场景下略显不便,但它是一种良好的设计实践,有效避免了如Angular中使用for-in遍历数组时可能引发的各类问题。在循环过程中,每一项的索引和值会被分别封装到index和item属性中,供template内部的模板使用,从而输出对应的索引和数值。然而,Polymer的数据更新机制并非基于脏数据检测(dirty checking),因此数据的动态更新可能相对复杂。例如,若有一个按钮,每次点击需要向列表中添加一项,正确的写法应如下所示:
<script> var Polymer = { dom: 'shadow' }; </script>
<base href="http://www.web-tinker.com/share/" />
<link rel="import" href="polymer/polymer.html" />
<dom-module id="demo-test">
<template>
<input placeholder="请输入内容" value="{{value::input}}" />
<button on-click="append">添加一项</button>
<ul>
<template is="dom-repeat" items="[[data]]">
<li>
第 <strong>[[index]]</strong> 项,
值为 <strong>[[item]]</strong>
</li>
</template>
</ul>
</template>
<script>
Polymer({
is: 'demo-test',
properties: {
data: {
type: Array,
value: [ 'a', 'b', 'c', 'd' ]
}
},
append: function() {
// data.push(this.value); // 这么写是不行的
this.push('data', this.value)
}
});
</script>
</dom-module>
<demo-test></demo-test>
前文曾提及,Polymer会将需要监控变化的数据设置为访问器属性。但如果直接修改数组元素,实际上并未对数组对象本身产生任何影响。当将数组赋值给ViewModel(VM)时,Polymer执行的是元素复制操作,而非传递数组对象的引用。这意味着该数组对象并非VM的直接引用,直接对该数组对象进行操作(如调用原生push方法)无法影响VM的状态,因此直接对数组执行push操作是无效的。尽管数组自身的push方法无法操作VM,但Polymer提供了直接操作VM的方法,例如示例中的this.push。该方法的操作对象不是普通对象,而是VM上的一个访问路径(如示例中push的第一个参数'data'即指向VM中的data访问路径)。除了push之外,Polymer还提供了pop、shift等类似原生数组方法的操作(需注意它们并非DOM元素的方法)。虽然这种操作方式略显繁琐,但尚在可接受范围内。