vue源碼分析(1)- new Vue
Vue.js 一個核心思想是數據驅動。所謂數據驅動,是指視圖是由數據驅動生成的,我們對視圖的修改,不會直接操作 DOM,而是通過修改數據。它相比我們傳統的前端開發(fā),如使用 jQuery 等前端庫直接修改 DOM,大大簡化了代碼量。特別是當交互復雜的時候,只關心數據的修改會讓代碼的邏輯變的非常清晰,因為 DOM 變成了數據的映射,我們所有的邏輯都是對數據的修改,而不用碰觸 DOM,這樣的代碼非常利于維護。
從開始用vue 1.0做項目到現在的vue2.0,使用已經有2年了,一直在忙于做項目,沒有抽時間來對vue源碼進行一個徹底的理解以及梳理,最近下決心要把vue源碼梳理一遍,會有一系列的文章放出,有興趣的小伙伴可以關注下,歡迎騷擾。。。
開始進入今天的正題,我們從最開始的一個new Vue來講起:

那么vue在new Vue發(fā)生了什么事情呢?
我們首先看一個vue源碼的結構:

首先我們可以看到在vue的src/core/instance/index.js
在講源碼之前,我們首先鋪墊一些javascript的基礎知識:構造函數:用來初始化新創(chuàng)建的對象的函數 例如:function Foo(){}
實例對象: 通過構造函數的new操作創(chuàng)建的對象是實例對象,可以一個構造函數,構造多個實例對象 例如:var f1 = new Foo
原型對象: 實例對象的原型對象 例如:Foo.prototype對于原型鏈的知識我們在這里并不過多闡述,只介紹三條簡單的原型鏈之間的關系:
1 原型對象(Foo.prototype)的constructor 屬性 ====> 原型對象對應的構造函數(Foo)
即:Foo.prototype.constructor === Foo
2 實例對象( f1 )的constructor屬性 ====>原型對象對應的構造函數
即: f1.constructor === Foo3 實例對象(f1)的_proto_屬性指向原型對象(Foo.prototype)
即:f1.__proto__ === Foo.prototype
好的,有了這些基礎知識,我們可以開始我們vue源碼的相關知識

我們看到Vue 是通過new關鍵字進行實例化的,然后再這個函數里面調用了this._init方法,那么這個方法有什么作用呢,這個方法在src/core/instance/init.js里面定義:

我們可以看到整個init方法實際上主要分為四個部分,我們一一解釋下部分作用
第一部分主要是做一些參數的初始化,例如uid啊等
第二部分主要是合并option,合并了options配置,這樣就可以通過vm.$options.el等訪問到創(chuàng)建實例時傳入的option第三部分主要是一堆初始化的函數,初始化生命周期,初始化事件中心,初始化渲染,初始化 data、props、computed、watcher 等等
判斷vm實例是否存在vm.$options.el,存在的話就將vm掛載到這個dom節(jié)點上,存在會執(zhí)行$mount方法,在執(zhí)行$mount之前頁面并沒有改變,完成渲染,經過這一步,頁面上就會從:{{message}} 變?yōu)?Hello Vue。
那有的人可能疑惑,我看完了init還是不理解,data是如何渲染到頁面上的,通過哪個方法渲染的,沒錯,你的疑惑是對的,截下來我們說一下,我們定義了data后我們可以通過message,為什么我們可以訪問到message呢?
在2部分有個方法是有個initState方法,這個方法在src/core/instance/state.js里面:

我們可以看到在這個函數中首先它會判斷是否有props,有的話執(zhí)行initProps,同樣data和methods一樣的邏輯,我們重點來看下initData這個函數:

它從vm.$options.data里面拿到data也就是我們在new Vue里面定義的對象message,拿到后判斷它是否是個function,一般我們推薦data是個函數,return出來一個對象,判斷的結果會執(zhí)行getData函數:

getData函數返回的data.call(vm, vm) 這個對象,然后就可以得出data = vm._data
然后接下來的會執(zhí)行一個data一級method的判斷,如下圖所示:

會判斷從vm.$option的props以及methods判斷兩者是否相等,因為有時候可能在props以及method會有同名,最終這兩者都會掛載上vm上,所以如果重名的話會沖突。
那么如何掛載到vm上,掛載的實現是依靠proxy實現的:
(關于proxy不懂同學可以查看阮一峰來時的es6講解)

proxy方法內部的實現實際上就是定義了一個get和set,這里面最終使用Object.defineProperty就是把傳入的target即vm的key代理了下,通過劫持vm實例中屬性的getter和setter來實現對data的代理。
看到這里我們理解了,當我們調用this.message時候,實際上我們調用了this._data.message,因為它實際使用了proxy做了代理proxy(vm, `data`, key)會將_data作為sourceKey傳入,這就是我們可以使用this.message可以訪問到this._data.message。
在初始化的最后,檢測到如果有el屬性,則調用vm.$mount方法掛載vm,掛載的目標就是把模板渲染成最終的 DOM。下章,我們會梳理分析 Vue 的掛載過程。
我的微信號13520229510 歡迎交流