vue2.0源碼解讀系列 - 來自vue的神秘禮盒

目前vue3.0打的很火熱搁进,都已經出了很多vue3.0源碼解析系列的博客灼舍, 但是vue2.0的源碼我覺得還是有必要細品一下萧福, 掌握了原有通用的源碼原理崇摄,才能知道新版本的vue3.0到底做了哪些更改擎值。如果已經很熟悉了,可跳過~

首先整體看一下整個頁面渲染的流程圖逐抑, 順著這張圖我們再帶著問題深入研究幅恋, 相信很快就能攻克閱讀Vue源碼的困難。

Vue執(zhí)行機制

初始化及掛載:new Vue() -> $mount

初始化及掛載

從文件夾core/index.js入口泵肄,看到 import Vue from './instance/index'這句話捆交;

接著我們定位到instance/index.js文件,看到
import { initMixin } from './init'

function Vue (options) {
  if (process.env.NODE_ENV !== 'production' &&
    !(this instanceof Vue)
  ) {
    warn('Vue is a constructor and should be called with the `new` keyword')
  }
  this._init(options)
}
initMixin(Vue);  // 這個Vue參數在當前文件定義了

可以看到: 上面Vue構造函數中腐巢,執(zhí)行了this._init(options)
this是指當前的Vue實例品追,是從initMixin()函數中定義的。

我們定位到:instance/init.js可以看到:

export function initMixin (Vue: Class<Component>) {
  Vue.prototype._init = function (options?: Object) {
    const vm: Component = this
    ... // 省略中間的處理
    vm._self = vm
    initLifecycle(vm) // 初始化生命周期
    initEvents(vm) // 初始化事件
    initRender(vm) // 初始化render
    callHook(vm, 'beforeCreate')
    initInjections(vm) // resolve injections before data/props
    initState(vm) // 初始化props冯丙、 methods肉瓦、 data、 computed 與 watch 等
    initProvide(vm) // resolve provide after data/props
    callHook(vm, 'created')
    ... // 省略一部分處理
    if (vm.$options.el) {
      vm.$mount(vm.$options.el) // 初始化之后調用 $mount 掛載組件
    }
  }
}
  • 在 new Vue() 之后胃惜。 Vue 會調用 _init 函數進行初始化泞莉,也就是這里的 init 過程,它會:
    • 初始化生命周期:initLifecycle(vm)
    • 事件:initEvents(vm)
    • props船殉、 methods鲫趁、 data、 computed 與 watch 等選項: initState(vm)
export function initState (vm: Component) {
  vm._watchers = []
  const opts = vm.$options
  if (opts.props) initProps(vm, opts.props) // props
  if (opts.methods) initMethods(vm, opts.methods) // methods
  if (opts.data) {
    initData(vm) // data
  } else {
    observe(vm._data = {}, true)
  }
  if (opts.computed) initComputed(vm, opts.computed) // computed
  if (opts.watch && opts.watch !== nativeWatch) {
    initWatch(vm, opts.watch) // watch
  }
}
  • 其中最重要的是通過 Object.defineProperty 設置 setter 與 getter 函數利虫,用來實現「響應式」以及「依賴收集」挨厚,后面會詳細講到,這里只要有一個印象即可糠惫;
(Q1: Vue的響應式以及依賴收集是如何實現的疫剃?)
  • 初始化之后調用 $mount 會掛載組件。

  • 如果是運行時編譯硼讽,即不存在 render function 但是采用 template進行渲染 的情況巢价,需要進行「編譯」步驟。

(Q2: Vue的模板編譯過程固阁?)

編譯

compile編譯可以分成 parse壤躲、optimize 與 generate 三個階段,最終需要得到 render function您炉。


編譯

查看compiler/index.js文件:

import { parse } from './parser/index'
import { optimize } from './optimizer'
import { generate } from './codegen/index'
import { createCompilerCreator } from './create-compiler'

// `createCompilerCreator` allows creating compilers that use alternative
// parser/optimizer/codegen, e.g the SSR optimizing compiler.
// Here we just export a default compiler using the default parts.
export const createCompiler = createCompilerCreator(function baseCompile (
  template: string,
  options: CompilerOptions
): CompiledResult {
  const ast = parse(template.trim(), options)
  if (options.optimize !== false) {
    optimize(ast, options)
  }
  const code = generate(ast, options)
  return {
    ast,
    render: code.render,
    staticRenderFns: code.staticRenderFns
  }
})

