• 微信号
  • 微信号
您当前的位置:首页 > 学海无涯 > 茑语花香>vue2响应式原理

vue2响应式原理

孤峰 孤峰家 2024-01-09 53人阅读

数据响应式:视图(view)跟着数据(data)的变化而同步变化。

要实现响应式,就必须得知道并能获取数据的变化,这样才能把变化的数据同步更新到视图上去,也就是说要能监听到data数据的变化。

01

如何监听数据的变化:

Vue数据响应式的核心是Object.defineProperty,它的作用是为对象的某个属性对外提供 get、set 方法,从而实现外部对该属性的读和写操作时能够被内部监听,实现后续的同步视图更新功能。

Object.defineProperty:当访问此对象的属性时,会自动调用get方法,当修改此对象的某个属性时会自动调用set方法。从而可以得知对象的属性是否被访问或者修改了,这是第一步。

以下是使用Object.defineProperty方法给data对象添加一个name属性,并给name赋值为“雪天”,这样添加的name属性是可控的,我们可以在name属性被访问或者被修改(劫持)时做一些其他的东西。

// Vue中的data const data = {} // 响应式监听 data 中的 name Object.defineProperty(data, "name", { // 使用 data.name 时 get 方法被调用,返回内部存储变量值 get: () => { console.log('get,被访问') }, // 使用 data.name = xxx 修改变量时,set 方法被调用,设置内部存储变量值 set: (newVal) => { console.log('set,被修改',newVal) } }) 

data.name = '雪天' // 输出 set console.log('name值',data.name) // 输出 undefined get

上面代码给data对象添加了name属性,并使用set方法为name属性赋值为“雪天”,但是输出name时为undefined,这是因为赋值虽然被set监听到了,但是get并没有拿到set监听到的这个的值,由于访问name属性会调用get方法,所以就是undefined。那接下来就需要set把拿到的newVal给get,只有给了get,访问时才能得到较新的name值。

因为set并没有赋值给get数据的能力,所以我们需要弄一个中转变量tmp来实现把newVal给到get,以下是更新后的代码:

// Vue 中的 data const data = {} 

// 需要的中转变量 var tmp = ''

Object.defineProperty(data, "name", { get: () => { console.log('get,被访问') // 2.get再去拿到tmp的值 return tmp }, set: (newVal) => { console.log('set,被修改------',newVal) // 1.set把拿到的较新值先给中转变量tmp tmp = newVal } })

data.name = '雪天' // 输出 set,被修改------ 雪天 console.log('name值-----',data.name) // 输出 雪天 get

到这里,使用Object.defineProperty给对象添加属性并赋值的操作就完成了。但是这样弄一个中转变量并不是较优解,Vue的做法是利用闭包实现。也就是说不用中转变量,封装一个函数,使用外层函数的参数来代替中转变量

02

创建函数 defineReactive 创建函数 defineReactive ,对Object.defineProperty 进行封装,接受三个参数,监听的目标对象、属性名,以及属性值,一个target(对象)通过调用 defineReactive 就能够实现对 key(对应属性名)进行监听

以下是defineReactive函数及其使用:

var data = {} 

// 重新定义属性 function defineReactive(target, key, value) { Object.defineProperty(target, key, { get() { return value }, set(newVal) { // value 一直在闭包中,此处设置完成后,下次get能够获取较新设置的值 // 若相同则不触发更新 if (newVal !== value) { value = newVal // 触发更新 console.log("更新了"); } } }) }

// 调用defineReactive defineReactive(data,'name',"雪天"); // 访问data console.log(data.name); // 修改name data.name = 'xt' console.log(data.name);

到此,监听一个对象的属性变化就可以了,还需把对象的每一个属性都使用Object.defineProperty的方法去定义一遍。那么接下来就需要一个方法,用来枚举对象属性,此函数命名为observe

03

observe函数

// 监听对象属性 target是对象名function observe(target){ if(typeof target !== 'object' || target === null) { // 判断 不是数组或对象不适合监听 return target } // 遍历 将对象的属性用 defineProperty的方式 重新定义 for(let key in target) { defineReactive(target, key, target[key]) } }

有observe函数,就可以监听到对象中所有的属性的变化了。

// 测试的数据const data = { name: 'xt', id: 1, info: { phone: '13900000000', e**il: '12xxxxx@qq.com' } } 

// 重新定义对象属性,监听function defineReactive(target, key, value){ Object.defineProperty(target, key, { get(){ return value }, set(newVal){ // 若相同则不触发更新 if(newVal !== value){ value = newVal // 触发更新 console.log("更新了"); } } }) }

// 监听对象属性function observe(target){ if(typeof target !== 'object' || target === null) { // 不是数组或对象则不监听 return target } // 将对象的属性用 defineProperty 重新定义 for(let key in target) { defineReactive(target, key, target[key]) } }

//调用observe observe(data)

// 访问dataconsole.log(data.name);

// 修改name data.name = '雪天'console.log(data.name);

// 修改phone data.info.phone = '1382222222'console.log(data.info.phone);

发现name属性没问题,可以修改也可以监听到,打印出"更新了",但是info>phone属性并没有被监听到,这是因为以上写的只能监听一层对象,下面继续优化代码,实现对象多层监听,也就是深度监听。

解决方法: 对对象的所有属性进行监听函数的递归调用,在执行Object.defineProperty前先进行递归调用observe,如果该属性为对象,则observe会递归调用defineReactive,不是则observe直接返回,继续执行Object.defineProperty。

具体做法就是直接在Object.defineProperty执行前先调用observe(value)函数。这里的value就是对象的值,把这个值扔给observe函数先判断是否为对象,如果是对象,那就再次遍历属性,再次调用defineReactive函数,直到对象的值不再是对象为止。

04

最终代码以下是添加深度监听的代码,也是最终版:

// 测试的数据 const data = { name: 'xt', id: 1, info: { phone: '13900000000', e**il: '12xxxxx@qq.com' } } 

// 重新定义属性,监听 function defineReactive(target, key, value) { // 把对象值扔给observe函数再次判断 observe(value) Object.defineProperty(target, key, { get() { return value }, set(newVal) { // value 一直在闭包中,此处设置完成后,下次get能够获取较新设置的值 // 这里有个小优化,若相同则不触发更新 if (newVal !== value) { value = newVal // 触发更新 console.log("更新了"); } } }) }

// 监听对象属性 function observe(target) { if (typeof target !== 'object' || target === null) { // 不是数组或对象不适合监听 return target } // 将对象的属性用 defineProperty 重新定义 for (let key in target) { defineReactive(target, key, target[key]) } }

observe(data)

// 访问data console.log('name初始值----'+data.name);

// 修改name data.name = '雪天' console.log('name修改后----'+data.name);

// 访问data console.log('phone初始值----'+data.info.phone);

// 修改phone data.info.phone = '1382222222' console.log('phone修改后----'+data.info.phone);

到此,即可实现data对象的所有属性进行监听了,能监听到就能获取到,把获取到的值再通过指令的形式同步到视图。

转载:感谢您阅览,转载请注明文章出处“来源从小爱孤峰知识网:一个分享知识和生活随笔记录的知识小站”。

链接:vue2响应式原理http://www.gufeng7.com/niaolang/1948.html

联系:如果侵犯了你的权益请来信告知我们删除。邮箱:119882116@qq.com

标签: