最近接触了一些面试者,当我问起“Vue 如何实现数据双向绑定”时,会脱口而出“数据劫持”,而后呢?而后就没有而后了╮(╯_╰)╭。确实,“数据劫持”是基础,但远不是面试官想听到的答案,不如花个十分钟看看本文,下次照着回答就行了javascript
要解答问题,首先要理解问题: 数据双向绑定 是一种模式,web语境下通常指数据从dom到JS对象之间的自动同步。DOM 与 JS 被隔离在两个不一样的运行时上,互相之间须要经过命令式的 DOM接口 沟通:DOM 须要正确触发事件,将信息传输给JS程序;而JS也须要在状态变动后,有意识地调用适当的接口,改变DOM内容。这种方式会引发两个问题:vue
双向绑定经过各类各样的设计,将数据从 DOM 到 JS 或者从 JS 到 DOM 的同步过程,封装在框架自己,上层代码脱离了对底层接口的依赖,只须要关注状态管理逻辑。java
咱们要讨论的第一个问题是,如何检测 JS 对象属性发生的变动?最简单粗暴的方法是“脏检查”,旧版本的Angular就是用的这种方法,在各类可能引起状态变动的事件后,启动一次脏检查。这种方法很直观,但实现上须要考虑不少问题:git
setTimeout
、requestNextAnimationFrame
等)Vue 则采用元编程接口 Object.defineProperty
实现的。 在组件初始化,会调用该接口,将对象属性包装为get
、set
函数,将代码“埋入”属性是“获取”、“修改”行为中。看个简单例子,直观感觉下:github
const person = {};
// 嘿嘿,谁都改不了个人名字
Object.defineProperty(person, 'name', {
get() {
return 'van';
},
set(v) {
console.log('they want change my name');
}
});
console.log(person.name);
// van
person.name = 'tec';
// they want change my name
console.log(person.name);
// van
复制代码
Object.defineProperty
只是解决了状态变动后,如何触发通知的问题,那要通知谁呢?谁会关心那些属性发生了变化呢?在 Vue 中,使用 Dep 解耦了依赖者与被依赖者之间关系的肯定过程。简单来讲:web
第一步,经过 Observer 提供的接口,遍历状态对象,给对象的每一个属性、子属性都绑定了一个专用的 Dep
对象。这里的状态对象主要指组件当中的data
属性。面试
第二步,建立三中类型的watcher:编程
computed
属性转化为 watcher
实例watch
配置转化为 watcher
实例render
函数绑定 watcher
实例第三步,状态变动后,触发 dep.notify()
函数,该函数再进一步触发 Watcher 对象 update
函数,执行watcher的从新计算。设计模式
对应下图:数组
注意,Vue 组件中的 render
函数,咱们能够单纯将其视为一种特殊的 computed
函数,在它所对应的 Watcher
对象发生变化时,触发执行render,生成新的 virutal-dom 结构,再交由 Vue 作diff,更新视图。
本文到这里就结束了,更多内容能够尝试看看源码,代码里面的设计模式很是值得学习。
Vue 使用数据劫持做为底层支撑,又设计了一套精妙的依赖管理方案解耦依赖。但数据劫持方案也有其难以解决的痛点: