如何防止组件收集不必要的依赖项



我有一个基于某种嵌套状态的组件。例如

<rect width="some.deep.state.width">

每当此路径中的任何对象发生更改(不是新设置的(时,即使没有任何更改,组件也会重新渲染。

例如,以下情况会导致重新应答:

Vue.set(some.deep, 'newProp', something)

可以想象,这是非常不希望的,尤其是当在存储中使用嵌套状态并且某些数组值发生更改时。

在我的情况下,我将多个实体保存在我的存储中,每当添加该实体的新实例时,基于该实体的所有组件都会重新提交,即使没有任何更改。

那么,有没有办法防止收集这种(技术性的(deps(双关语(?我可以通过某种机制阻止Vue收集deps吗?

附言:我知道,这种机制在其他地方也很有用。在我的情况下,这不是想要的

显示该行为的Codesandbox(在控制台中(:https://codesandbox.io/s/vuejs-playground-forked-sx534?file=/components/hello.js

内联示例-触发更新:

const app = new Vue({
el: '#app',
data: function () {
return { some: { nested: { prop: 6 }}}
},
created: function () {
console.log('component created')

setTimeout(function () {

Vue.set(this.some, 'otherProp', 10)

}.bind(this), 2000)
},
updated: function () {
console.log('component updated')
}
})
<script src="https://cdnjs.cloudflare.com/ajax/libs/vue/2.5.17/vue.js"></script>
<div id="app">
Prop: {{ some.nested.prop }}
</div>

我认为这个问题并不能阻止组件收集不必要的依赖项。当您在模板中使用<rect width="some.deep.state.width">时,Vue renderWatcher已经收集了some.deep依赖项。然后执行Vue.set(this.some.deep, 'newProp', something)会通知所有收集到的some.deep观察器,其中包括renderWatcher。

我有一个破解解决方案,如果你确保你不想重新渲染,它会停止renderWatcher的运行。

const app = new Vue({
el: '#app',
data: function () {
return { some: { deep: { prop: 6 }}}
},
created: function () {
console.log('component created')

setTimeout(function () {
console.log('trigger rerender?')
Vue.set(this.some, 'otherProp', 10)
this.preventRerender()
}.bind(this), 2000)
},
render(h) {
console.log('render')
return h('div', this.some.deep.prop)
},
methods: {
// keep calling at the code end
preventRerender() {
this._watcher.active = false
this.$nextTick(() => {
this._watcher.active = true
})
}
}
})

您必须注意,更新后的挂钩仍然被触发。

我最终完成这项工作的方式是观察深度嵌套的属性。观察者似乎没有登记我在问题中解释的变化。因此,我为每个属性添加了一个观察程序,它在组件上设置了一个属性,然后我可以在模板中使用该属性。

这解决了不必要的重新渲染问题,然而,事实证明,多个值同时更改的观察者每个都会触发一个重新渲染。这很奇怪,因为所有观察者都在一个dom周期中执行,因此应该只触发一个重新发布程序。如果有人知道原因,请随意详细说明!

const app = new Vue({
el: '#app',
data: function () {
return {
some: { nested: { prop: 6 }},
theProp: null
}
},
created: function () {
console.log('component created')

this.$watch(() => this.some.nested.prop, (newValue) => this.theProp = newValue, {immediate: true})

setTimeout(() => {

// Does not triggers update
Vue.set(this.some, 'otherProp', 10)

}, 2000)

setTimeout(() => {

// Triggers update
this.some.nested.prop = 10

}, 4000)
},
updated: function () {
console.log('component updated')
}
})
<script src="https://cdnjs.cloudflare.com/ajax/libs/vue/2.5.17/vue.js"></script>
<div id="app">
Prop: {{ theProp }}
</div>

最新更新