Vue 3 中的 ref 与反应式?

Den*_*soi 58 vuejs3 vue-composition-api

看一些人的 Vue 3 预览教程的一些例子。 [目前是测试版]

我找到了两个例子:

反应式

<template>
  <button @click="increment">
    Count is: {{ state.count }}, double is: {{ state.double }}
  </button>
</template>

<script>
import { reactive, computed } from 'vue'

export default {
  setup() {
    const state = reactive({
      count: 0,
      double: computed(() => state.count * 2)
    })

    function increment() {
      state.count++
    }

    return {
      state,
      increment
    }
  }
}
</script>
Run Code Online (Sandbox Code Playgroud)

参考

<template>
  <div>
    <h2 ref="titleRef">{{ formattedMoney }}</h2>
    <input v-model="delta" type="number">
    <button @click="add">Add</button>
  </div>
</template>

<script>
import { ref, computed, onMounted } from "vue";

export default {
  setup(props) {
    // State
    const money = ref(1);
    const delta = ref(1);

    // Refs
    const titleRef = ref(null);

    // Computed props
    const formattedMoney = computed(() => money.value.toFixed(2));

    // Hooks
    onMounted(() => {
      console.log("titleRef", titleRef.value);
    });

    // Methods
    const add = () => (money.value += Number(delta.value));

    return {
      delta,
      money,
      titleRef,
      formattedMoney,
      add
    };
  }
};
</script>
Run Code Online (Sandbox Code Playgroud)

Chr*_*yes 107

关键点

  • reactive()只接受对象,而不是JS 原语(字符串、布尔值、数字、BigInt、符号、空值、未定义)
  • ref()reactive()在幕后呼唤
  • 由于reactive()适用于对象和ref()调用reactive(),因此对象适用于两者
  • 但是,ref().value重新分配的属性,reactive()没有这个,因此不能重新分配

