Vue實例初始化完成後,啓動加載($mount)模塊數據。vue
(一)Vue$3.protype.$mount node
標紅的函數 compileToFunctions 過於複雜,主要是生AST 樹,返回的 ref 以下:react
render 是瀏覽器虛擬機編譯出來的一個函數。咱們點進入能夠看到以下代碼(本身調整後空格換行後的數據) express
(function(){ with(this){ return _c('div',{ attrs:{"id":"app"}}, [_c('input',{directives:[{name:"model",rawName:"v-model",value:(message),expression:"message"}], attrs:{"type":"text"},domProps:{"value":(message)}, on:{"input":function($event){ if($event.target.composing)return;message=$event.target.value}}}), _v(_s(message)+"\n")]) } })
跳過這個複雜的函數。瀏覽器
這裏做者涉及的很奇妙,由於 mount.call(this, el, hydrating) 中的 mount 定義以下 app
var mount = Vue$3.prototype.$mount;dom
Vue$3.prototype.$mount = function (el, hydrating) { el = el && inBrowser ? query(el) : undefined; return mountComponent(this, el, hydrating) //vm._watcher 賦值 };
後來又重寫了$mount 方法:ide
Vue$3.prototype.$mount = function (el, hydrating) { }函數
(二)mountComponent () 函數oop
組件安裝
1 function mountComponent(vm, el, hydrating) { 2 vm.$el = el; 3 if (!vm.$options.render) { 4 //若是不存咋,則建立一個空的虛擬節點 5 vm.$options.render = createEmptyVNode; 6 } 7 callHook(vm, 'beforeMount'); 8 9 var updateComponent; 10 if ("development" !== 'production' && config.performance && mark) { 11 //此處另外一種 updateComponent = 。。。。 12 } else { 13 updateComponent = function () { 14 vm._update(vm._render(), hydrating); //渲染DOM 15 }; 16 } 17 //noop 空函數, 18 vm._watcher = new Watcher(vm, updateComponent, noop); //生成中間件 _watcher 19 hydrating = false; 20 21 // $vnode不存在,,則手動安裝實例,自啓動 22 // mounted is called for render-created child components in its inserted hook 23 if (vm.$vnode == null) { 24 vm._isMounted = true; 25 callHook(vm, 'mounted'); 26 } 27 return vm //調用 實例加載鉤子函數,返回vue實例 28 }
Watcher是一個十分複雜的對象,是溝通 Observer與 Compile 的橋樑做用。
(3)Watcher對象
一、構造函數
Watcher的構造函數並不複雜,主要是爲當前Watcher 初始化各類屬性,好比depIds,newDeps,getter 等,
最後調用 Watcher.prototype.get(),讓Dep收集此Wather實例。
Watcher構造函數會將 傳入的第二個參數轉換 this.getter 屬性;
因爲 this.lazy=false,會當即進入 Watcher.prototype.get()。
二、Watcher.prototype.get()
繞了一大圈,這個函數其實也就調用了 傳入構造函數的第二個參數。
1 Watcher.prototype.get = function get() { 2 pushTarget(this); 3 var value; 4 var vm = this.vm; 5 try { 6 //初始化時 最終 調用咱們傳入的 updateComponent 7 // vm._update(vm._render(), hydrating) 8 value = this.getter.call(vm, vm); 9 } catch (e) { 10 } finally { 11 if (this.deep) { 12 traverse(value); 13 } 14 popTarget(); 15 this.cleanupDeps(); 16 } 17 return value 18 };
此時 this.getter = vm._update(vm._render(), hydrating); 開始渲染渲染DOM,這裏十分重要。
先 執行 Vue.prototype._render(),代碼以下
這裏 render 即是生成的AST代碼。
接下來會按照 以下順序 觸發各類函數:
代理函數 proxyGetter() ==> reactiveGetter() => 執行 render裏面的函數 _c ;
執行完後,將建立的vnode直接返回。
讓咱們再仔細看看 defineReactive$$1() 函數,爲元素自定義get/set方法。
1 function defineReactive$$1(obj, key, val, customSetter, shallow) { 2 var dep = new Dep();//依賴管理 3 /* 此時obj 是帶有__ob__屬性的對象,key是msg */ 4 var property = Object.getOwnPropertyDescriptor(obj, key);//返回鍵描述信息 5 if (property && property.configurable === false) { 6 //不能夠修改直接返回 7 return 8 } 9 10 var getter = property && property.get; 11 var setter = property && property.set; 12 13 var childOb = !shallow && observe(val); 14 Object.defineProperty(obj, key, { 15 enumerable: true, 16 configurable: true, 17 get: function reactiveGetter() { 18 var value = getter ? getter.call(obj) : val; 19 if (Dep.target) { 20 dep.depend(); 21 if (childOb) { 22 childOb.dep.depend(); 23 if (Array.isArray(value)) { 24 dependArray(value); 25 } 26 } 27 } 28 return value 29 }, 30 set: function reactiveSetter(newVal) { 31 var value = getter ? getter.call(obj) : val; //獲取當前值,是前一個值 32 if (newVal === value || (newVal !== newVal && value !== value)) { 33 //值沒有發生變化,再也不作任何處理 34 return 35 } 36 /* eslint-enable no-self-compare */ 37 if ("development" !== 'production' && customSetter) { 38 customSetter(); 39 } 40 if (setter) { 41 setter.call(obj, newVal);//調用默認setter方法或將新值賦給當前值 42 } else { 43 val = newVal; 44 } 45 childOb = !shallow && observe(newVal); 46 dep.notify();//賦值後通知依賴變化 47 } 48 }); 49 }
說的不太清楚,下篇文章繼續說。