You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.08.03 04:15:39