Polymer中嵌套数组新增元素无法被dom-repeat渲染的问题
嵌套数组批量更新后dom-repeat渲染问题解决办法
问题场景
现有嵌套数组结构如下(以添加Jon的新科目为例):
[ { gender: "male", persons: [ { name: "Jon", subjects: [ { subName: "maths", marks: "45" }, { subName: "phy", marks: "47" }, ], }, ], }, { gender: "female", persons: [ { name: "Amily", subjects: [{ subName: "bio", marks: "43" }], }, ], }, ]
执行以下代码添加科目时,dom-repeat无法渲染新增内容:
addSubject() { /* 不生效的代码 */ let newData = [...this.data]; newData[0].persons[0].subjects.push({ subName: "chem", marks: "50" }); this.data = newData; }
使用this.push("data.0.persons.0.subjects", { subName: "chem", marks: "50" })可以生效,但批量为多个用户添加科目时需要多次调用,希望能在临时数组中累积所有修改后一次性更新。
对应的渲染模板:
<template is="dom-repeat" items="[[data]]"> <div>Gender: [[item.gender]]</div> <template is="dom-repeat" items="[[item.persons]]"> <div>Name: [[item.name]]</div> <template is="dom-repeat" items="[[item.subjects]]"> <div>Subject: [[item.subName]], [[item.marks]]</div> </template> </template> </template>
但在简单数组场景下,相同的复制赋值方法却能正常渲染:
<template is="dom-repeat" items="[[simpleData]]"> <div>Subject: [[item.subName]], [[item.marks]]</div> </template>
static get properties() { return { simpleData: { type: Array, notify: true, value: () => [ { subName: "math", marks: "11" }, { subName: "phy", marks: "22" }, ], }, } addSubjectInSimple() { let newData = [...this.simpleData]; newData.push({ subName: "chem", marks: "50" }); this.simpleData = newData; }
问题原因
[...this.data]属于浅拷贝:仅复制了最外层数组的引用,嵌套的gender对象、persons数组、subjects数组依然指向原数据的引用。Polymer的dom-repeat通过监听数组/对象的引用变化来触发重渲染,虽然外层数组引用更新了,但内层subjects数组的引用未变,因此无法触发对应层级的渲染更新。
而简单数组场景中,直接修改了最外层数组的引用,且push操作后整个数组的引用变化能被dom-repeat感知,所以可以正常渲染。
解决方案
方法1:深度拷贝全量数据
通过深度拷贝生成全新的数据结构,所有嵌套层级的数组/对象都使用新引用,赋值后能触发所有层级的dom-repeat更新。
示例代码:
addSubject() { // 用JSON序列化/反序列化实现深度拷贝(注意:无法处理函数、Symbol、循环引用) let newData = JSON.parse(JSON.stringify(this.data)); // 批量添加科目:比如同时给Jon和Amily新增科目 newData[0].persons[0].subjects.push({ subName: "chem", marks: "50" }); newData[1].persons[0].subjects.push({ subName: "maths", marks: "48" }); this.data = newData; }
如果数据包含JSON无法处理的类型,需自定义深度拷贝函数:
function deepCopy(obj) { if (obj === null || typeof obj !== 'object') return obj; if (obj instanceof Array) return obj.map(item => deepCopy(item)); if (typeof obj === 'object') { const newObj = {}; for (const key in obj) { newObj[key] = deepCopy(obj[key]); } return newObj; } }
方法2:仅更新修改层级的引用
无需全量深度拷贝,只针对修改的层级生成新引用,既保证dom-repeat能感知变化,又提升性能。
示例代码:
addSubject() { let newData = [...this.data]; // 处理Jon的科目:逐层生成新引用 newData[0] = { ...newData[0], persons: [...newData[0].persons] }; newData[0].persons[0] = { ...newData[0].persons[0], // 直接生成包含新科目的新subjects数组 subjects: [...newData[0].persons[0].subjects, { subName: "chem", marks: "50" }] }; // 处理Amily的科目(批量操作) newData[1] = { ...newData[1], persons: [...newData[1].persons] }; newData[1].persons[0] = { ...newData[1].persons[0], subjects: [...newData[1].persons[0].subjects, { subName: "maths", marks: "48" }] }; this.data = newData; }
内容的提问来源于stack exchange,提问作者Abhigyan Kumar
相关产品推荐
相关产品推荐

