Vue 3:计算属性不跟踪其在组合 API 中的依赖关系

mar*_*lin 2 javascript vue.js vue-component vuejs3 vue-composition-api

考虑这个说明性示例:

const App = {
 setup() {
  const name = Vue.ref("");
  let firstTime = true;
  const message = Vue.computed(() => {
    if (firstTime) {
      firstTime = false;
      return "Welcome stranger";
    }
    return `Hello ${name.value}`;
  });
  
  return {
    name,
    message
  }
 }
};

Vue.createApp(App).mount("#root");
Run Code Online (Sandbox Code Playgroud)
<script src="https://unpkg.com/vue@next"></script>
<div id="root">
  name: <input v-model="name"/> <br/>
  message: {{ message }}
</div>
Run Code Online (Sandbox Code Playgroud)

如您所见,message存储一个计算值,该值应该跟踪更新,name但它不是。
为什么会这样以及如何解决?

taw*_*ser 5

Computed 应始终使用您想要计算的不可变的反应式 ref 对象。

因此,如果您在开始时声明正在使用的反应性对象,它将起作用。

const App = {
 setup() {
  const name = Vue.ref("");
  let firstTime = true;
  const message = Vue.computed(() => {
    name.value;
    if (firstTime) {
      firstTime = false;
      return "Welcome stranger";
    }
    return `Hello ${name.value}`;
  });
  
  return {
    name,
    message
  }
 }
};

Vue.createApp(App).mount("#root");
Run Code Online (Sandbox Code Playgroud)
<script src="https://unpkg.com/vue@next"></script>
<div id="root">
  name: <input v-model="name"/> <br/>
  message: {{ message }}
</div>
Run Code Online (Sandbox Code Playgroud)


Xin*_*hao 5

这是因为如果你这样写,Vue无法message发现你的计算属性和 ref之间的依赖关系。name问题是firstTime变量。

发生的事情是,Vue通过运行计算属性并观察在此过程中访问了哪些响应式引用,在运行时(而不是编译时)发现依赖关系:

  • 您的计算属性需要有机会至少运行一次。Vue 通过在注册后立即运行来确保这一点。
  • 在执行计算属性期间,需要访问反应式引用。这对您来说是有问题的,因为name.value您的计算属性第一次运行时不会被访问。而且因为在第一次运行期间根本没有访问响应式引用,所以您的计算属性将永远不会再次被触发。

如果您不是第一次访问 name.value 也没关系,但您需要访问其他响应性的东西,并且当firstTime变为 false 时会发生变化:

const App = {
 setup() {
  const name = Vue.ref("");
  const firstTime = Vue.ref(true);

  Vue.watch(()=> name.value, ()=>{firstTime.value=false;});

  const message = Vue.computed(() => {
   
    if (firstTime.value) {
      return "Welcome stranger";
    }
    return `Hello ${name.value}`;
  });
  
  return {
    name,
    message
  }
 }
};

Vue.createApp(App).mount("#root");
Run Code Online (Sandbox Code Playgroud)
<script src="https://unpkg.com/vue@next"></script>
<div id="root">
  name: <input v-model="name"/> <br/>
  message: {{ message }}
</div>
Run Code Online (Sandbox Code Playgroud)