关于前端:前端必读Vue响应式系统大PK

5次阅读

共计 4639 个字符,预计需要花费 12 分钟才能阅读完成。

转载请注明出处:葡萄城官网,葡萄城为开发者提供业余的开发工具、解决方案和服务,赋能开发者。
原文参考:https://www.sitepoint.com/vue…

响应式零碎(Reactivity systems)是古代前端框架的要害局部之一。利用零碎的的高度交互性、动态性和响应能力全靠它反对。每个 Web 开发人员而言都应该理解这一零碎的性能和实际操作。

原理

响应零碎是一种使主动使数据源(模型)与数据表示(视图)层主动放弃同步的机制。每次模型更改时,都会从新渲染视图。

以一个简略的 Markdown 编辑器为例。通常编辑器有两个窗格:一个窗格用于编写 Markdown 代码(用于批改根底模型),另一个窗格用于预览已编译的 HTML(显示已更新的视图)。当咱们在书写窗格中写货色时,它会立刻在预览窗格中主动预览。这个例子比较简单,在理论状况中会简单很多。

在许多状况下,咱们要显示的数据取决于其余数据。在这种状况下,须要跟踪相干数据,并依据跟踪状况来更新数据。例如,咱们有一个 fullName,该属性由 firstName 和 lastName 属性组成。批改其任何依赖项后,fullName 将主动从新评估,并在视图中显示后果。

理解什么是响应式零碎后,在理解 Vue 3 中的响应零碎如何工作以及如何在实践中应用之前,让咱们一起来疾速回顾一下 Vue 2 中的响应零碎内容及其注意事项。

Vue 2 的响应式零碎简介

Vue 2 中的响应或多或少会被“暗藏”。无论咱们搁置在 data 对象中的是什么,Vue 都会使其隐式反馈(reactive implicitly)。这样尽管能够使开发人员的工作更加轻松,但灵便度却会不可避免的升高。
在幕后,Vue 2 应用 ES5 Object.defineProperty 将 data 对象的所有属性转换为 getter 和 setter。对于每个组件实例,Vue 创立一个依赖关系察看程序实例,观察者会记录组件渲染期间依赖收集 / 跟踪的任何属性。当属性触发依赖项的设置器时,将告诉观察者,并将组件从新渲染并更新视图。然而却也会有一些问题存在。

变更检测正告

因为 Object.defineProperty 办法的限度,Vue 无奈检测到某些数据更改。包含:

  • 给对象增加属性或把对象移除属性(例如 obj.newKey = value)
  • 按索引设置数组项(例如 arr[index] = newValue)
  • 批改数组的长度(例如 arr.length = newLength)
    不过为了解决这些问题,Vue 为提供了 Vue.set API 办法,该办法向响应对象增加了一个属性,确保新属性也是响应性的,从而触发了视图更新。

用下述实例探讨该状况:

  <h1>Hello! My name is {{person.name}}. I'm {{person.age}} years old.</h1>
  <button @click="addAgeProperty">Add "age" property</button>
  <p>Here are my favorite activities:</p>
  <ul>
    <li v-for="item, index in activities" :key="index">
      {{item}}
      <button @click="editActivity(index)">Edit</button>
    </li>
  </ul>
  <button @click="clearActivities">Clear the activities list</button>
</div>
  el: '#app',
  data: {
    person: {name: "David"},
    activities: [
      "Reading books",
      "Listening music",
      "Watching TV"
    ]
  },
  methods: { 
    // 1. Add a new property to an object
    addAgeProperty() {this.person.age = 30},
    // 2. Setting an array item by index
    editActivity(index) {const newValue = prompt('Input a new value')
      if (newValue) {this.activities[index] = newValue
      }
    },
    // 3. Modifying the length of the array
    clearActivities() {this.activities.length = 0}
  }
});

在下面的示例中,咱们会发现这三种办法都不起作用。咱们不能向该 person 对象增加新属性,无奈应用 activities 的索引来编辑数组中的我的项目,也不能批改 activities 数组的长度。

优化如下:

  el: '#app',
  data: {
    person: {name: "David"},
    activities: [
      "Reading books",
      "Listening music",
      "Watching TV"
    ]
  },
  methods: { 
    // 1. Adding a new property to the object
    addAgeProperty() {Vue.set(this.person, 'age', 30)
    },
    // 2. Setting an array item by index
    editActivity(index) {const newValue = prompt('Input a new value')
      if (newValue) {Vue.set(this.activities, index, newValue)
      }
    },
    // 3. Modifying the length of the array
    clearActivities() {this.activities.splice(0)
    }
  }
});

