首页 > 语言 > JavaScript > 正文

一步一步实现Vue的响应式(对象观测)

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

平时开发中,Vue的响应式系统让我们不再去操作DOM,只需关心数据逻辑的处理,极大地降低了代码的复杂度。而响应式系统也是Vue的核心,作为开发者有必要了解其实现原理!

简易版

以watch为切入点

watch是平时开发中使用率非常高的功能,其目的是观测一个数据,当数据变化时执行我们预先定义的回调。使用方式如下:

{ watch: {  obj(val, oldVal) {   console.log(val, oldVal);  } }}

上面观测了Vue实例的obj属性,当其值发生变化时,打印出新值与旧值。

因此,我们定义一个watch函数:

function watch (data, key, cb) { // do something}
    watch函数接收3个属性,分别是 data: 被观测对象 key: 被观测的属性 cb: 数据变化后要执行的回调

Object.defineProperty

既然要在数据变化后再执行回调,所以需要知道数据是什么时候被修改的,这就是Object.defineProperty的作用,其为数据定义了访问器属性。在数据被读取时会触发get,在数据被修改时会触发set。

我们定义一个defineReactive函数,其用来将一个数据变成响应式的:

function defineReactive(data, key) { let val = data[key];  Object.defineProperty(data, key, {  configurable: true,  enumerable: true,  get: function() {   return val;  },  set: function(newVal) {   if (newVal === val) {    return;   }      val = newVal;  } });}

defineReactive函数为data对象的key属性定义了get、set,get返回属性key的值val,set中修改key的值为新值newVal。到目前为止,key属性还是没有什么特殊之处。

数据被修改会触发set,那cb一定是在set中被执行。但set与cb之间好像并没有什么联系,所以我们来搭建一座桥梁,来构建两者的联系:

let target = null;

我们在全局定义了一个target变量,它用来保存cb的值,然后在set中调用。所以,cb什么时候被保存在target中?回到出发点,我们要调用watch函数来观测data的key属性,当值被修改时执行我们定义的回调cb,这就是cb被保存在target中的时机了:

function watch(data, key, cb) { target = cb;}

watch函数中target被修改了,但我要是再想调用watch函数一次,也就是说我想在data[key]被修改时,执行两个不同的回调,又或者说,我想再观测data的其它属性,那该怎么办?必须得在target被再次修改前,将其值保存到别处。因为,target是同个属性的不同回调或不同属性的回调所共有的。

我们有必要为key属性建立一个私有的仓库,来保存回调。其实defineReactive函数有一点特殊地方:函数内部定义了一个val变量,然后在get和set函数都使用了val变量,这形成一个闭包,defineReactive函数的作用域是key属性私有的,这就是天然的私有仓库了:

发表评论 共有条评论
用户名: 密码:
验证码: 匿名发表

图片精选