vuejs 组件中道具的深度反应性
Deep reactivity of props in vuejs components
我正在尝试构建一个简单的页面构建器,它有一个行元素、它的子列元素和最后一个我需要调用的组件。为此,我设计了架构,我将数据集定义到根组件,并通过 props 将数据推送到其子元素。假设我有一个根组件:
<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:
这是我的根组件:
这是我的 RowComponent:
这是我的 ColumnComponent:
这是我的构建器元素:
我不知道为什么这个数据没有传递给它的子组件,我的意思是最后一个组件对 props 没有反应,任何更好的想法都非常感谢。
谢谢
问题在于您在 addComponent
方法中设置数据的方式。
当您通过直接修改数组的索引来更改数组时,Vue 无法获取更改,例如,
arr[0] = 10
正如他们在 change detection guide 中对数组突变的定义,
Vue wraps an observed array’s mutation methods so they will also
trigger view updates. The wrapped methods are:
- push()
- pop()
- shift()
- unshift()
- splice()
- sort()
- reverse()
所以你可以改变。
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);
我正在尝试构建一个简单的页面构建器,它有一个行元素、它的子列元素和最后一个我需要调用的组件。为此,我设计了架构,我将数据集定义到根组件,并通过 props 将数据推送到其子元素。假设我有一个根组件:
<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:
这是我的根组件:
这是我的 RowComponent:
这是我的 ColumnComponent:
这是我的构建器元素:
我不知道为什么这个数据没有传递给它的子组件,我的意思是最后一个组件对 props 没有反应,任何更好的想法都非常感谢。
谢谢
问题在于您在 addComponent
方法中设置数据的方式。
当您通过直接修改数组的索引来更改数组时,Vue 无法获取更改,例如,
arr[0] = 10
正如他们在 change detection guide 中对数组突变的定义,
Vue wraps an observed array’s mutation methods so they will also trigger view updates. The wrapped methods are:
- push()
- pop()
- shift()
- unshift()
- splice()
- sort()
- reverse()
所以你可以改变。
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);