Vue2.0 核心之响应式流程看了部分Vue源码分析或运行机制的文章,接收到这些信息:使用Object.defineProperty实现响应监听,使用Dep,Wacther实现依赖收集追踪使用Virtual Dom、高效diff算法实现最小化更新对整个流程还是没有理解,或者写了流程,也是作者自己实现的一套,不是Vue的源码。为了熟悉Vue真正的流程花了点时间看了Vue的源码,通过debugger(浏览器版本)自己梳理了一遍。以下代码来源自Vue,只做了删减,保留核心。new Vue(options)的执行流程,debugger的主体流程如下:beforeCreate HookinitSate()initData() – obervecreated Hookvm.$mount(vm.$options.el)根据template解析出render function声明 update function updateComponent = function () { vm._update(vm._render(), hydrating); };new Watcher(vm, updateComponent, noop,{before…}mounted Hook不考虑updateComponent和Hooks剩下:initData() – obervenew 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 //观察者,同步UIindex.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()函数中进行依赖收集,画了一下运行流程(绿色和蓝色线条):通过这个流程我们可以看到,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) }queueWatcher 将Watcher给了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++) { copiesi } } // 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 }) } } })()流程图,黄色线条:未完待续暂时只研究了内存 => UI,UI => 内存 以后再研究。框架的弯弯绕绕但没有一行冗余代码,Vue贼6