Vue 3:计算属性不会在组合 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");
<script src="https://unpkg.com/vue@next"></script>
<div id="root">
name: <input v-model="name"/> <br/>
message: {{ message }}
</div>

正如您所看到的,message存储了一个计算的值,该值应该跟踪对name的更新,但它不是
为什么会这样?如何修复?

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");
<script src="https://unpkg.com/vue@next"></script>
<div id="root">
name: <input v-model="name"/> <br/>
message: {{ message }}
</div>

这是因为如果以这种方式编写,Vue将无法发现计算属性message和引用name之间的依赖关系。问题出在firstTime变量上。

目前的情况是,Vue在运行时(而不是编译时(通过执行计算属性并观察在此过程中访问了哪些反应引用来发现依赖关系:

  • 您的计算属性需要有机会至少运行一次。Vue通过在注册时立即运行它来确保它
  • 在执行计算属性的过程中,需要访问reactive-ref。由于第一次运行计算属性时不会访问name.value,因此这对您来说是不正常的。因为在第一次运行期间,根本没有访问任何反应性ref,所以您的计算属性将永远不会再次被触发

如果你不是第一次访问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");
<script src="https://unpkg.com/vue@next"></script>
<div id="root">
name: <input v-model="name"/> <br/>
message: {{ message }}
</div>

name.value分配给计算属性开头的变量,然后在结尾返回

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

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

在这个特定的例子中,当初始渲染预期会有不同的结果时,我们可以使用watch而不是computed,因为watch是惰性的,不会在初始渲染时执行(只有当其依赖项name发生变化时(,这正是这里所需要的。

const App = {
setup() {
const name = Vue.ref("");
Vue.watch(name, () => state.message = `Hello ${name.value}`);
const state = {
name,
message: "Welcome stranger"
};
return state;
}
};
Vue.createApp(App).mount("#root");
<script src="https://unpkg.com/vue@next"></script>
<div id="root">
name: <input v-model="name" /> <br/> message: {{ message }}
</div>

最新更新