Почему простая константа все еще работает в Composition API (Vue 3)?

Как указано в официальной документации, при определении нашей переменной данных в новой композиции api setup, мы должны использовать ref или reactive, чтобы Vue мог отслеживать любые изменения в этой конкретной переменной.

Я экспериментирую с реактивностью и обнаружил странное поведение. Возьмите этот компонент этот компонент как ссылку:

App.vue

<template>
  <div id="app">
    <p>{{ awesomeState.txt }}</p>
    <p>{{ myConst }}</p>
    <button @click="operate">Test it</button>
  </div>
</template>

<script lang="ts">
import { defineComponent } from "vue";
import awesomeStore from "@/AwesomeStore";

export default defineComponent({
  setup() {
    const awesomeState = awesomeStore.getState();
    const myConst = "I'm a const"; // This should be const `myConst = ref('...');`
    return {
      awesomeState,
      myConst
    };
  },
  name: "App",
  methods: {
    operate() {
      this.myConst = 'Why this updates?';
      awesomeStore.changeText("yess!");
    }
  }
});
</script>

AwesomeStore.ts

import { reactive, readonly, watch } from "vue";

export abstract class Store<T extends Record<string, any>> {
  protected state: T;

  constructor() {
    const data = this.data();
    this.state = reactive(data) as T;
    watch(() => this.state, (value) => {
      console.log(value); debugger;
    }, {deep: true});
  }

  protected abstract data(): T;

  public getState(): T {
    return readonly(this.state) as T;
  }
}

interface Test extends Object {
  txt: string;
}

class AwesomeClass extends Store<Test> {
  protected data(): Test {
    return {
      txt: "init"
    };
  }
  public changeText(msg: string) {
    this.state.txt = msg;
  }
}

export default new AwesomeClass();

Когда я нажимаю кнопку, свойство myConst обновляется. Кроме того, я не должен определять метод за пределами setup(), я не понимаю, почему это происходит.

Кто-нибудь может получить подсказку?

Заранее спасибо,

ОБНОВЛЕНИЕ

Я забыл упомянуть, что открытое поведение имеет место только в том случае, если я пытаюсь изменить константу вместе с методом awesomeStore.changeText:

operate() {
      this.myConst = 'Why this updates?';
      //awesomeStore.changeText("yess!"); <-- If commented, myConst do not update.
    }

person albertTaberner    schedule 27.08.2020    source источник


Ответы (1)


Здесь происходят две вещи, которые заставляют его работать

1) вы используете метод вместо передачи функции

Вы можете использовать стандартный Vue API data:, methods:,etc... или API композиции, например: setup()

В вашем примере используются оба. AFAIK, это нормально, однако имейте в виду, что метод, переданный setup vs methods, будет вести себя по-другому. В методе у вас будет доступ к объекту this, где myConst является изменяемым

  methods: {
    operate() {
      this.myConst = 'Why this updates?'; // <=== access to mutable variable in `this` 
      awesomeStore.changeText("yess!");
    }
  }

если бы вы использовали этот пример (ниже), обновление myConst не сработало бы

setup(){
  const myConst = "I'm a const"; // This should be const `myConst = ref('...');`
  // ...stuff...
  return {
    awesomeState,
    myConst,
    operate: ()=>{
      this.myConst = 'Why this updates?'; // <=== nope
      myConst = 'Why this updates?';      // <=== also nope
      awesomeStore.changeText("yess!");
    }
  }
}

2) Вы обновляете ref и static

Другая проблема заключается в том, что вы обновляете статическое значение, за которым следует ссылка. Изменение ref приведет к повторному запуску рендеринга, после чего статическое значение изменится.

поэтому, если вы удалите обновление через магазин, вы больше не должны видеть, что обновление происходит

  methods: {
    operate() {
      this.myConst = 'Why this updates?';
      // awesomeStore.changeText("yess!"); // <== no ref change, no update
    }
  }

вот площадка для проверки моей теории

const {
  createApp,
  ref,
} = Vue

createApp({
  el: '#app',
  setup() {
    let mystatic = 1
    const myref = ref(1)
    return {
      mystatic,
      myref,
      updateStatic: () => {
        mystatic++
      },
      updateRef: () => {
        myref.value++
      }
    }

  },
  methods: {
    updateStaticMethod() {
      this.mystatic++
    }
  }
}).mount('#app')
<script src="https://unpkg.com/vue@next/dist/vue.global.js"></script>

<div id="app">
  <li>Static: {{ mystatic }}</li>
  <li>Ref: {{ myref }}</li>
  <li><button @click="updateStatic">Static+</button> will not change. ever.</li>
  <li><button @click="updateStaticMethod">Static+(method)</button> will not update unless triggered elswhere</li>
  <li><button @click="updateRef">Ref+</button>update ref value (and force redraw)</li>
  <li><button @click="()=>{updateStaticMethod();updateRef();}">Both++</button> update both</li>
</div>

person Daniel    schedule 27.08.2020
comment
Привет, Даниэль, Спасибо за любезное объяснение. Я думал, что vue ищет значение только для объекта со свойством value, чтобы отобразить его в шаблоне. Я думаю, ваш пункт 2 является ключевым. awesomeStore.changeText повторно обработайте шаблон. В любом случае, я также думал, что Vue отображает только те части DOM, которые меняются. - person albertTaberner; 28.08.2020