vue props 包含数组时无法触发子组件更新

子组件接收一个option字段,option有一个属性为column的数组,数组中又包含一些可能存在dicData数组的对象,当dicData 被更新时,不会触发视图更新,而数据确实已经被更新,当我改变一些其他数据依赖,触发视图更新时,dicData相关的视图也就被更新了,貌似只差一个渲染了,相关代码如下,感谢解答!

尝试过this.$set,是相同的结果,应该不是这个问题,option.column依旧是一个响应对象,option.column 打印如下,可以看到所有属性都有getter,setter函数

clipboard.png

父组件

<template>
  <x-crud
    ref="crud"
    :data="shopList"
    :option="option"
    :table-loading="loading"
    :page="page"
    @current-change="handleCurrentChange"
    @size-change="pageSizeChange"
    @refresh-change="refreshChange"
    @row-save="add"
    @row-update="edit"
    @row-del="del"
    @search-change="search"
  >
  </x-crud>
</template>
      
<script>
const curOption = {
  labelWidth: 100,
  addBtn: false,
  menuWidth: 220, indexLabel: '序号', "menuAlign": "center",
  column: [
    {
      label: "卡片名称", prop: "vcard_name", search: true, width: 200, overHidden: true,
      rules: [{ required: true, message: "请输入卡片名称", trigger: "blur" }]
    },
    {
      label: "卡片类型", prop: "cat_id", width: 100, type: 'select', dicData: [], search: true, overHidden: true,
      rules: [{ required: true, message: "请选择卡片类型", trigger: "change" }]
    },
    {
      label: "消耗方式", prop: "consume_mode", type: 'select', solt: true,
      dicData: [{ value: 0, label: '单次卡' }, { value: 1, label: '时间卡' }],
      rules: [{ required: true, message: "请选择消耗方式", trigger: "change" }]
    },
    {
      label: "有效期(天)", prop: "validity", width: 90, align: "right",
      rules: [{ validator: "", trigger: 'blur' }]
    },
    {
      label: "成本价", prop: "cost_price", align: "right",
      rules: [{ validator: "", trigger: 'blur' }]
    },
    {
      label: "市场价", prop: "market_price", align: "right",
      rules: [{ validator: "", trigger: 'blur' }]
    },
    {
      label: "销售价", prop: "mall_price", align: "right",
      rules: [{ validator: "", trigger: 'blur' }]
    },
    {
      label: "启用", prop: "status", type: 'switch', width: 60, solt: true, valueDefault: 1,
      dicData: [{ label: '', value: 1 }, { label: '', value: 0 }],
    },
    {
      label: "关联品牌", prop: "sup_brand_id", dicData: [], multiple: true, type: 'select', hide: true, span: 24,
      rules: [{ required: true, message: "请选择关联品牌", trigger: "change" }]
    },
  ]
}


data() {
    return {
      shopList: [],
      catList: [],
      brandList: [],
      staticList: [],
      searchValues: [],
      loading: true,
      dialogVisible: false,
      option: Object.assign({}, tabOption, curOption),
      page: {
        total: 40, //总页数
        currentPage: 1, //当前页数
        pageSize: 10, //每页显示多少条
      },
    }
  },

created() {
    Promise.all([getBrandList(), getVcardCatList(), getFranchiserList(), this.getShopListBypage(1)]).then(res => {
      this.brandList = res[0].data.data
      this.option.column.find(item => item.prop == 'cat_id').dicData = setSelectOption(res[1].data.data.cats, 'cat_name', 'cat_id')
      this.option.column.find(item => item.prop == 'sup_brand_id').dicData = setSelectOption(res[2].data.data.list, 'group_name', 'shop_group_id')
      this.option.addBtn = true
      this.loading = false
    })
    }
</script>



       

子组件

<script>
render() {
      const scopedSlots = {
        default: scope => (
          <span>
            <el-button type="primary" size="small" icon="el-icon-edit">
              编辑
            </el-button>
            <el-button type="danger" size="small" icon="el-icon-delete">
              删除
            </el-button>
          </span>
        )
      };
      return (
        <el-table data={this.data} {...{ props: this.option }}>
          {this.option.column.map(item => {
            console.log(item.dicData)
            if (item.dicData instanceof Array && item.dicData.length > 0) {
              return (
                <el-table-column
                  {...{ props: item }}
                  scopedSlots={{
                    default: scope => {
                      if (item.multiple) {
                        let labels = ''
                        if (scope.row[item.prop] instanceof Array && scope.row[item.prop].length > 0) {
                          scope.row[item.prop].forEach((v, i) => {
                            const dic = item.dicData.find(e => e.value == v)
                            const line = (i == scope.row[item.prop].length - 1 ? '' : ',')
                            if (dic) labels += dic.label + line
                          })
                        }
                        return labels
                      } else {
                        const dic = item.dicData.find(e => e.value == scope.row[item.prop])
                        if (dic) return dic.label
                      }
                    }
                  }}
                />
              );
            } else {
              return <el-table-column {...{ props: item }} />;
            }
          })}
          <el-table-column label="操作" scopedSlots={scopedSlots} />
        </el-table>
      );
    }
</script>
阅读 7k
4 个回答

已解决,问题的关键在子组件的render函数判断
if (item.dicData instanceof Array && item.dicData.length > 0){/*...*/}
改为
if (item.dicData instanceof Array){/*...*/}
可能Vue在首次渲染是没有走进这个判断,之后也不会进入了,也有可能是length这个属性本身不是响应属性导致的,水平有限,希望哪位大神来解答一下为什么会这样,我将会采纳你的答案,暂时采纳我自己这个吧

使用this.$set来对数组中dicData对象进行更新就可以了,比如dicData这个对象的某个属性为a,原来的值为1,假如你对其进行更新操作this.$set(dicData,'a',10)

在过滤数组的时候,从代码来看,我认为其实你在使用接口赋值数据的时候就已经不是响应式的了,你应该注意使用vue.js的变异方法,如push(),然后再结合vue.set()方法。

代码书写的比较乱,大体说一下我猜测的原因吧,vue 中存在的数据变更而视图未变更的问题均是由未触发响应式机制引起的,这点不像 angular 或者 react,因此你只需要注意排查你所 set 的属性值,当前是否是一个带有响应式的属性:

  • 声明 vm.data 的时候,尽量将所有用到的属性均以默认值的形式声明(这一点你并没有达到,因为你的 curOption.column 中的好几元素都未声明 dictData)
  • 如果在 vm.data 中提前声明属性,请使用 vm.$set 来设置新的属性
  • 对于数组的变更,本身 vue 已经对 pushsplice 等一系列方法进行了拦截,修改特定某项也推荐使用 vm.$set 来更改
撰写回答
你尚未登录,登录后可以
  • 和开发者交流问题的细节
  • 关注并接收问题和回答的更新提醒
  • 参与内容的编辑和改进,让解决方法与时俱进
推荐问题