可以看到柒爵,先創(chuàng)建了一個編譯器, 創(chuàng)建成功后:

  • 調用parse函數生成AST抽象語法樹
  • 如果選項中的optimize為true赚爵, 則需要進行優(yōu)化棉胀, 調用optimize函數
  • 接著, 根據生成的AST,通過調用generate函數生成代碼段對象
  • 最后將AST冀膝、code對象中的render字符串(VNode渲染所需要的)
    唁奢、code中的staticRenderFns字符串包裹成一個對象返回。
parse

parse 會用正則等方式解析 template 模板中的指令窝剖、class麻掸、style等數據,形成AST赐纱。(如何解析脊奋?)

optimize

optimize 的主要作用是標記 static 靜態(tài)節(jié)點熬北,這是 Vue 在編譯過程中的一處優(yōu)化,后面當 update 更新界面時诚隙,會有一個 patch的過程讶隐,diff 算法會直接跳過靜態(tài)節(jié)點,從而減少了比較的過程久又,優(yōu)化了 patch 的性能巫延。

(Q3: Vue是如何區(qū)分靜態(tài)節(jié)點的?Vue的patch過程地消?diff算法做了哪些事情炉峰?)
generate

generate 是將 AST 轉化成 render function 字符串的過程(如何轉換?)脉执,得到結果是 render 的字符串以及 staticRenderFns 字符串疼阔。

在經歷過 parse、optimize 與 generate 這三個階段以后适瓦,組件中就會存在渲染 VNode 所需的 render function 了竿开。

(Q4: VNode是什么?)

響應式

響應式

當render function被渲染的時候玻熙,會讀取對象中的值否彩, 從而觸發(fā)getter函數進行依賴收集。依賴收集的目的是將觀察者Watcher對象放到訂閱者Dep中的subs中嗦随。

image.png

當修改對象中的值時列荔,會觸發(fā)setter函數通知之前收集的Dep中的每一個Watcher重新渲染視圖,Watcher收到通知后枚尼, 調用update函數來更新視圖贴浙。當然這中間還有一個 patch 的過程以及使用隊列來異步更新的策略,這個我們后面再講署恍。

響應式過程
(Q4: Vue2.0的響應式原理崎溃?)

Virtual DOM

虛擬DOM其實是render function執(zhí)行后的產物,是一棵以 JavaScript 對象( VNode 節(jié)點)作為基礎的樹盯质,用對象屬性來描述節(jié)點袁串,實際上它只是一層對真實 DOM 的抽象。最終可以通過一系列操作使這棵樹映射到真實環(huán)境上呼巷。由于 Virtual DOM 是以 JavaScript 對象為基礎而不依賴真實平臺環(huán)境囱修,所以使它具有了跨平臺的能力,比如說瀏覽器平臺王悍、Weex破镰、Node 等。

更新視圖

  • 前面說到:在修改一個對象值的時候,會通過 setter -> Watcher -> update 的流程來修改對象對應的值鲜漩。

  • 當數據變化后源譬,執(zhí)行 render function 就可以得到一個新的 VNode 節(jié)點,我們如果想要得到新的視圖孕似,最簡單粗暴的方法就是直接解析這個新的 VNode 節(jié)點瓶佳,然后用 innerHTML 直接全部渲染到真實 DOM 中。但是其實我們只對其中的一小塊內容進行了修改鳞青,這樣做似乎有些浪費。

  • 因此我們可以只修改有修改的部分为朋,這個時候就會通過patch去比較了臂拓。將新的 VNode 與舊的 VNode 一起傳入 patch 進行比較,經過 diff 算法得出它們的「差異」习寸。最后我們只需要將這些「差異」的對應 DOM 進行修改即可胶惰。

總結

回過頭來,我們再來看第一張圖


Vue執(zhí)行機制

對于vue整體上的執(zhí)行機制是否有了一些概念霞溪?

  • 頁面渲染:new Vue() -> init() -> $mount()
  • 數據更新:用戶操作導致數據需要更新孵滞,視圖需要更新 -> getter收集依賴 -> dep.depend() -> dep.subs.push(watcher) -> setter通知watcher更新視圖 -> dep.notify() -> dep.subs[i].update() -> render fucntion() -> VNode -> patch -> DOM
  • 模板更新: 模板編譯:parse -> optimize -> generator

