Vue 3:计算属性不跟踪其在组合 API 中的依赖关系
Posted
技术标签:
【中文标题】Vue 3:计算属性不跟踪其在组合 API 中的依赖关系【英文标题】:Vue 3: computed property doesn't track its dependency in composition API 【发布时间】:2021-01-13 23:15:22 【问题描述】:考虑这个说明性示例:
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
的更新,但事实并非如此。
为什么会这样以及如何解决?
【问题讨论】:
【参考方案1】:在这个特殊的例子中,当初始渲染时预期不同的结果时,我们可以使用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>
【讨论】:
【参考方案2】:这是因为如果你这样写,Vue 无法发现你的计算属性message
和引用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");
<script src="https://unpkg.com/vue@next"></script>
<div id="root">
name: <input v-model="name"/> <br/>
message: message
</div>
【讨论】:
很好的解释,但在这里添加一个观察者有点过头了(至少你应该在firstTime.value = false
之后取消观察它,因为它不再需要了)。
@marzelin 对,这实际上只是为了说明机制。话虽如此,如果您观看了某些内容,但在第一次突变后该内容从未改变,那么您是否停止观看实际上并没有什么不同。
是的,但在这里你看到的是name
,它会随着用户输入而改变,而不仅仅是一次。
@marzelin 啊,我明白了。以为手表是第一次。在这种情况下你是对的【参考方案3】:
将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>
【讨论】:
【参考方案4】: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 3:计算属性不跟踪其在组合 API 中的依赖关系的主要内容,如果未能解决你的问题,请参考以下文章
Vue 3 组合 API,如何在 setup() 函数中获取上下文父属性?