Vue双向数据绑定原理

78 阅读3分钟

image.png vue在初始化的时候,在initState方法中会调取initData方法初始化data数据,对data对象进行遍历,在这个方法中会调observe,让数据变得响应式。

  1. 在observe函数中会获取value上的__ob__属性指向的Observer实例,如果这个属性没有被定义,就会根据数据创建一个Observer实例

  2. Observer类会给每个value打上__ob__属性,值为该Observer实例,表示这个value已经被转化成响应式的了。

    接下来将会对数据类型进行判断:

  • 对象则遍历**对象**的属性(利用walk()方法)调用`defineReactive`
    
  • 数组:`调用observeArray`(),遍历数组的元素,递归调用observe
    
  1. defineReactive 函数方法内部使用 Object.defineProperty 对数据进行劫持,在getter中收集依赖,setter中通知依赖更新。
  • get中通过Dep类实例,dep.depend触发**Watcher(订阅者)** 的依赖收集,收集订阅者;如果是数组则遍历数组,将数组中的每个值都通过depend添加到依赖。
    
  • set时会对数据进行对比,如果数据没有发生变化就不会进行派发更新,变化了则通过 dep.notify()发布通知,通知所有依赖更新,遍历所有依赖,依次执行watcher的update方法
    

单独说数组---- 数组则通过重写数组方法来实现的【push,pop.shift,unshift.splice,sort.reverse】。 流程

  1. 以Array.prototype为原型创建一个对象arrayMethods对象
  2. 让响应式数组继承自arrayMethods
  3. 遍历数组方法 缺陷:无法对除了这7个方法之外的修改数组进行响应式--因此Vue增加了两个全局API:Vue.setVue.delete
let arr = [1,2,3]
arr[0] = 5;       // 通过数组下标修改数组中的数据
arr.length = 0    // 通过修改数组长度清空数组

不容易理解的

window.target和dep.target 就是一个watcher对象,我们在dep实例中收集watcher对象的目的就是在数据发生变化时,能够调用收集到的watcher对象的update方法来更新视图

重要函数

1.Observe 函数

获取value上的 __ob__属性指向的Obverser实例,如果该属性未定义,根据数据创建一个Obverser实例; 在实例化是会向value添加 __ob__属性

2.Observer类

  • 是一个工厂函数,将一个正常的object转换为每一个层级的属性都是响应式的
    

3.defineReactive方法

  • 给数据添加响应式,在getter中收集依赖,setter中通知依赖更新
    

4.Dep类

  • 依赖收集管理器
    

5.watcher类

  • 连接表达式和值,说白了就是 watcher 连接视图层的依赖,并可以触发视图层的更新,与 Dep 紧密结合,通过 Dep 来控制其对视图层的监听
    

6.cached 方法--生成带有缓存的函数

export function cached<F: Function> (fn: F): F {
  const cache = Object.create(null)
  return (function cachedFn (str: string) {
    const hit = cache[str]  // 如果已缓存,hit就是有数据的,如果未缓存hit 就是 undefined
    return hit || (cache[str] = fn(str))  // hit 没有数据,那么就调用fnstr缓存起来并返回
  }: any)

7.camelize 将我们字符串的命名规则转换为 骆驼命名规则

const camelizeRE = /-(\w)/g
export const camelize = cached((str: string): string => {
  return str.replace(camelizeRE, (_, c) => c ? c.toUpperCase() : '')
})

对象的响应式处理

(1)让object数据变的可观测

  • --利用object.defineProperty(),将该属性的读和写,利用get()和set()进行拦截
    

数组的响应式处理

以Array.prototype 为原型创建了一个arrayMethods对象,并强制让数组的 proto 指向arrayMethods