具體的有些機制細節(jié),本系列會一一更新鸯匹, 共同學習坊饶, 共同進步!
具體有些細節(jié)殴蓬, 可能筆者在理解上存在誤解匿级,如果有問題,歡迎在評論或者留言區(qū)進行反饋交流~

參考資料

  • 染陌掘金小冊《剖析Vue.js內部運行機制》
  • vue2.0源碼
?著作權歸作者所有,轉載或內容合作請聯(lián)系作者
  • 序言:七十年代末染厅,一起剝皮案震驚了整個濱河市痘绎,隨后出現的幾起案子,更是在濱河造成了極大的恐慌肖粮,老刑警劉巖,帶你破解...
    沈念sama閱讀 216,402評論 6 499
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現場離奇詭異署惯,居然都是意外死亡烫沙,警方通過查閱死者的電腦和手機,發(fā)現死者居然都...
    沈念sama閱讀 92,377評論 3 392
  • 文/潘曉璐 我一進店門凌净,熙熙樓的掌柜王于貴愁眉苦臉地迎上來悲龟,“玉大人,你說我怎么就攤上這事冰寻⌒虢蹋” “怎么了?”我有些...
    開封第一講書人閱讀 162,483評論 0 353
  • 文/不壞的土叔 我叫張陵,是天一觀的道長轻腺。 經常有香客問我乐疆,道長,這世上最難降的妖魔是什么贬养? 我笑而不...
    開封第一講書人閱讀 58,165評論 1 292
  • 正文 為了忘掉前任挤土,我火速辦了婚禮,結果婚禮上误算,老公的妹妹穿的比我還像新娘仰美。我一直安慰自己,他們只是感情好儿礼,可當我...
    茶點故事閱讀 67,176評論 6 388
  • 文/花漫 我一把揭開白布咖杂。 她就那樣靜靜地躺著,像睡著了一般蚊夫。 火紅的嫁衣襯著肌膚如雪诉字。 梳的紋絲不亂的頭發(fā)上,一...
    開封第一講書人閱讀 51,146評論 1 297
  • 那天知纷,我揣著相機與錄音壤圃,去河邊找鬼。 笑死琅轧,一個胖子當著我的面吹牛伍绳,可吹牛的內容都是我干的。 我是一名探鬼主播鹰晨,決...
    沈念sama閱讀 40,032評論 3 417
  • 文/蒼蘭香墨 我猛地睜開眼墨叛,長吁一口氣:“原來是場噩夢啊……” “哼!你這毒婦竟也來了模蜡?” 一聲冷哼從身側響起漠趁,我...
    開封第一講書人閱讀 38,896評論 0 274
  • 序言:老撾萬榮一對情侶失蹤,失蹤者是張志新(化名)和其女友劉穎忍疾,沒想到半個月后闯传,有當地人在樹林里發(fā)現了一具尸體,經...
    沈念sama閱讀 45,311評論 1 310
  • 正文 獨居荒郊野嶺守林人離奇死亡卤妒,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內容為張勛視角 年9月15日...
    茶點故事閱讀 37,536評論 2 332
  • 正文 我和宋清朗相戀三年甥绿,在試婚紗的時候發(fā)現自己被綠了。 大學時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片则披。...
    茶點故事閱讀 39,696評論 1 348
  • 序言:一個原本活蹦亂跳的男人離奇死亡共缕,死狀恐怖,靈堂內的尸體忽然破棺而出士复,到底是詐尸還是另有隱情图谷,我是刑警寧澤翩活,帶...
    沈念sama閱讀 35,413評論 5 343
  • 正文 年R本政府宣布,位于F島的核電站便贵,受9級特大地震影響菠镇,放射性物質發(fā)生泄漏。R本人自食惡果不足惜承璃,卻給世界環(huán)境...
    茶點故事閱讀 41,008評論 3 325
  • 文/蒙蒙 一利耍、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧盔粹,春花似錦隘梨、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 31,659評論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至咬崔,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間烦秩,已是汗流浹背垮斯。 一陣腳步聲響...
    開封第一講書人閱讀 32,815評論 1 269
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留只祠,地道東北人兜蠕。 一個月前我還...
    沈念sama閱讀 47,698評論 2 368
  • 正文 我出身青樓,卻偏偏與公主長得像抛寝,于是被迫代替她去往敵國和親熊杨。 傳聞我的和親對象是個殘疾皇子,可洞房花燭夜當晚...
    茶點故事閱讀 44,592評論 2 353

推薦閱讀更多精彩內容