在此示例中,咱们用 Vue.setAPI 办法将新 age 属性增加到 person 对象,并从流动数组中抉择 / 批改特定我的项目。在最初一种状况下,应用 JavaScript 内置 splice 办法。

这个做法齐全可行但却略显蠢笨,而且会导致前后代码不统一。而 Vue 3 就解决了这个问题。
咱们用上面示例持续看:

  data() {
    return {
      person: {name: "David"},
      activities: [
        "Reading books",
        "Listening music",
        "Watching TV"
      ]
    }
  },
  methods: { 
    // 1. Adding a new property to the object
    addAgeProperty() {this.person.age = 30},
    // 2. Setting an array item by index
    editActivity(index) {const newValue = prompt('Input a new value')
      if (newValue) {this.activities[index] = newValue
      }
    },
    // 3. Modifying the length of the array
    clearActivities() {this.activities.length = 0}
  }
}

Vue.createApp(App).mount('#app')

能够看到在 Vue 3 中,所有办法都能够失常工作。

在 Vue 2.6 中,引入的 Vue.observable API 办法,肯定水平的公开了响应式零碎,使开发人员能够体验到响应式零碎的内容。实际上,这和 Vue 外部用来包装 data 对象是完全相同的办法,对于在简略场景创立小的跨组件状态存储很有用。但仍旧没方法和 Vue3 的响应式零碎相比,接下来就为大家具体介绍。

留神:因为 Object.defineProperty 办法是仅限 ES5 且不可调整的性能,因而 Vue 2 不反对 IE8 及以下版本。

Vue 3 响应式零碎如何工作

为了充分利用 ES6 Proxy and Reflect API,Vue 3 中的响应式零碎已被齐全重写。新版本新增响应式 API,该 API 使零碎比以前更加灵便和弱小。

Proxy API 容许开发人员拦挡和批改指标对象上的更低级对象操作。代理(proxy)是对象的克隆 / 包装(clone/wrapper),并提供非凡性能(称为 target),这些性能响应特定的操作并笼罩 JavaScript 对象的内置行为(称为 traps)。如果依然须要应用默认行为,则能够应用相应的 Reflection API,其名称顾名思义就是反映 Proxy API 的办法。这里有一个示例,用来理解如何在 Vue 3 中应用这些 API:

  name: "David",
  age: 27
};

const handler = {get(target, property, receiver) {// track(target, property)
    console.log(property) // output: name
    return Reflect.get(target, property, receiver)
  },
  set(target, property, value, receiver) {// trigger(target, property)
    console.log(`${property}: ${value}`) // output: "age: 30" and "hobby: Programming"
    return Reflect.set(target, property, value, receiver)
  }
}

let proxy = new Proxy(person, handler);   

console.log(person)

// get (reading a property value)
console.log(proxy.name)  // output: David

// set (writing to a property)
proxy.age = 30;

// set (creating a new property)
proxy.hobby = "Programming";

console.log(person) 

要创立一个新的代理,应用 new Proxy(target, handler) 构造函数。它带有两个参数:指标对象(person 对象)和处理程序对象,该对象定义将拦挡哪些操作(get 和 set 操作)。在 handler 对象中,get 和 set 陷阱来跟踪何时读取属性以及何时批改 / 增加属性。设置控制台语句以确保运行正确。

在 get 和 set 陷阱采纳下列参数:

  • target:代理包装的指标对象
  • property:属性名称
  • value:属性值(此参数仅用于设置操作)
  • receiver:进行操作的对象(通常是代理)
  • Reflect API 办法与其相应的代理办法承受雷同的参数

正文中 track 函数和 trigger 函数特定用于 Vue,用于跟踪何时读取属性以及何时批改 / 增加属性。

在示例的最初一部分,用控制台语句输入原始 person 对象。而后用另一份申明中读取属性 name 的 proxy 对象。接下来,批改 age 属性并创立一个新 hobby 属性。最初,再次输入该对象以查看它是否正确更新。

以上就是 Vue3 响应式零碎的残缺工作流程,但在理论工作中会简单得多。

应用 Vue 3 响应式零碎,还有一些注意事项:

  • 仅实用于反对 ES6 + 的浏览器
  • 响应代理不等于原始对象

总结

以上咱们将 Vue2 和 Vue3 中响应式零碎局部进行了比拟,并对响应式零碎的工作原理进行了阐明,在前面的文章中,咱们会进一步为大家介绍 Vue3 中响应式零碎的 API,敬请期待。

拓展浏览

如果你曾经熟练掌握 Vue3, 无妨让咱们通过理论搭建一款基于 Vue 3 组件的表格编辑零碎,更加深刻学习

正文完
 0