最新消息:雨落星辰是一个专注网站SEO优化、网站SEO诊断、搜索引擎研究、网络营销推广、网站策划运营及站长类的自媒体原创博客

javascript - Vue 3: computed property doesn't track its dependency in composition API - Stack Overflow

programmeradmin1浏览0评论

Consider this illustrative example:

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

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

Consider this illustrative example:

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>

As you can see, message stores a computed value that should track updates to name but it isn't.
Why is it like that and how to fix it?

Share Improve this question edited Sep 28, 2020 at 18:35 Boussadjra Brahim 1 asked Sep 28, 2020 at 11:09 marzelinmarzelin 11.6k4 gold badges38 silver badges50 bronze badges
Add a comment  | 

4 Answers 4

Reset to default 7

Computed should always use an immutable reactive ref object you want to be computed.

so if you declare the reactive objects you are using at the beginning it will work.

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>

This is because Vue cannot discover the dependency between your computed property message and the ref name if you write it this way. The problem is the firstTime variable.

What is going on is that Vue discovers dependency at runtime (instead of compile time) by running the computed property and observing what reactive references are accessed during the process:

  • your computed property needs to be given a chance to at least run once. Vue ensures it by running it immediately when it is registered.
  • during the execution of your computed property, reactive ref needs to be accessed. This is broken for you because name.value is not accessed the first time your computed property runs. And because during the first run no reactive ref is accessed at all, your computed property will never be triggered again.

It is fine if you don't access the name.value for the first time, but you need to access something else that is reactive and will change when firstTime becomes 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>

Assign name.value to a variable in the beginning of computed property then return it at the end

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>

In this particular example when different results are expected on initial render we can use watch instead of computed since watch is lazy by default and won't be executed on initial render (only when its dependency: name changes) and that's exactly what is needed here.

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>

发布评论

评论列表(0)

  1. 暂无评论