vuejs 组件中 props 的深度反应性
Posted
技术标签:
【中文标题】vuejs 组件中 props 的深度反应性【英文标题】:Deep reactivity of props in vuejs components 【发布时间】:2020-11-15 00:51:59 【问题描述】:我正在尝试构建一个简单的页面构建器,它有一个行元素、它的子列元素和我需要调用的最后一个组件。为此,我设计了一个架构,我将数据集定义到根组件并通过道具将数据推送到其子元素。所以假设我有一个根组件:
<template>
<div>
<button @click.prevent="addRowField"></button>
<row-element v-if="elements.length" v-for="(row, index) in elements" :key="'row_index_'+index" :attrs="row.attrs" :child_components="row.child_components" :row_index="index"></row-element>
</div>
</template>
<script>
import RowElement from "../../Components/Builder/RowElement";
export default
name: "edit",
data()
return
elements: [],
,
created()
this.listenToEvents();
,
components:
RowElement,
,
methods:
addRowField()
const row_element =
component: 'row',
attrs: ,
child_components: [
]
this.elements.push(row_element)
,
</script>
在这里你可以看到我有一个按钮,我正在尝试推送元素,并且它的元素正在通过道具传递给它的子元素,所以这个RowElement
组件有以下代码:
<template>
<div>
<column-element v-if="child_components.length" v-for="(column,index) in child_components" :key="'column_index_'+index" :attrs="column.attrs" :child_components="column.child_components" :row_index="row_index" :column_index="index"></column-element>
</div>
<button @click="addColumn"></button>
</template>
<script>
import ColumnElement from "./ColumnElement";
export default
name: "RowElement",
components: ColumnElement,
props:
attrs: Object,
child_components: Array,
row_index: Number
,
methods:
addColumn(type, index)
this.selectColumn= false
let column_element =
component: 'column',
child_components: []
;
let component =
//Some logic here then we are emitting event so that it goes to parent element and there it can push the columns
eventBus.$emit('add-columns', column: column_element, index: index);
</script>
所以现在我必须在根页面上监听事件,所以我有:
eventBus.$on('add-columns', (data) =>
if(typeof this.elements[data.index] !== 'undefined')
this.elements[data.index].child_components.push(data.column)
);
现在我需要再次访问这些数据 ColumnComponent
所以在 columnComponent 文件中我有:
<template>
//some extra div to have extended features
<builder-element
v-if="!loading"
v-for="(item, index) in child_components"
:key="'element_index_'+index" :column_index="column_index"
:element_index="index" class="border bg-white"
:element="item" :row_index="row_index"
>
</builder-element>
</template>
<script>
export default
name: "ColumnElement",
props:
attrs: Object,
child_components: Array,
row_index: Number,
column_index: Number
,
</script>
还有我最后的BuilderElement
<template>
<div v-if="typeof element.component !== 'undefined'" class="h-10 w-10 mt-1 mb-2 mr-3 cursor-pointer font-bold text-white rounded-lg">
<div>element.component</div>
<img class="h-10 w-10 mr-3" :src="getDetails(item.component, 'icon')">
</div>
<div v-if="typeof element.component !== 'undefined'" class="flex-col text-left">
<h5 class="text-blue-500 font-bold">getDetails(item.component, 'title')</h5>
<p class="text-xs text-gray-600 mt-1">getDetails(item.component, 'desc')</p>
</div>
</template>
<script>
export default
name: "BuilderElement",
data()
return
components:[
id: 1, title:'Row', icon:'/project-assets/images/row.png', desc:'Place content elements inside the row', component_name: 'row',
//list of rest all the components available
]
,
props:
element: Object,
row_index: Number,
column_index: Number,
element_index: Number,
,
methods:
addElement(item,index)
//Some logic to find out details
let component_element =
component: item.component_name,
attrs: ,
child_components: [
]
eventBus.$emit('add-component', component: component_element, row_index: this.row_index, column_index: this.column_index, element_index: this.element_index);
,
getDetails(component, data)
let index = _.findIndex(this.components, (a) =>
return a.component_name === component;
)
console.log('Component'+ component);
console.log('Index '+index);
if(index > -1)
let component_details = this.components[index];
return component_details[data];
else
return null;
,
,
</script>
如您所见,我再次发出名为 add-component
的事件,该事件再次在根组件中进行侦听,因此在以下侦听器中进行了侦听:
eventBus.$on('add-component', (data) =>
this.elements[data.row_index].child_components[data.column_index].child_components[data.element_index] = data.component
);
它显示了我的vue-devtools
中的数据集,但它没有出现在构建器元素中:
图片 FYR:
这是我的根组件:
这是我的行组件:
这是我的列组件:
这是我的构建器元素:
我不知道为什么这些数据没有传递给它的子组件,我的意思是最后一个组件对道具没有反应,任何更好的想法都非常感谢。
谢谢
【问题讨论】:
复制问题需要一段时间,但您是否尝试过使用this.$set
设置动态属性?
@pai.not.pi 你有什么例子吗?在小提琴/jsbin 中?
【参考方案1】:
问题在于您在 addComponent
方法中设置数据的方式。
当您通过直接修改数组的索引来更改数组时,Vue 无法获取更改,例如,
arr[0] = 10
正如他们在change detection guide 中定义的数组突变,
Vue 包装了观察到的数组的变异方法,因此它们也将 触发视图更新。包装的方法是:
推() pop() shift() unshift() 拼接() 排序() 反向()
所以你可以改变。
this.elements[data.row_index].child_components[data.column_index].child_components[data.element_index] = data.component
到,
this.elements[data.row_index].child_components[data.column_index].child_components.splice(data.element_index, 1, data.component);
【讨论】:
以上是关于vuejs 组件中 props 的深度反应性的主要内容,如果未能解决你的问题,请参考以下文章
Vue子组件(deep)深度监听props对象属性无效的解决办法