Vue2.0 核心之响应式流程

栏目: JavaScript · 发布时间: 5年前

内容简介:不考虑这两个步骤实现了依赖的收集与通知、以及初始化,我在github上的项目

Vue2.0 核心之响应式流程

看了部分Vue源码分析或运行机制的文章,接收到这些信息:

  • 使用 Object.defineProperty 实现响应监听,
  • 使用 DepWacther 实现依赖收集追踪
  • 使用 Virtual Dom 、高效 diff 算法实现最小化更新

对整个流程还是没有理解,或者写了流程,也是作者自己实现的一套,不是Vue的源码。为了熟悉Vue真正的流程花了点时间看了Vue的源码,通过debugger(浏览器版本)自己梳理了一遍。

以下代码来源自Vue,只做了删减,保留核心。

new Vue(options) 的执行流程,debugger的主体流程如下:

  1. beforeCreate Hook
  2. initSate()

    • initData() -- oberve
  3. created Hook
  4. vm.$mount(vm.$options.el)

    1. 根据template解析出render function
    2. 声明 update function

      updateComponent = function () {
            vm._update(vm._render(), hydrating);
        };
    3. new Watcher(vm, updateComponent, noop,{before...}
    4. mounted Hook

不考虑 updateComponentHooks 剩下:

initData() -- oberve
new Watcher(vm, updateComponent, noop,{before...}

这两个步骤实现了依赖的收集与通知、以及初始化,我在github上的项目 learnVue 截取了Vue核心代码进行流程学习,以下是我简化后的代码:

import {Observer} from './observer';
    import  Watcher  from "./watcher";
    let noop =function(){};
    let vm={
        _watchers:[],
        data :{
            name:'jack',
            age:12
        }
        // render:new Function("with(this){return _c('div',{attrs:{\"id\":\"app\"}},[_v(_s(name))])}")
    }
    new Observer(vm.data);

    new Watcher(vm,function(){
        //update function 
        var name=vm.data.name;
        var age=vm.data.age;
        console.log(name,age)
    },noop);

下面代码,控制台会正常输出 lili , 10 :

setTimeout(()=>{
    vm.data.name='lucy';
    vm.data.age=10;
    vm.data.name='lili';
},1000)

依赖收集

什么优化都不做 Object.defineProperty 定义 set 即可实现同步。后果是任一属性更新都会同步dom,造成性能浪费。Vue使用 订阅/观察 模式做了第一步优化。

打开 vue项目下observer文件夹 ,可以看到以下几个文件:

index.js(observer) //植入响应式钩子
dep.js //依赖管理
watcher.js //观察者,同步UI

index.js 核心代码:

const dep = new Dep()
    Object.defineProperty(obj, key, {
        enumerable: true,
        configurable: true,
        get: function reactiveGetter() {
            const value = val
            if (Dep.target) {
                dep.depend()
            }
            return value
        }
    })

调用 dep.js 中的方法

depend () {
    if (Dep.target) {
      Dep.target.addDep(this)
    }
  }

Dep.target 又是什么呢?这就是 new Watcher 的作用了。

watcher.js:

constructor(
        vm,
        expOrFn,
        cb
    ) {
        this.vm = vm
        this.getter = expOrFn
        this.value = this.get() //初始化
    }
    get() {
        pushTarget(this) //推送 Dep.target
        let value
        const vm = this.vm
        value = this.getter.call(vm, vm) //触发 get钩子

        popTarget() //还原 Dep.target
        this.cleanupDeps()
        return value
    }

Watcher.get() 函数中进行依赖收集,画了一下运行流程(绿色和蓝色线条):

Vue2.0 核心之响应式流程

通过这个流程我们可以看到,Watcher 就是负责把内存的值同步到UI的。

异步更新

同步更新会导致大量的重绘,从而导致UI性能问题。Vue采用异步更新策略做了第二步优化,把一个批次的修改一次更新给UI。来看下index.js中 set 方法。

set: function reactiveSetter(newVal) {
            const value = val
            /* eslint-disable no-self-compare */
            if (newVal === value || (newVal !== newVal && value !== value)) {
                return
            }
            val = newVal
        dep.notify()
    }

dep.js

notify () {
    // stabilize the subscriber list first
    const subs = this.subs.slice()
    for (let i = 0, l = subs.length; i < l; i++) {
      subs[i].update() //Watcher.update
    }
  }

watcher.js

/**
     * Subscriber interface.
     * Will be called when a dependency changes.
     */
    update() {
        /* istanbul ignore else */
        queueWatcher(this)
    }

queueWatcherWatcher 给了 queueWatcher //scheduler.js 调度中心,异步的实现,优先 promise ,降级 setTimeout

export const nextTick = (function () {
    const callbacks = []
    let pending = false
    let timerFunc
  
    function nextTickHandler () {
      pending = false
      const copies = callbacks.slice(0)
      callbacks.length = 0
      for (let i = 0; i < copies.length; i++) {
        copies[i]()
      }
    }
  
    // the nextTick behavior leverages the microtask queue, which can be accessed
    // via either native Promise.then or MutationObserver.
    // MutationObserver has wider support, however it is seriously bugged in
    // UIWebView in iOS >= 9.3.3 when triggered in touch event handlers. It
    // completely stops working after triggering a few times... so, if native
    // Promise is available, we will use it:
    /* istanbul ignore if */
    if (typeof Promise !== 'undefined') {
      var p = Promise.resolve()
      var logError = err => { console.error(err) }
      timerFunc = () => {
        p.then(nextTickHandler).catch(logError)
      }
    }else {
      // fallback to setTimeout
      /* istanbul ignore next */
      timerFunc = () => {
        setTimeout(nextTickHandler, 0)
      }
    }
  
    return function queueNextTick (cb, ctx) {
      let _resolve
      callbacks.push(() => {
        if (cb) cb.call(ctx)
        if (_resolve) _resolve(ctx)
      })
      if (!pending) {
        pending = true
        timerFunc()
      }
      if (!cb && typeof Promise !== 'undefined') {
        return new Promise(resolve => {
          _resolve = resolve
        })
      }
    }
  })()

流程图,黄色线条:

Vue2.0 核心之响应式流程

未完待续

暂时只研究了 内存 => UIUI => 内存 以后再研究。框架的弯弯绕绕但没有一行冗余代码,Vue贼6

Vue2.0 核心之响应式流程


以上就是本文的全部内容,希望对大家的学习有所帮助,也希望大家多多支持 码农网

查看所有标签

猜你喜欢:

本站部分资源来源于网络,本站转载出于传递更多信息之目的,版权归原作者或者来源机构所有,如转载稿涉及版权问题,请联系我们

看见未来

看见未来

余晨 / 浙江大学出版社 / 2015-4-15 / 59.00元

【内容简介】 这是互联网群星闪耀的时代,巨人们用最尖端的技术和自成体系的哲学改变着我们的生活,甚至影响了整个世界和人类的历史进程。在这个时代,没有人可以避开互联网的渗透。互联网早已不是简单的技术变革,人们正试图赋予其精神和内涵,以期互联网能更好地为人类所用。 本书中作者 面对面地采访了包括马克·扎克伯格、埃隆·马斯克、杨致远、凯文·凯利、克里斯·安德森、罗伯特·希勒、迈克尔·莫瑞茨、凯......一起来看看 《看见未来》 这本书的介绍吧!

HTML 压缩/解压工具
HTML 压缩/解压工具

在线压缩/解压 HTML 代码

HTML 编码/解码
HTML 编码/解码

HTML 编码/解码

Markdown 在线编辑器
Markdown 在线编辑器

Markdown 在线编辑器