这篇文章主要介绍“vue.js数据响应式原理是什么”,在日常操作中,相信很多人在vue.js数据响应式原理是什么问题上存在疑惑,小编查阅了各式资料,整理出简单好用的操作方法,希望对大家解答”vue.js数据响应式原理是什么”的疑惑有所帮助!接下来,请跟着小编一起来学习吧!
得力于 Object.defineProperty() 的特性,vue 的数据变化有别于 react 和小程序,是非侵入式的。详细介绍可以看 MDN 文档,这里特别说明几点:
get / set 属性是函数,出于习惯会被称为 getter 函数 / setter 函数(Java,c++ 中都有这种惯例)
value 或 writable
和 get 或 set
是不能同时出现的,否则报错
注意区别 Object.defineProperties()
Object.defineProperty()
在使用 getter 和 setter 的时候,要想实现属性的修改,需要借助一个变量周转,如下面的 value,这就很麻烦。
const obj = {} let value Object.defineProperty(obj, 'a', { enumerable: true, configurable: true, get() { console.log('getter') return value }, set(newValue) { value = newValue console.log('setter', newValue) } })
所以定义了 defineReactive 函数,方便去给对象增加一个响应式属性。这里创建一个闭包的环境:闭包一定要有内外两个函数,外面这个函数 defineReactive 的 value 就形成了闭包。
const obj = {} function defineReactive(data, key, value) { // 如果只传了两个参数,则让 value 直接等于 data[key] if (arguments.length === 2) value = data[key] Object.defineProperty(data, key, { enumerable: true, // 可被枚举(for...in 或 Object.keys 方法) configurable: true, // 可被配置,比如删除 get() { console.log('查看了' + key + '属性') return value }, set(newValue) { console.log('修改了' + key + '属性') value = newValue } }) } defineReactive(obj, 'a', 10) console.log(obj.a) obj.a = 11 console.log(obj.a)
得到的结果如下图:
我们自己写一个能够侦测对象全部属性的库
新建 index.js 作为主入口文件,用于测试效果,我们 let 一个对象 obj,目标是通过把 obj 作为参数传给 observe 函数,即可实现对 obj 对象所有属性的查看与修改的监测。
// index.js import observe from './observe.js' let obj = { a: { m: { n: 1 } }, b: 2 } observe(obj)
observe 函数用于观察一个对象(value)的属性是否已被监测的(是否有 __ob__
属性),如果不是则让其属性成为响应式的(通过 new Observer(value)
)。
注意:之所以起了 __ob__
这么奇怪的变量名,是为了保证不会与对象的原有属性同名。
// observe.js import Observer from './Observer.js' export default (value) => { if (typeof value !== 'object') return if (value.__ob__ !== undefined) { // 暂时留空 } else { new Observer(value) } }
Observer 是一个类,一旦 new 了一个实例,则做 2 件事:
给传入的 value(其实是个对象) 添加 __ob__
属性,值为这次 new 的实例(也就是构造函数中的 this),因为希望 __ob__
属性是不可被枚举的,所以用 def 函数处理。
遍历 value 的属性,通过 defineReactive 函数将其变为响应式的
// Observer.js import { def } from './utils.js' import defineReactive from './defineReactive.js' export default class Observer { constructor(value) { def(value, '__ob__', this, false) this.walk(value) } // 处理对象,让对象的属性变为响应式 walk(value) { for (let key in value) { defineReactive(value, key) } } }
def 函数定义如下:
export const def = (obj, key, value, enumerable) => { Object.defineProperty(obj, key, { value, enumerable, writable: true, configurable: true }) }
相较于前面定义的时候,在两个地方添加了 observe(value),从而实现了递归侦测对象的全部属性。这里的参数 value,就是已经被变为响应式的属性的值,这个值如果是个对象,也需要被侦测,所以也要被 observe。
// defineReactive.js import observe from './observe.js' export default function defineReactive(data, key, value) { if (arguments.length === 2) value = data[key] // 注意这里不是传 key 而是传 value,因为 key 只是一个字符串,value 才是 key 指向的对象 observe(value) // 让 data 的 key 属性变为响应式属性 Object.defineProperty(data, key, { enumerable: true, configurable: true, get() { console.log('查看了' + key + '属性') return value }, set(newValue) { console.log('修改了' + key + '属性') value = newValue // 修改的属性也需要被观察,如果是对象需要被侦测 observe(newValue) } }) }
至此,在 index.js 传入 observe 的 obj 的每个属性都是响应式的了
// index.js ...省略前面的代码 obj.a.m = { y: 8 } console.log(obj.a.m.y)
测试结果如下:
普通对象也是有 getter 和 setter 的:
get propertyName(){} 用来得到当前属性值的回调函数
set propertyName(){} 用来监视当前属性值变化的回调函数
下面的代码中,属性 a 称为“数据属性”,它只有一个简单的值;属性b这种用 getter 和 setter 方法定义的属性称为“存取器属性”。
var num= { a: 2, get b(){ return 2 } }
存取器属性定义为一个或两个与属性同名的函数,这个函数定义不使用 function 关键字,而是使用 get 或 set,也没有使用冒号将属性名和函数体分开。
到此,关于“vue.js数据响应式原理是什么”的学习就结束了,希望能够解决大家的疑惑。理论与实践的搭配能更好的帮助大家学习,快去试试吧!若想继续学习更多相关知识,请继续关注亿速云网站,小编会继续努力为大家带来更多实用的文章!
免责声明:本站发布的内容(图片、视频和文字)以原创、转载和分享为主,文章观点不代表本网站立场,如果涉及侵权请联系站长邮箱:is@yisu.com进行举报,并提供相关证据,一经查实,将立刻删除涉嫌侵权内容。