Vue 3:计算 属性 不跟踪其在组合中的依赖性 API
Vue 3: computed property doesn't track its dependency in 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");
<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>
在计算 属性 的开头将 name.value
分配给一个变量,然后在末尾 return 它
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>
这是因为 Vue 无法 发现计算 属性 message
和引用 name
之间的依赖关系,如果你这样写的话.问题是 firstTime
变量。
发生的事情是 Vue 在 运行 时间 (而不是编译时间)通过 运行ning 计算的 属性 和 观察 在此过程中 访问了 哪些反应性引用:
- 您的计算 属性 需要至少 运行 一次 的机会。 Vue 通过在注册时立即 运行ning 来确保它。
- 在您的计算 属性 执行期间,需要 访问 响应式引用。这对你来说是坏的,因为
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");
<script src="https://unpkg.com/vue@next"></script>
<div id="root">
name: <input v-model="name"/> <br/>
message: {{ message }}
</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>
考虑这个说明性的例子:
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>
在计算 属性 的开头将 name.value
分配给一个变量,然后在末尾 return 它
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>
这是因为 Vue 无法 发现计算 属性 message
和引用 name
之间的依赖关系,如果你这样写的话.问题是 firstTime
变量。
发生的事情是 Vue 在 运行 时间 (而不是编译时间)通过 运行ning 计算的 属性 和 观察 在此过程中 访问了 哪些反应性引用:
- 您的计算 属性 需要至少 运行 一次 的机会。 Vue 通过在注册时立即 运行ning 来确保它。
- 在您的计算 属性 执行期间,需要 访问 响应式引用。这对你来说是坏的,因为
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");
<script src="https://unpkg.com/vue@next"></script>
<div id="root">
name: <input v-model="name"/> <br/>
message: {{ message }}
</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>