共计 1277 个字符,预计需要花费 4 分钟才能阅读完成。
最近出去面试,栽在这个问题上,提到 vuejs,面试官一般会让你说 vuejs 的特点,一般就要回答 virtual dom tree, dom tree diff, 以及数据双向绑定, 然后面试官会追问你,vuejs 是如何实现数据双向绑定的,前面的问题算基础的话,能答出这个就更上一个台阶,说明你的思考能力不停留在表层,遗憾的是我只能大概说出 Object.defineProperty。我回来搜了一下,发现其实 vuejs 的官网对这个原理是有详尽的阐释的,如果失败了只能怪自己准备不足。这篇文章我就整理一下分享给大家,如果有错误还请指出。
vuejs 官网对这个问题的解释是 对响应式原理的解释,这里:https://cn.vuejs.org/v2/guide…
问题就是 vuejs 如何追踪对象的属性变化,答是利用 es5 的 Object.defineProperty, 参考:https://developer.mozilla.org…
Object.defineProperty 是一个无法被 shim 的属性,就是说它无法被降级使用,这也是 vuejs 不支持 ie8 以下的根本原因。
Object.defineProperty 用来设置一个对象的某一个属性,这都不是最关键的,关键是在设置属性的同时,可以设置 setter/getter,setter/getter 设置两个函数,在这个属性被调用或者设置的时候自动执行,所以在 setter 的函数里,只要写了更新 dom 的方法,就可以在这个属性变化的时候执行,实现了属性变化的追踪。
实际上,vuejs 的实现更加复杂,遵照这张流程图:
vuejs 里每一个组件对应了一个 watcher,Object.defineProperty 是紫色的圆圈,当组件里某一个属性被 get 的时候,getter 函数会通知 Watcher,“说我这有一个属性被渲染了,你记一下”,然后当这个属性的 setter 被触发(也就是该属性数据被修改的时候),也会通知 Watcher,说“我这有这样一个东西被改了,你看看在不在你的名单里。”Watcher 此时去检查被改的属性在不在自己记录的名单里,如果在,就通知组件渲染程序,让它再去更新虚拟 dom 树。
需要注意的几个点:
1.getter/setter 对用户是不可见的,是在 vue 内部实现的。2.js 里无法监听对象属性的增加或者删除,所以 vue 只能在开始 data 里添加响应式属性,所以当组件创建完毕,再给这个组件塞一个属性,这个属性是无法响应到 dom 的。3.vue 会在组件初始化的过程中进行 getter/setter 转换,所以也无法动态插入新属性,插入了也是非响应数据,但可以通过 Vue.set(object, key, value) 方法将属性加入到后台可响应的对象中。4. 官网还介绍了更新队列,上文说的 Watcher 中的更新会被推入到一个更新队列中,那么就是说数据更新后不会马上反映到 dom 上。5. 但是我们可以通过 Vue.nextTick(callback) 方法,将这次数据更新马上反映到 dom 上,这个方法的 callback 是 dom 更新完成的回调。