首页 > 解决方案 > 如果 prop 在模板中直接分配给对象,则 Vue prop watcher 会意外触发

问题描述

这是代码:

<template>
  <div>
    <foo :setting="{ value: 'hello' }" />
    <button @click="() => (clicked++)">{{ clicked }}</button>
  </div>
</template>

<script>
import Vue from 'vue'

// dummy component with dummy prop
Vue.component('foo', {
  props: {
    setting: {
      type: Object,
      default: () => ({}),
    },
  },
  watch: {
    setting() {
      console.log('watch:setting')
    },
  },
  render(createElement) {
    return createElement(
      'div',
      ['Nothing']
    )
  },
})

export default {
  data() {
    return {
      clicked: 0,
    }
  },
}
</script>

我还做了一个codepen:https ://codepen.io/clinicion-lin/pen/LYNJwJP

问题是:每次单击按钮时,settingfoo 组件中的 prop 观察器都会触发,我猜原因是:settings在重新渲染期间重新评估了其中的内容,但我不确定。

这种行为本身不会造成任何问题,但如果没有给予足够的关注,它可能会导致不需要的更新甚至错误(实际上我只是做了一个,这就是我来问的原因:D)。使用 可以轻松解决该问题:setting="settingValue",但我想知道是否有替代解决方案或最佳实践。我看到有些代码也是直接在模板中赋值对象,感觉自然方便。

感谢任何可以提供解释或提示的人。

标签: vue.js

解决方案


首先,文档:“每次父组件更新时,子组件中的所有道具都将刷新为最新值”

通过v-bind:propA="XX"在模板中使用,你是在告诉 Vue “XX 是 JavaScript 表达式,我想用它作为 propA 的值” 所以如果你使用{ value: 'hello' }表达式,字面意思是“创建新对象”表达式,真的是令人惊讶的新对象吗?每次重新渲染父级时创建(并在您的情况下执行观察者)?

为了更好地理解 Vue,记住模板中的所有内容总是编译成纯 JavaScript 并使用该工具作为vue-compiler-online来查看 Vue 编译器的输出确实很有帮助。

例如这样的模板:

<foo :settings="{ value: 'hello' }" />

编译成这样:

function render() {
    with(this) {
        return _c('foo', {
            attrs: {
                "settings": {
                    value: 'hello'
                }
            }
        })
    }
}

像这样的模板:

<foo :settings="dataMemberOrComputed" />

编译成这样:

function render() {
    with(this) {
        return _c('foo', {
            attrs: {
                "settings": dataMemberOrComputed
            }
        })
    }
}

突然很清楚,在第一个示例中每次都会创建新对象,并且在第二个示例中使用对对象的相同引用(相同或不同......取决于逻辑)。

我看到有些代码也是直接在模板中赋值对象,感觉自然方便。

我仍然是 Vue 新手,但我不时浏览像 Vuetify 这样的“大”库的源代码来学习新东西,我不认为将“新对象”表达式传递给 prop 是常见的模式(这会有什么用?)。

很常见的是<foo v-bind="{ propA: propAvalue, propB: propBValue }">-传递对象的属性,这是一个非常不同的事情......


推荐阅读