引入vue.js 后,是一个立即执行函数,将在全局对象下挂载一个名为 Vue$3 的构造函数
(function (global, factory) {
global.Vue = factory();
}(this, (function () {
function Vue$3 (options) {
this._init(options);
}
...
// 扩展Vue$3的属性和方法
initMixin(Vue$3);
stateMixin(Vue$3);
eventsMixin(Vue$3);
lifecycleMixin(Vue$3);
renderMixin(Vue$3);
...
initGlobalAPI(Vue$3);
return Vue$3;
})));
等价于
window.Vue = Vue$3(){
this._init(options);
}
initData 执行顺序
- window.Vue = function Vue$3(options){ };
- initMixin(); // 在 Vue.prototype 上扩展一个 _init 方法
function initMixin (Vue) { Vue.prototype._init = function (options) { var vm = this; vm._uid = uid$1++; vm._isVue = true; // merge options if (options && options._isComponent) { initInternalComponent(vm, options); } else { vm.$options = mergeOptions( resolveConstructorOptions(vm.constructor), options || {}, vm ); } { initProxy(vm); } vm._self = vm; initLifecycle(vm); initEvents(vm); initRender(vm); callHook(vm, 'beforeCreate'); initInjections(vm); // resolve injections before data/props initState(vm); initProvide(vm); // resolve provide after data/props callHook(vm, 'created'); if (vm.$options.el) { vm.$mount(vm.$options.el); } }; }
- new Vue(); // 在构造完 Vue 实例后执行 实例的 _init 方法
- this._init(options);
- initState(); // 初始化相关状态:props,methods,data,computed,watch
function initState (vm) { vm._watchers = []; var opts = vm.$options; if (opts.props) { initProps(vm, opts.props); } if (opts.methods) { initMethods(vm, opts.methods); } if (opts.data) { initData(vm); } else { observe(vm._data = {}, true /* asRootData */); } if (opts.computed) { initComputed(vm, opts.computed); } if (opts.watch && opts.watch !== nativeWatch) { initWatch(vm, opts.watch); } }
- initData();
function initData (vm) { var data = vm.$options.data; data = vm._data = typeof data === 'function' ? getData(data, vm) : data || {}; // proxy data on instance var keys = Object.keys(data); var i = keys.length; while (i--) { var key = keys[i]; proxy(vm, "_data", key); } // observe data observe(data, true /* asRootData */); }
- proxy(vm, "_data", key); // 代理,简化路径
- observe(data, true /* asRootData */);
- var ob = new Observer(value);
var Observer = function Observer (value) { this.value = value; this.dep = new Dep(); this.vmCount = 0; def(value, '__ob__', this); if (Array.isArray(value)) { var augment = hasProto ? protoAugment : copyAugment; augment(value, arrayMethods, arrayKeys); this.observeArray(value); } else { this.walk(value); } };
- this.walk(value); // 构造实例后自动调用 ob.walk()
Observer.prototype.walk = function walk (obj) { var keys = Object.keys(obj); for (var i = 0; i < keys.length; i++) { defineReactive(obj, keys[i], obj[keys[i]]); } };
- defineReactive(obj, keys[i], obj[keys[i]]); // 遍历 data 对象的键值,对每个键值进行响应式处理
- Object.defineProperty(obj, key, { getter, setter }
// 注:源码取自 defineReactive() 中 Object.defineProperty(obj, key, { enumerable: true, configurable: true, get: function reactiveGetter () { var value = getter ? getter.call(obj) : val; if (Dep.target) { // Watch类的实例 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 ("development" !== 'production' && customSetter) { customSetter(); } if (setter) { setter.call(obj, newVal); } else { val = newVal; } childOb = !shallow && observe(newVal); dep.notify(); } });
initWatch 执行顺序
- initState(vm);
- initWatch(vm, opts.watch); // 遍历 watch 中的每个键值执行一次 createWatcher 方法;
function initWatch (vm, watch) { for (var key in watch) { var handler = watch[key]; if (Array.isArray(handler)) { for (var i = 0; i < handler.length; i++) { createWatcher(vm, key, handler[i]); } } else { createWatcher(vm, key, handler); } } }
- createWatcher(vm, key, handler); // 在该函数中执行 Vue.prototype.$watch 方法;
- vm.$watch(keyOrFn, handler, options); 该函数将 new Watcher 实例,返回值为 unwatchFn 函数,用来卸载当前侦探器;
// 源码: Vue.prototype.$watch = function ( expOrFn, cb, options ) { var vm = this; if (isPlainObject(cb)) { return createWatcher(vm, expOrFn, cb, options) } options = options || {}; options.user = true; var watcher = new Watcher(vm, expOrFn, cb, options); if (options.immediate) { cb.call(vm, watcher.value); } return function unwatchFn () { watcher.teardown(); } }; // 示例: var unwatchFn = this.$watch('name', function(){ console.log('doSomeThing'); ... if(unwatchFn ){ unwatchFn(); } }, { immediate: true, });
- var watcher = new Watcher(vm, expOrFn, cb, options);
var Watcher = function Watcher ( vm, expOrFn, cb, options, isRenderWatcher ) { this.vm = vm; ... if (typeof expOrFn === 'function') { this.getter = expOrFn; } else { this.getter = parsePath(expOrFn); } this.value = this.get(); };
- this.getter = parsePath(expOrFn); // parsePath 返回一个匿名函数,即 opts.watch 中键值的 handler 方法;
var bailRE = /[^\w.$]/; // 匹配除了 字母\数字\下划线\$ 以外的任意一个字符 function parsePath (path) { if (bailRE.test(path)) { return } var segments = path.split('.'); // 返回 a.b.c 的值 return function (obj) { for (var i = 0; i < segments.length; i++) { if (!obj) { return } obj = obj[segments[i]]; } return obj } }
- this.value = watcher.get(); // 构造函数执行完后立即执行watch实例的get方法
Watcher.prototype.get = function get () { pushTarget(this); var value; var vm = this.vm; try { value = this.getter.call(vm, vm); } catch (e) { } finally { popTarget(); this.cleanupDeps(); } return value };
- watcher.get() 方法中执行 pushTarget(this),设置了 Dep.target = _target;
-
value = this.getter.call(vm, vm); // getter 就是 parsePath 返回的匿名函数,在此处被调用执行;
-
同时匿名函数中执行 obj = obj[segments[i]];触发 proxy 中的 getter 代理 vm['name'] = vm["_data"]["name"];
在 initData() 时 data 的每个键值执行过 proxy(vm, "_data", key); 在 initProps() 时 props 的每个键值执行过 proxy(vm, "_props", key); var sharedPropertyDefinition = { enumerable: true, configurable: true, get: noop, set: noop }; function proxy (target, sourceKey, key) { sharedPropertyDefinition.get = function proxyGetter () { return this[sourceKey][key] }; sharedPropertyDefinition.set = function proxySetter (val) { this[sourceKey][key] = val; }; Object.defineProperty(target, key, sharedPropertyDefinition); }
-
vm["_data"]["name"] 取值时触发 Object.defineProperty 的 get 访问器描述符 reactiveGetter
/** * Define a reactive property on an Object. */ function defineReactive ( obj, key, val, customSetter, shallow ) { var dep = new Dep(); // 针对Data中的每个属性建立以一个Dep类实例对象 var property = Object.getOwnPropertyDescriptor(obj, key); if (property && property.configurable === false) { return } var getter = property && property.get; var setter = property && property.set; 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) { // Watch类的实例 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; if (newVal === value || (newVal !== newVal && value !== value)) { return } if (setter) { setter.call(obj, newVal); } else { val = newVal; } childOb = !shallow && observe(newVal); dep.notify(); } }); }
-
在 getter 中因为存在 if(Dep.target),步骤8中赋值实现,Dep.target = Watcher 类实例 watcher,执行 dep.depend();
-
watcher.addDep(this); // 将这个 watcher 实例加入到此 data属性的依赖收集 Dep 中
-
popTarget(); 中将 Dep.target = null;
-
watcher.cleanupDeps(); // 清除依赖收集
-
当 data 属性值发生变化时,触发 setter 函数,执行该 data属性上的 dep.notify()。
-
dep.notify(); // 遍历 dep.subs 里的所有 watcher, 执行 watcher.update();
Dep.prototype.notify = function notify () { var subs = this.subs.slice(); for (var i = 0, l = subs.length; i < l; i++) { subs[i].update(); } };
-
watcher.update();
Watcher.prototype.update = function update () { if (this.lazy) { this.dirty = true; } else if (this.sync) { this.run(); } else { queueWatcher(this); } };
-
watcher.run();
Watcher.prototype.run = function run () { var value = this.get(); var oldValue = this.value; this.value = value; this.cb.call(this.vm, value, oldValue); };
$mount 执行顺序
- 在 this._init() 方法中执行 vm.$mount(vm.$options.el);
- vm.$mount(vm.$options.el);
- var mount = Vue$3.prototype.$mount;最早的公共方法 Vue$3.prototype.$mount 被保存在 mount 中,然后重写了 Vue$3.prototype.$mount 方法;
// public mount method Vue$3.prototype.$mount = function ( el, hydrating ) { el = el && inBrowser ? query(el) : undefined; return mountComponent(this, el, hydrating) }; ... var mount = Vue$3.prototype.$mount; Vue$3.prototype.$mount = function ( el, hydrating ) { el = el && query(el); var options = this.$options; if (!options.render) { var template = options.template; if (template) { var ref = compileToFunctions(template, { shouldDecodeNewlines: shouldDecodeNewlines, shouldDecodeNewlinesForHref: shouldDecodeNewlinesForHref, delimiters: options.delimiters, comments: options.comments }, this); var render = ref.render; var staticRenderFns = ref.staticRenderFns; options.render = render; options.staticRenderFns = staticRenderFns; } } return mount.call(this, el, hydrating) };
- 执行 mountComponent();
function mountComponent ( vm, el, hydrating ) { vm.$el = el; callHook(vm, 'beforeMount'); var updateComponent = function () { vm._update(vm._render(), hydrating); }; new Watcher(vm, updateComponent, noop, null, true /* isRenderWatcher */); hydrating = false; if (vm.$vnode == null) { vm._isMounted = true; callHook(vm, 'mounted'); } return vm }
- vm._update(vm._render(), hydrating);
- vm._render();
Vue.prototype._render = function () { var vm = this; var ref = vm.$options; var render = ref.render; var _parentVnode = ref._parentVnode; if (vm._isMounted) { for (var key in vm.$slots) { var slot = vm.$slots[key]; if (slot._rendered || (slot[0] && slot[0].elm)) { vm.$slots[key] = cloneVNodes(slot, true /* deep */); } } } vm.$scopedSlots = (_parentVnode && _parentVnode.data.scopedSlots) || emptyObject; vnode = render.call(vm._renderProxy, vm.$createElement); vnode.parent = _parentVnode; return vnode };
- vm._update();
Vue.prototype._update = function (vnode, hydrating) { var vm = this; if (vm._isMounted) { callHook(vm, 'beforeUpdate'); } vm.$el = vm.__patch__( vm.$el, vnode, hydrating, false, vm.$options._parentElm, vm.$options._refElm ); };
- vm.__patch__()
function createPatchFunction (backend) { ... retrun function patch (oldVnode, vnode, hydrating, removeOnly, parentElm, refElm) { ... createElm( vnode, insertedVnodeQueue, oldElm._leaveCb ? null : parentElm$1, nodeOps.nextSibling(oldElm) ); invokeInsertHook(vnode, insertedVnodeQueue, isInitialPatch); return vnode.elm } } var patch = createPatchFunction({ nodeOps: nodeOps, modules: modules }); Vue$3.prototype.__patch__ = inBrowser ? patch : noop;
- createElm();
function createElm (vnode, insertedVnodeQueue, parentElm, refElm, nested) { vnode.elm = vnode.ns ? nodeOps.createElementNS(vnode.ns, tag) : nodeOps.createElement(tag, vnode); setScope(vnode); createChildren(vnode, children, insertedVnodeQueue); ... insert(parentElm, vnode.elm, refElm); }
- insert(parentElm, vnode.elm, refElm);
function insert (parent, elm, ref$$1) { if (isDef(parent)) { if (isDef(ref$$1)) { if (ref$$1.parentNode === parent) { nodeOps.insertBefore(parent, elm, ref$$1); } } else { nodeOps.appendChild(parent, elm); } } } var nodeOps = Object.freeze({ createElement: createElement$1, createElementNS: createElementNS, createTextNode: createTextNode, createComment: createComment, insertBefore: insertBefore, removeChild: removeChild, appendChild: appendChild, parentNode: parentNode, nextSibling: nextSibling, tagName: tagName, setTextContent: setTextContent, setAttribute: setAttribute });
-
nodeOps.insertBefore(parent, elm, ref$$1),执行该方法后最终将 template 内容完整插入到 el:"#app" 元素中;