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

Why?

網上現有的Vue源碼解析文章一搜一大批,可是爲何我還要去作這樣的事情呢?由於以爲紙上得來終覺淺,絕知此事要躬行html

而後平時的項目也主要是Vue,在使用Vue的過程當中,也對其一些約定產生了一些疑問,可能官網上只會建議你這麼作,可是核心實現咱們可能並不知道。好比:vue

  • v-for key 是如何達到「就地複用」策略
  • 數組更新檢測是如何完成的
  • set 爲何就能動態添加根級別的響應式屬性
  • 爲何Vue能夠跨平臺支持weex,以及後來出現的mpvue
  • ...

其次,好久沒有更新內容了,以前對Vue源碼也是有點研究,只不過沒有很體系的記錄,如今抽了點時間,作了一次基礎的總結吧。一方面是由於想要克服本身的惰性,另外一方面也是想從新溫故一遍。node

What?

一共分紅了10個基礎部分,後續還會繼續記錄。咱們能夠先看一下概覽:
git

而後咱們來看一下基礎的目錄:github

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

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

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

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

入口開始,解讀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

相關文章
相關標籤/搜索