首页 > 语言 > JavaScript > 正文

详解Vue源码学习之双向绑定

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

原理

当你把一个普通的 JavaScript 对象传给 Vue 实例的 data 选项,Vue 将遍历此对象所有的属性,并使用 Object.defineProperty 把这些属性全部转为 getter/setter。Object.defineProperty 是 ES5 中一个无法 shim 的特性,这也就是为什么 Vue 不支持 IE8 以及更低版本浏览器。

上面那段话是Vue官方文档中截取的,可以看到是使用Object.defineProperty实现对数据改变的监听。Vue主要使用了观察者模式来实现数据与视图的双向绑定。

function initData(vm) { //将data上数据复制到_data并遍历所有属性添加代理 vm._data = vm.$options.data; const keys = Object.keys(vm._data);  let i = keys.length; while(i--) {   const key = keys[i];  proxy(vm, `_data`, key); } observe(data, true /* asRootData */) //对data进行监听}

在第一篇数据初始化中,执行new Vue()操作后会执行initData()去初始化用户传入的data,最后一步操作就是为data添加响应式。

实现

在Vue内部存在三个对象:Observer、Dep、Watcher,这也是实现响应式的核心。

Observer

Observer对象将data中所有的属性转为getter/setter形式,以下是简化版代码,详细代码请看这里。

export function observe (value) { //递归子属性时的判断 if (!isObject(value) || value instanceof VNode) {  return } ... ob = new Observer(value)}export class Observer { constructor (value) {  ... //此处省略对数组的处理  this.walk(value) } walk (obj: Object) {  const keys = Object.keys(obj)  for (let i = 0; i < keys.length; i++) {   defineReactive(obj, keys[i]) //为每个属性创建setter/getter  } } ...}//设置set/getexport function defineReactive ( obj: Object, key: string, val: any) { //利用闭包存储每个属性关联的watcher队列,当setter触发时依然能访问到 const dep = new Dep() ... //如果属性为对象也创建相应observer let childOb = observe(val) Object.defineProperty(obj, key, {  enumerable: true,  configurable: true,  get: function reactiveGetter () {   if (Dep.target) {    dep.depend() //将当前dep传到对应watcher中再执行watcher.addDep将watcher添加到当前dep.subs中    if (childOb) { //如果属性是对象则继续收集依赖     childOb.dep.depend()     ...    }   }   return value  },  set: function reactiveSetter (newVal) {   ...   childOb = observe(newVal) //如果设置的新值是对象,则为其创建observe   dep.notify() //通知队列中的watcher进行更新  } })}

创建Observer对象时,为data的每个属性都执行了一遍defineReactive方法,如果当前属性为对象,则通过递归进行深度遍历。该方法中创建了一个Dep实例,每一个属性都有一个与之对应的dep,存储所有的依赖。然后为属性设置setter/getter,在getter时收集依赖,setter时派发更新。这里收集依赖不直接使用addSub是为了能让Watcher创建时自动将自己添加到dep.subs中,这样只有当数据被访问时才会进行依赖收集,可以避免一些不必要的依赖收集。

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

图片精选