【更新中】【翻译】Vue3.0数据响应式

Last updated: 7/19/2020, 1:51:27 AM(官方文档更新时间)

Reactivity

深入响应式

何为响应式?

在JS中如何实现数据响应式?

  • 检测其中某个值是否发生变化
  • 跟踪变化函数
  • 触发此函数来更新值

Vue如何实现跟踪变化

当您将纯JavaScript对象作为data选项传递给Vue实例时,Vue将遍历其所有属性,并使用带有getter和setter的处理程序将其转换为Proxy。 这是ES6支持的功能,但在我们提供Vue 3.0中,使用较旧的Object.defineProperty支持IE浏览器。两者具有相同的Surface API,但是Proxy版本更轻量,性能更佳。

打个广告,如果对Proxy不太熟悉,可以看看我之前的博客,proxy学习笔记,故这里跳过vue3文档关于Proxy用法的讲解。

之前我们提到过,为了使API能够在发生变化时更新最终值,我们将必须在发生变化时set
新值。 我们在handler中通过一个名为track的函数进行此操作,在该函数中传递targetkey

最后,当变化发生时,我们会set新值。 为此,我们通过触发这些更改来set到新Proxy上:

const dinner = {
  meal: 'tacos'
}

const handler = {
  get(target, prop, receiver) {
    track(target, prop)
    return Reflect.get(...arguments)
  },
  set(target, key, value, receiver) {
    trigger(target, key)
    return Reflect.set(...arguments)
  }
}

const proxy = new Proxy(dinner, handler)
console.log(proxy.meal)

还记得文章开始提到的如何实现数据响应式吗?现在我们来解答以下Vue是如何处理这些变化的:

  • 检测其中某个值是否发生变化 Vue不再做这些了,因为我们有了Proxy可以帮我们进行拦截操作
  • 跟踪变化函数:我们在Proxy的getter中来实现,称之为effect(副作用)。
  • 触发此函数来更新数据:我们在Proxy的setter中来实现,称之为trigger。

Proxy对象对用户是透明的,但是在后台,它们使Vue在访问或修改属性时,能够执行依赖跟踪和更改通知。 从Vue 3开始,我们的数据响应式可以在单独的软件包中使用。 需要注意的是,记录转换后的数据对象时,浏览器控制台的格式会有所不同,因此您可能需要安装vue-devtools以获得更友好的检查界面。

Proxied Objects

Vue在内部跟踪所有reactive对象,因此它始终为同一对象返回相同的代理。

当从reactive代理那访问嵌套对象时,该对象在返回之前也将转换为代理:

const handler = {
  get(target, prop, receiver) {
    track(target, prop)
    const value = Reflect.get(...arguments)
    if (isObject(value)) {
      return reactive(value)
    } else {
      return value
    }
  }
  // ...
}

Procy vs original identity

使用Proxy确实会引入新的警告,需要注意的是:代理对象不等于(===)target对象本身。如:

const obj = {}
const wrapped = new Proxy(obj, handlers)

console.log(obj === wrapped) // false

原对象和代理对象在大多数情况下将具有相同的行为,但是请注意,它会使依赖于强比较的操作(如:.filter().map() )失败。 使用选项API时,这种警告不太可能出现,因为所有响应状态都可以从this中访问,并保证已经是代理。

然而,使用Composition API来生成响应式对象时,最佳做法是永远不要保留对原始对象的引用,而是只使用reactive版本:

const obj = reactive({
  count: 0
}) // no reference to original

Watchers

每个组件实例都有一个相应的watcher实例,将组件渲染期间“涉及”到的所有属性记录为依赖项。稍后,当触发依赖项的setter时,它会通知watcher,从而导致组件重新渲染。

当您将对象作为data传递给Vue实例时,Vue会将其转换为Proxy。 当访问或修改属性时,该代理使Vue能够执行依赖项跟踪(dependency-tracking)和更改通知(change-notification)。 每个属性均被视为依赖项。

在第一次渲染之后,一个组件将跟踪一个依赖项列表——它在渲染过程中访问的属性。 相反,该组件成为每个依赖属性的订阅者。 当代理拦截set操作时,该属性将通知其所有订阅的组件重新渲染。


Last updated: 7/18/2020, 5:08:24 PM(官方文档更新时间)

reactive 基础

声明reactive状态

