入口開始,解讀Vue源碼(一)-- 造物創世


Why?

網上現有的Vue源碼解析文章一搜一大批,但是為什么我還要去做這樣的事情呢?因為覺得紙上得來終覺淺,絕知此事要躬行

然后平時的項目也主要是Vue,在使用Vue的過程中,也對其一些約定產生了一些疑問,可能官網上只會建議你這么做,但是核心實現我們可能並不知道。比如:

  • v-for key 是如何達到“就地復用”策略
  • 數組更新檢測是如何完成的
  • set 為什么就能動態添加根級別的響應式屬性
  • 為什么Vue可以跨平台支持weex,以及后來出現的mpvue
  • ...

其次,很久沒有更新內容了,之前對Vue源碼也是有點研究,只不過沒有很體系的記錄,現在抽了點時間,做了一次基礎的總結吧。一方面是因為想要克服自己的惰性,另一方面也是想重新溫故一遍。

What?

一共分成了10個基礎部分,后續還會繼續記錄。我們可以先看一下概覽:

然后我們來看一下基礎的目錄:

入口開始,解讀Vue源碼(一)———— 造物創世

入口開始,解讀Vue源碼(二)—— new Vue 的故事

入口開始,解讀Vue源碼(三)—— initMixin 上篇

入口開始,解讀Vue源碼(三)—— initMixin 下篇

入口開始,解讀Vue源碼(四)—— 實現一個基礎的 Vue 雙向綁定

入口開始,解讀Vue源碼(五)—— $mount 內部實現

入口開始,解讀Vue源碼(六)—— $mount 內部實現 --- compile parse函數生成AST

入口開始,解讀Vue源碼(七)—— $mount 內部實現 --- compile optimize標記節點

入口開始,解讀Vue源碼(八)—— $mount 內部實現 --- compile generate 生成render函數

入口開始,解讀Vue源碼(九)—— $mount 內部實現 --- render函數 --> VNode

入口開始,解讀Vue源碼(十)—— $mount 內部實現 --- patch

開篇:入口開始,解讀Vue源碼(一)-- 造物創世

世間萬物的起源來自於盤古的開天辟地,Vue 項目的起源,源於一次Vue的實例化:

new Vue({
  el: ...,
  data: ...,
  ....
})

那么在這次實例化的過程中,究竟發生了哪些行為?讓我們來一探究竟。打開Vue的源碼文件,其核心代碼在src/core目錄下。下面我們從入口文件index.js開始進入:(剛開始看的時候,我們可能不太清楚每個引用方法的具體實現,不過沒關系,我們可以自己根據他的命名來YY一下。)

// src/core/index.js

// 這里應該是我們 Vue 核心方法
import Vue from './instance/index'
// 根據命名,應該可以猜出這里是初始化一些全局API
import { initGlobalAPI } from './global-api/index'
// 根據命名,這里應該是獲取一個Boolean類型的變量,來判斷是不是ssr
import { isServerRendering } from 'core/util/env'
// 這里開始執行初始化全局變量
initGlobalAPI(Vue)
// 為Vue原型定義屬性$isServer
Object.defineProperty(Vue.prototype, '$isServer', {
  get: isServerRendering
})
// 為Vue原型定義屬性$ssrContext
Object.defineProperty(Vue.prototype, '$ssrContext', {
  get () {
    /* istanbul ignore next */
    return this.$vnode && this.$vnode.ssrContext
  }
})

Vue.version = '__VERSION__'

export default Vue

下面我們來一步步驗證我們的猜測,首先找到core/instance/index文件,可以清晰的看到:

import { initMixin } from './init'
import { stateMixin } from './state'
import { renderMixin } from './render'
import { eventsMixin } from './events'
import { lifecycleMixin } from './lifecycle'
import { warn } from '../util/index'

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)
stateMixin(Vue)
eventsMixin(Vue)
lifecycleMixin(Vue)
renderMixin(Vue)

export default Vue

這里簡單粗暴的定義了一個 Vue Class,然后又調用了一系列init、mixin這樣的方法來初始化一些功能,具體的我們后面在分析,不過通過代碼我們可以確認的是:沒錯!這里確實是導出了一個 Vue 功能類。

接下來,我們接着看initGlobalAPI這個東西,其實在Vue官網上,就已經為我們說明了Vue的全局屬性:

關於全局API

那我們來看看,是不是這么回事(內容太多,只貼一下主要的代碼):

// core/global-api/index

...
export function initGlobalAPI (Vue: GlobalAPI) {
  // config
  const configDef = {}
  configDef.get = () => config
  if (process.env.NODE_ENV !== 'production') {
    configDef.set = () => {
      warn(
        'Do not replace the Vue.config object, set individual fields instead.'
      )
    }
  }
  Object.defineProperty(Vue, 'config', configDef)

  // 這些工具方法不視作全局API的一部分,除非你已經意識到某些風險,否則不要去依賴他們
  Vue.util = {
    warn,
    extend,
    mergeOptions,
    defineReactive
  }
  // 這里定義全局屬性
  Vue.set = set
  Vue.delete = del
  Vue.nextTick = nextTick

  Vue.options = Object.create(null)
  ASSET_TYPES.forEach(type => {
    Vue.options[type + 's'] = Object.create(null)
  })
  Vue.options._base = Vue
  extend(Vue.options.components, builtInComponents)

  // 定義全局方法
  initUse(Vue)
  initMixin(Vue)
  initExtend(Vue)
  initAssetRegisters(Vue)
}

  • 【Vue.config】 各種全局配置項

  • 【Vue.util】 各種工具函數,還有一些兼容性的標志位(哇,不用自己判斷瀏覽器了,Vue已經判斷好了)

  • 【Vue.set/delete】 這個你文檔應該見過

  • 【Vue.nextTick】

  • 【Vue.options】 這個options和我們上面用來構造實例的options不一樣。這個是Vue默認提供的資源(組件指令過濾器)。

  • 【Vue.use】 通過initUse方法定義

  • 【Vue.mixin】 通過initMixin方法定義

  • 【Vue.extend】通過initExtend方法定義

接下來便是提供給ssr使用的全局變量$isServer$ssrContext。 關於他們的使用,其實ssr文檔也有說明:Head 管理

到這里,我們的入口文件差不多就了解清楚了,接下來,我們開始去了解一下 Vue class 的具體實現,其中我們會了解到Vue的相關生命周期的知識。

End?

文章前后也是利用碎片時間總結整理而成,有些也是翻閱了很多的資料,也有過引用巨人的段落,文章中有所標注。如果沒有標注,可能是本人忘記了,歡迎提醒。文章中如果有筆誤或者不正確的解釋,也歡迎批評指正,共同進步。

最后:

github地址

部分源碼demo


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM