zoukankan      html  css  js  c++  java
  • vue 响应式原理

    响应式原理

    简单点讲 vue 的响应式是通过 Object.defineProperty 和 观察者模式来实现的。
    vue 初始化的时候 watcher 构造函数通过 Object.defineProperty 方法对 data 属性进行递归遍历,设置 get、set,初始化编译的时候会触发 getter 函数,进行依赖收集,将观察者 watcher 添加到目标对象 dep 中。改变数据的时候会触发 set, 执行 notify 方法,调用 dep 中 watcher 对象的 update 方法,update 方法将 watcher 添加到 watcher 队列中, 通过调用 nextTick 异步执行,触发更新。

    至于实现的细节,现在细细道来...

    我们需要分析的大致是以下源码文件:

    • observer
      • dep.js
      • index.js
      • scheduler.js
      • watcher.js

    先看 index.js

    export class Observer { // 定义 Observer 构造函数
      value: any;
      dep: Dep;
      vmCount: number; // number of vms that have this object as root $data
    
      constructor (value: any) {
        this.value = value
        this.dep = new Dep()
        this.vmCount = 0
        def(value, '__ob__', this)
        if (Array.isArray(value)) { // 如果是数组
          if (hasProto) {
            protoAugment(value, arrayMethods)
          } else {
            copyAugment(value, arrayMethods, arrayKeys)
          }
          this.observeArray(value)
        } else {
          this.walk(value)
        }
      }
    
      /**
       * Walk through all properties and convert them into
       * getter/setters. This method should only be called when
       * value type is Object.
       */
      walk (obj: Object) {
        const keys = Object.keys(obj)
        for (let i = 0; i < keys.length; i++) { // 对属性进行遍历
          defineReactive(obj, keys[i])
        }
      }
    
      /**
       * Observe a list of Array items.
       */
      observeArray (items: Array<any>) {
        for (let i = 0, l = items.length; i < l; i++) {
          observe(items[i])
        }
      }
    }
    
    ...
    ...
    ...
    
    /**
     * Define a reactive property on an Object.
     */
    export function defineReactive ( // 把对象的属性变成响应式 
      obj: Object,
      key: string,
      val: any,
      customSetter?: ?Function,
      shallow?: boolean
    ) {
      const dep = new Dep()
    
      const property = Object.getOwnPropertyDescriptor(obj, key) // 获取obj对象key属性的数据属性
      if (property && property.configurable === false) { // 如果该属性是不能修改或删除的,则直接返回
        return
      }
    
      // cater for pre-defined getter/setters
      const getter = property && property.get //尝试拿到该对象原生的get属性,保存到getter中
      const setter = property && property.set //尝试拿到该对象原生的set属性,保存到setter中
      if ((!getter || setter) && arguments.length === 2) { //如果getter不存在或者setter存在,且参数只有两个
        val = obj[key] //则直接通过obj[ke]获取值,并保存到val中
      }
    
      let childOb = !shallow && observe(val) //递归调用observe:当某个对象的属性还是对象时会进入
      Object.defineProperty(obj, key, { //调用Object.defineProperty设置obj对象的访问器属性
        enumerable: true,
        configurable: true,
        get: function reactiveGetter () {
          const value = getter ? getter.call(obj) : val
          if (Dep.target) {
            dep.depend() //调用depend()收集依赖, 
            if (childOb) { // 如果childOb存在
              childOb.dep.depend()  //则调用childOb.dep.depend()收集依赖
              if (Array.isArray(value)) { // 如果 val 是数组则调用小下面的函数处理
                dependArray(value)
              }
            }
          }
          return value
        },
        set: function reactiveSetter (newVal) {
          const value = getter ? getter.call(obj) : val //如果之前有定义getter,则调用getter获取值,否则就赋值为val
          /* eslint-disable no-self-compare */
          if (newVal === value || (newVal !== newVal && value !== value)) { //如果value没有改变就直接返回
            return
          }
          /* eslint-enable no-self-compare */
          if (process.env.NODE_ENV !== 'production' && customSetter) {
            customSetter()
          }
          // #7981: for accessor properties without setter
          if (getter && !setter) return
          if (setter) {
            setter.call(obj, newVal)
          } else {
            val = newVal
          }
          childOb = !shallow && observe(newVal) // newVal调用observe处理,newVal为数组或对象其属性也是响应式
          dep.notify() // 通知订阅的 watcher 做更新
        }
      })
    }
    

    接下来看 dep.js

     notify () {
        // stabilize the subscriber list first
        const subs = this.subs.slice()
        if (process.env.NODE_ENV !== 'production' && !config.async) {
          // subs aren't sorted in scheduler if not running async
          // we need to sort them now to make sure they fire in correct
          // order
          subs.sort((a, b) => a.id - b.id)
        }
        for (let i = 0, l = subs.length; i < l; i++) { // subs 中存的 wather,循环执行 watcher 中的 update 方法
          subs[i].update()
        }
      }
    

    接着看 watcher.js

    /**
       * Subscriber interface.
       * Will be called when a dependency changes.
       */
      update () {
        /* istanbul ignore else */
        if (this.lazy) {
          this.dirty = true
        } else if (this.sync) {
          this.run()
        } else {
          queueWatcher(this) // 执行 watcher 队列 queueWatcher 方法
        }
      }
    
    

    scheduler.js:

    export function queueWatcher (watcher: Watcher) {
      const id = watcher.id
      if (has[id] == null) { // 相同的 watcher 只添加进队列一次
        has[id] = true
        if (!flushing) {
          queue.push(watcher)
        } else {
          // if already flushing, splice the watcher based on its id
          // if already past its id, it will be run next immediately.
          let i = queue.length - 1
          while (i > index && queue[i].id > watcher.id) {
            i--
          }
          queue.splice(i + 1, 0, watcher)
        }
        // queue the flush
        if (!waiting) {
          waiting = true
    
          if (process.env.NODE_ENV !== 'production' && !config.async) {
            flushSchedulerQueue()
            return
          }
          nextTick(flushSchedulerQueue)  // 调用异步 nestTick 处理 watcher 队列
        }
      }
    }
    
  • 相关阅读:
    匈牙利算法-二分图的最大匹配
    UOJ 407(IOI2018 D1T3)
    UOJ 460
    UOJ 405(IOI2018 D1T1)
    Codeforces 1110E
    2.文件结构
    1.常用快捷键
    Python3.x和Python2.x的差异
    javascript 常用内置对象
    94. Binary Tree Inorder Traversal(非递归实现二叉树的中序遍历)
  • 原文地址:https://www.cnblogs.com/hi-shepherd/p/12608651.html
Copyright © 2011-2022 走看看