把一个JS对象变成一个响应式的状态要用到reactive方法:

import { reactive } from 'vue'

// reactive state
const state = reactive({
  count: 0
})

reacttive等效于Vue 2.x中的Vue.observable()API,改了个名称以防与RxJS observables混淆。 在这里,返回的状态是一个响应式的对象。 响应式转换是“深度”的——它影响传递对象的所有嵌套属性。

Vue中响应式状态的基本用例是我们可以在渲染期间使用它。 由于依赖关系跟踪,当响应式状态更改时,视图会自动更新。

这是Vue响应式系统的本质。 当您在组件的data()中返回一个对象时,该对象会在data内部被reactive()变为可响应的。 模板被编译为依赖这些reactive属性的渲染函数。

你可以在Basic Reactivity API’s中了解更多关于reactive的特性。

创建独立的响应式value作为refs

想象一下,我们有一个独立的原始值(例如,一个string),并且希望使其具有响应性。 当然,我们可以使一个对象具有与字符串相等的单个属性,并将其传递给reactive。 Vue提供一种方法可以由相同的效果-ref

import { ref } from 'vue'

const count = ref(0)

ref将返回一个响应的、可变的对象,该对象充当对其内部属性值的响应式引用——这就是名称的来源。 该对象仅包含一个名为value的属性:

import { ref } from 'vue'

const count = ref(0)
console.log(count.value) // 0

count.value++
console.log(count.value) // 1

展开ref

当ref作为渲染上下文(从setup()返回的对象)的属性返回并在模板中访问时,它会自动展开为内部值。 无需在模板中附加.value

<template>
  <div>
    <span>{{ count }}</span>
    <button @click="count ++">Increment count</button>
  </div>
</template>

<script>
  import { ref } from 'vue'
  export default {
    setup() {
      const count = ref(0)
      return {
        count
      }
    }
  }
</script>

reactive对象的访问

ref作为reactive对象的属性进行访问或改变时,它会自动展开为内部值,因此其表现为普通属性:

const count = ref(0)
const state = reactive({
  count
})

console.log(state.count) // 0

state.count = 1
console.log(count.value) // 1

如果一个新ref被分配给已有ref关联的属性,它将替换旧的ref

const otherCount = ref(2)

state.count = otherCount
console.log(state.count) // 2
console.log(count.value) // 1

ref展开仅在reactive对象内部存在嵌套时发生。 从数组或原生集合类型(如Map)访问ref时,不会执行ref展开

const books = reactive([ref('Vue 3 Guide')])
// need .value here
console.log(books[0].value)

const map = reactive(new Map([['count', ref(0)]]))
// need .value here
console.log(map.get('count').value)

解构reactive状态

当我们想使用一个属性很多的reactive对象中的小部分属性时,可以使用ES6的解构来得到我们想要的属性:

import { reactive } from 'vue'

const book = reactive({
  author: 'Vue Team',
  year: '2020',
  title: 'Vue 3 Guide',
  description: 'You are reading this book right now ;)',
  price: 'free'
})

let { author, title } = book

不幸的是,解构后这些属性的响应式特性会消失。这种情况下,我们需要将reactive对象转换为refsrefs将保留与源对象的响应式连接:

import { reactive, toRefs } from 'vue'

const book = reactive({
  author: 'Vue Team',
  year: '2020',
  title: 'Vue 3 Guide',
  description: 'You are reading this book right now ;)',
  price: 'free'
})

let { author, title } = toRefs(book)

title.value = 'Vue 3 Detailed Guide' // we need to use .value as title is a ref now
console.log(book.title) // 'Vue 3 Detailed Guide'

你可以在Refs API中了解更多。

readonly 防止更改reactive对象

有些时侯,我们想跟踪reactive对象(ref或reactive)的变化,但是,我们也想防止从应用的某个位置更改它。 例如,当我们有一个reactive对象时,我们想防止在注入它的地方对其进行更改。 为此,我们可以为原始对象创建一个只读代理:

const original = reactive({ count: 0 })

const copy = readonly(original)

// mutating original will trigger watchers relying on the copy
original.count++

// mutating the copy will fail and result in a warning
copy.count++ // warning: "Set operation on key 'count' failed: target is readonly."

不好意思,翻不动了,等我哪天无聊再搞吧~

计算属性和watch

你可能感兴趣的:(前端学习)