ref() 什么时候..

  • 它是一个原始(例如'string'true23等)
  • 这是一个您需要稍后重新分配的对象(如数组 -此处有更多信息

reactive() 什么时候..

  • 这是一个您不需要重新分配的对象,并且您希望避免 ref()

总之

ref()似乎是要走的路,因为它支持所有对象类型并允许使用.value. ref()是一个很好的起点,但是当您习惯了 API 时,就会知道它的reactive()开销更少,并且您可能会发现它更能满足您的需求。

ref() 用例

您将始终ref()用于原语,但ref()适用于需要重新分配的对象,例如数组。

setup() {
    const blogPosts = ref([]);
    return { blogPosts };
}
getBlogPosts() {
    this.blogPosts.value = await fetchBlogPosts();
}
Run Code Online (Sandbox Code Playgroud)

上面的 withreactive()需要重新分配一个属性而不是整个对象。

setup() {
    const blog = reactive({ posts: [] });
    return { blog };
}
getBlogPosts() {
    this.blog.posts = await fetchBlogPosts();
}
Run Code Online (Sandbox Code Playgroud)

reactive() 用例

一个很好的用例reactive()是一组属于一起的原语:

const person = reactive({
  name: 'Albert',
  age: 30,
  isNinja: true,
});
Run Code Online (Sandbox Code Playgroud)

上面的代码感觉比

const name = ref('Albert');
const age = ref(30);
const isNinja = ref(true);
Run Code Online (Sandbox Code Playgroud)

有用的链接

如果你仍然迷路,这个简单的指南帮助了我:https : //www.danvega.dev/blog/2020/02/12/vue3-ref-vs-reactive/

只使用的论点ref()https : //dev.to/ycmjason/thought-on-vue-3-composition-api-reactive-thinked-harmful-j8c

Vue Composition API RFC 的原因reactive()ref()存在背后的决策以及其他重要信息:https : //vue-composition-api-rfc.netlify.app/#overhead-of-introducing-refs

  • 这也许是最好的答案和最有据可查的答案。清晰、简洁、开门见山,并提供示例。这应该在 Vue 官方文档中。不开玩笑 (4认同)
  • https://v3.vuejs.org/guide/composition-api-template-refs.html#usage-inside-v-const list = Reactor([1, 2, 3]) 和 const divs = 之间有什么区别参考([])? (2认同)
  • @Yiping 说得好。数组是一个对象,并且都接受对象,所以我不确定。但是,事实证明,使用“ref”的“.value”重新分配数组有“reactive”所没有的优点。https://github.com/vuejs/docs-next/issues/801#issuecomment-757587022 (2认同)
  • @Bryant Object.assign 合并对象。它不会重新分配。假设您有两个反应:一个用于存储原始状态,另一个用于存储修改后的状态。如果您尝试使用 Object.assign 将修改后的内容恢复到原始状态,您将得到一个合并的对象。`const Original = Reactor({name: "Original"})` `const Modified = Reactor({name: "Modified", Age: 5})` `Object.assign(Modified, Original);` =&gt; {name: “原始”,年龄:5} (2认同)
  • 如果您想要更详细的答复,我花了很长时间研究并写下了我自己对这个问题的想法:https://michaelnthiessen.com/ref-vs-reactive/ (2认同)

rol*_*oli 22

我将简单解释为什么有两种创建反应状态的方法:

其他答案已经显示了两者之间的差异


reactive:创建反应状态。返回对象的反应式代理:

import { reactive } from 'vue'

const reactiveObj = reactive({ count: 0 })
reactiveObj.count++
Run Code Online (Sandbox Code Playgroud)

通过 Options API,我们过去常常将反应状态保持在data(). 通过 Composition API,我们可以使用 API 实现相同的目的reactive。到目前为止,一切都很好,但是……

为什么我们需要ref???

原因很简单,因为reactive有以下限制:

  • 反应性损失:
const state = reactive({ count: 0 })

// the function receives a plain number and
// won't be able to track changes to state.count
callSomeFunction(state.count)
Run Code Online (Sandbox Code Playgroud)
const state = reactive({ count: 0 })
let { count } = state
// does not affect original state
count++
Run Code Online (Sandbox Code Playgroud)
let state = reactive({ count: 0 })

// this won't work!
state = reactive({ count: 1 })
Run Code Online (Sandbox Code Playgroud)
  • 它不能保存基本类型,例如字符串、数字或布尔值。

因此ref,Vue 提供了解决reactive.

ref()接受参数并将其包装在具有 .value 属性的 ref 对象中返回:

const count = ref(0)

console.log(count) // { value: 0 }
console.log(count.value) // 0

count.value++
console.log(count.value) // 1
Run Code Online (Sandbox Code Playgroud)

参考文献可以:

  • 保存任何值类型
  • 反应式替换整个对象:
const objectRef = ref({ count: 0 })

// this works reactively
objectRef.value = { count: 1 }
Run Code Online (Sandbox Code Playgroud)
  • 传递到函数中或从普通对象解构而不会失去反应性
const obj = {
  foo: ref(1),
  bar: ref(2)
}

// the function receives a ref
// it needs to access the value via .value but it
// will retain the reactivity connection
callSomeFunction(obj.foo)

// still reactive
const { foo, bar } = obj
Run Code Online (Sandbox Code Playgroud)

我应该一直使用吗ref

个人意见如下

大多数尝试过这两种方法的开发人员建议使用ref我读过的文章。

但就我个人而言,我认为这ref具有相同的局限性,reactive如果使用不当,您很容易陷入“反应性损失”问题。 ref还有一些行为,例如:

  • 在模板中展开,但这只发生在顶级属性中
  • 打开里面reactive
  • 当从数组或本地集合类型(如 Map)访问 ref 时,不会执行解包
  • 参考文献同步

每次都要处理也.value有点令人困惑,Vue 知道这一点,并且在撰写本文时有一个RFC - 反应性转换,旨在提供一个解决方案。

我希望您现在对此有了更好的理解reactiveref但我认为值得一提的是,您应该了解更多用于响应式状态的 API:readonly、shallowRef、shallowReactive、shallowReadonly、unref 等等。


Den*_*soi 21

ref和之间有一些相似之处reactive,因为它们都提供了一种存储数据的方法并允许该数据具有反应性。

然而:

高层差异:

你不能在基元(字符串、数字、布尔值)上使用reactive()——这就是你需要refs的原因,因为你会遇到需要“reactive boolean”的情况,例如……

当然,您可以创建一个包装原始值的对象并使该对象具有反应性():

const wrappedBoolean = reactive({
  value: true
})
Run Code Online (Sandbox Code Playgroud)

就这样,你重新发明了一个 ref。

来源:Vue 论坛讨论

反应式

reactive获取对象并返回proxy对原始对象的反应。

例子

import {ref, reactive} from "vue";

export default {
  name: "component",
  setup() {
    const title = ref("my cool title")
    const page = reactive({
      contents: "meh?",
      number: 1,
      ads: [{ source: "google" }],
      filteredAds: computed(() => {
        return ads.filter(ad => ad.source === "google")
      })
    })
    
    return {
       page, 
       title
    }
  }
}
Run Code Online (Sandbox Code Playgroud)

解释

在上面,每当我们想要更改或访问 的属性时page
例如page.adspage.filteredAds将通过代理进行更新。