首页 > 语言 > JavaScript > 正文

深入浅出 Vue 系列 -- 数据劫持实现原理

2024-05-06 15:40:18
字体:
来源:转载
供稿:网友

一、前言

数据双向绑定作为 Vue 核心功能之一,其实现原理主要分为两部分:

    数据劫持 发布订阅模式

本篇文章主要介绍 Vue 实现数据劫持的思路,下一篇则会介绍发布订阅模式的设计。

二、针对 Object 类型的劫持

对于 Object 类型,主要劫持其属性的读取与设置操作。在 JavaScript 中对象的属性主要由一个字符串类型的“名称”以及一个“属性描述符”组成,属性描述符包括以下选项:

    value: 该属性的值; writable: 仅当值为 true 时表示该属性可以被改变; get: getter (读取器); set: setter (设置器); configurable: 仅当值为 true 时,该属性可以被删除以及属性描述符可以被改变; enumerable: 仅当值为 true 时,该属性可以被枚举。

上述 setter 和 getter 方法就是供开发者自定义属性的读取与设置操作,而设置对象属性的描述符则少不了 Object.defineProperty() 方法:

function defineReactive (obj, key) { let val = obj[key] Object.defineProperty(obj, key, {  get () {   console.log(' === 收集依赖 === ')   console.log(' 当前值为:' + val)   return val  },  set (newValue) {   console.log(' === 通知变更 === ')   console.log(' 当前值为:' + newValue)   val = newValue  } })}const student = { name: 'xiaoming'}defineReactive(student, 'name') // 劫持 name 属性的读取和设置操作

上述代码通过 Object.defineProperty() 方法设置属性的 setter 与 getter 方法,从而达到劫持 student 对象中的 name 属性的读取和设置操作的目的。

读者可以发现,该方法每次只能设置一个属性,那么就需要遍历对象来完成其属性的配置:

 Object.keys(student).forEach(key => defineReactive(student, key))

另外还必须是一个具体的属性,这也非常的致命。

假如后续需要扩展该对象,那么就必须手动为新属性设置 setter 和 getter 方法,**这就是为什么不在 data 中声明的属性无法自动拥有双向绑定效果的原因 **。(这时需要调用 Vue.set() 手动设置)

以上便是对象劫持的核心实现,但是还有以下重要的细节需要注意:

1、属性描述符 - configurable

在 JavaScript 中,对象通过字面量创建时,其属性描述符默认如下:

const foo = { name: '123'}Object.getOwnPropertyDescriptor(foo, 'name') // { value: '123', writable: true, enumerable: true, configurable: true }

前面也提到了 configurable 的值如果为 false,则无法再修改该属性的描述符,所以在设置 setter 和 getter 方法时,需要注意 configurable 选项的取值,否则在使用 Object.defineProperty() 方法时会抛出异常:

// 部分重复代码 这里就不再罗列了。function defineReactive (obj, key) { // ... const desc = Object.getOwnPropertyDescriptor(obj, key) if (desc && desc.configurable === false) {  return } // ...}            
发表评论 共有条评论
用户名: 密码:
验证码: 匿名发表

图片精选