Vue源码梳理

Vue版本 Vue.version = 2.6.10

Vue静态方法、静态属性

静态方法(类方法) Vue.use Vue.mixin Vue.extend Vue.set Vue.delete Vue.nextTick Vue.observable Vue.version Vue.compile Vue.component Vue.directive Vue.filter

静态属性(类属性)Vue.cid Vue.config Vue.options Vue.util

实例属性实例方法 $nextTick _render $on $once $off $emit _update $forceUpdate $destroy $data $props $set $delete $watch _init $isServer $ssrContext

问题一 双向绑定原理Vue.observable

<script> function defineReactive$$1 ( obj, key, val, customSetter, shallow ) { var dep = new Dep(); var property = Object.getOwnPropertyDescriptor(obj, key); if (property && property.configurable === false) { return } // cater for pre-defined getter/setters var getter = property && property.get; var setter = property && property.set; if ((!getter || setter) && arguments.length === 2) { val = obj[key]; } var childOb = !shallow && observe(val); Object.defineProperty(obj, key, { enumerable: true, configurable: true, get: function reactiveGetter () { var value = getter ? getter.call(obj) : val; if (Dep.target) { /* 收集信息 */ dep.depend(); if (childOb) { childOb.dep.depend(); if (Array.isArray(value)) { dependArray(value); } } } return value }, set: function reactiveSetter (newVal) { var value = getter ? getter.call(obj) : val; /* eslint-disable no-self-compare */ if (newVal === value || (newVal !== newVal && value !== value)) { return } /* eslint-enable no-self-compare */ if (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); dep.notify(); } }); } </script>

对象的key的setter、getter被代理 val 缓存函数

问题二 $watch和computed原理

var uid$2 = 0; var Watcher = function Watcher ( vm, expOrFn, cb, options, isRenderWatcher ) { this.vm = vm; if (isRenderWatcher) { vm._watcher = this; } vm._watchers.push(this); // options if (options) { this.deep = !!options.deep; this.user = !!options.user; this.lazy = !!options.lazy; this.sync = !!options.sync; this.before = options.before; } else { this.deep = this.user = this.lazy = this.sync = false; } this.cb = cb; this.id = ++uid$2; // uid for batching this.active = true; this.dirty = this.lazy; // for lazy watchers this.deps = []; this.newDeps = []; this.depIds = new _Set(); this.newDepIds = new _Set(); this.expression = expOrFn.toString(); // parse expression for getter if (typeof expOrFn === function) { this.getter = expOrFn; } else { this.getter = parsePath(expOrFn); if (!this.getter) { this.getter = noop; warn( “Failed watching path: \”” + expOrFn + “\” “ + Watcher only accepts simple dot-delimited paths. + For full control, use a function instead., vm ); } } this.value = this.lazy ? undefined : this.get(); }; /** * Evaluate the getter, and re-collect dependencies. */ Watcher.prototype.get = function get () { pushTarget(this); var value; var vm = this.vm; try { value = this.getter.call(vm, vm); } catch (e) { if (this.user) { handleError(e, vm, (“getter for watcher \”” + (this.expression) + “\””)); } else { throw e } } finally { // “touch” every property so they are all tracked as // dependencies for deep watching if (this.deep) { traverse(value); } popTarget(); this.cleanupDeps(); } return value }; /** * Add a dependency to this directive. */ Watcher.prototype.addDep = function addDep (dep) { var id = dep.id; if (!this.newDepIds.has(id)) { this.newDepIds.add(id); this.newDeps.push(dep); if (!this.depIds.has(id)) { dep.addSub(this); } } }; /** * Clean up for dependency collection. */ Watcher.prototype.cleanupDeps = function cleanupDeps () { var i = this.deps.length; while (i) { var dep = this.deps[i]; if (!this.newDepIds.has(dep.id)) { dep.removeSub(this); } } var tmp = this.depIds; this.depIds = this.newDepIds; this.newDepIds = tmp; this.newDepIds.clear(); tmp = this.deps; this.deps = this.newDeps; this.newDeps = tmp; this.newDeps.length = 0; }; /** * Subscriber interface. * Will be called when a dependency changes. */ Watcher.prototype.update = function update () { /* istanbul ignore else */ if (this.lazy) { this.dirty = true; } else if (this.sync) { this.run(); } else { queueWatcher(this); } }; /** * Scheduler job interface. * Will be called by the scheduler. */ Watcher.prototype.run = function run () { if (this.active) { var value = this.get(); if ( value !== this.value || // Deep watchers and watchers on Object/Arrays should fire even // when the value is the same, because the value may // have mutated. isObject(value) || this.deep ) { // set new value var oldValue = this.value; this.value = value; if (this.user) { try { this.cb.call(this.vm, value, oldValue); } catch (e) { handleError(e, this.vm, (“callback for watcher \”” + (this.expression) + “\””)); } } else { this.cb.call(this.vm, value, oldValue); } } } }; /** * Evaluate the value of the watcher. * This only gets called for lazy watchers. */ Watcher.prototype.evaluate = function evaluate () { this.value = this.get(); this.dirty = false; }; /** * Depend on all deps collected by this watcher. */ Watcher.prototype.depend = function depend () { var i = this.deps.length; while (i) { this.deps[i].depend(); } }; /** * Remove self from all dependencies subscriber list. */ Watcher.prototype.teardown = function teardown () { if (this.active) { // remove self from vms watcher list // this is a somewhat expensive operation so we skip it // if the vm is being destroyed. if (!this.vm._isBeingDestroyed) { remove(this.vm._watchers, this); } var i = this.deps.length; while (i) { this.deps[i].removeSub(this); } this.active = false; } };

问题三 Dep Watcher observable之间的关联

$watch关键代码 (通过key取值的函数)

function parsePath (path) { if (bailRE.test(path)) { return } var segments = path.split(.); return function (obj) { for (var i = 0; i < segments.length; i++) { if (!obj) { return } obj = obj[segments[i]]; } return obj } }

computed和$watcher关键代码 (收集observe数据,收集依赖)

function get () { pushTarget(this); var value; var vm = this.vm; try { value = this.getter.call(vm, vm); } catch (e) { if (this.user) { handleError(e, vm, (“getter for watcher \”” + (this.expression) + “\””)); } else { throw e } } finally { // “touch” every property so they are all tracked as // dependencies for deep watching if (this.deep) { traverse(value); } popTarget(); this.cleanupDeps(); } return value }

问题三衍生问题1 Watcher收集依赖 设计出栈入栈(能控制Target可靠且唯一),Watcher.prototype.get

pushTarget popTarget 入栈出栈方法,能控制入栈出栈的可靠性。

解决依赖问题

Dep.target = null; var targetStack = []; /* 入栈 */ function pushTarget (target) { targetStack.push(target); Dep.target = target; } /* 出栈 */ function popTarget () { targetStack.pop(); Dep.target = targetStack[targetStack.length 1]; }

入栈出栈全过程Dep.target

Wathcher的 get执行顺序 pushTarget()–> watcher getter() –> observe getter() –>Dep收集 observe的信息和watcher的信息 –>popTarget

问题三衍生问题2 Dep中间件(代理类)代理收集数据存储结果,调节observable和Watcher的数据交互

问题四抽出这三部分源码并且理解其含义包含 压栈、函数闭包、缓存、对用户数据的二次处理(如 prototype 策略模式、用户数据和默认配置的合并处理)

var property = Object.getOwnPropertyDescriptor(obj, key);

Vue的设计模式 类 工厂 策略

© 版权声明
THE END
喜欢就支持一下吧
点赞5 分享
评论 抢沙发
头像
欢迎您留下宝贵的见解!
提交
头像

昵称

取消
昵称表情代码图片