vue源碼學習(一)——從入口開始

學習vue源碼時,咱們首先須要看的是package.json文件,該文件裏配置了vue的依賴以及開發環境和生產環境的編譯的啓動腳本等其餘信息。首先咱們須要關注的是script。咱們這裏先看第一個dev腳本:vue

"dev": "rollup -w -c scripts/config.js --environment TARGET:web-full-dev"

咱們能夠看到vue是採用了rollup編譯的腳本,而後對應的查看其配置文件config.jsnode

const builds = {
...
// Runtime+compiler development build (Browser)
  'web-full-dev': {
    entry: resolve('web/entry-runtime-with-compiler.js'),
    dest: resolve('dist/vue.js'),
    format: 'umd',
    env: 'development',
    alias: { he: './entity-decoder' },
    banner
  },
...
}

在該文件中咱們能夠找到web-full-dev的配置項,而且知道其編譯的文件爲'web/entry-runtime-with-compiler.js'
那麼咱們就須要去找到該文件了。
咱們能夠發現entry-runtime-with-compiler.js的其中有一行代碼是:web

import Vue from './runtime/index'

而後繼續跟着代碼往上找,咱們會發現仍是嵌套了好幾層,最後在'/instance/index'中找到咱們vue的定義:最終其路勁以下:json

/src/platforms/web/web-runtime-with-compiler.js   
=> /src/platforms/web/runtime/index.js 
=> /src/core/index.js 
=> /src/core/instance/index.js

最終咱們在instance/index.js上找到了vue的廬山真面目,他的構造函數及其簡單:瀏覽器

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)
}

咱們能夠發現它並無使用class的,只是一個普通的構造函數,經過 !(this instanceof Vue) 來強制使用new來構建。
之因此不採用class,我的理解是爲了更好的把代碼拆分。原型上的方法只須要經過prototype來添加。以下weex

initMixin(Vue) // 這裏主要註冊了_init
stateMixin(Vue) // $set,$delete,$watch
eventsMixin(Vue) // $on, $once, $off, $emit
lifecycleMixin(Vue) //  _update, $forceUpdate,$destroy
renderMixin(Vue) // $nextTick, _render

這邊都是基於在Vue上擴展方法,這樣就把代碼分離開發,方便維護。不須要所有寫到Vue函數內部。
而後咱們往回走,咱們能夠看到/src/core/index.js 中dom

initGlobalAPI(Vue)
Object.defineProperty(Vue.prototype, '$isServer', {
  get: isServerRendering
})
Object.defineProperty(Vue.prototype, '$ssrContext', {
  get () {
    /* istanbul ignore next */
    return this.$vnode && this.$vnode.ssrContext
  }
})

Object.defineProperty(Vue, 'FunctionalRenderContext', {
  value: FunctionalRenderContext
})

Vue.version = '__VERSION__'

該文件中主要就是註冊全局API,以供咱們內部或外部使用
再往上/src/platforms/web/runtime/index.js,咱們能夠看到函數

// install platform specific utils
Vue.config.mustUseProp = mustUseProp
Vue.config.isReservedTag = isReservedTag
Vue.config.isReservedAttr = isReservedAttr
Vue.config.getTagNamespace = getTagNamespace
Vue.config.isUnknownElement = isUnknownElement

// install platform runtime directives & components
extend(Vue.options.directives, platformDirectives)
extend(Vue.options.components, platformComponents)

// install platform patch function 
Vue.prototype.__patch__ = inBrowser ? patch : noop // 用於把vNode顯示到dom上的方法,這邊要區分是瀏覽器環境仍是weex環境

// public mount method
Vue.prototype.$mount = function () {
.....
} // 把dom掛在到頁面上

這邊就是註冊一些全局的工具,以及patch方法
那麼再往上:src/platforms/web/web-runtime-with-compiler.js, 這個文件中主要就是重寫$mount工具

const mount = Vue.prototype.$mount
Vue.prototype.$mount = function (
){
...
}

那麼爲何要重寫呢?原來在runtime裏面的$mount方法是沒有編譯功能的,而最後一個重寫就是增長了編譯。
在vue腳手架咱們會讓咱們選擇哪一個版本:以下圖oop

clipboard.png

這就是2個版本的區別,是否包含編譯功能。一個是完整版,一個是運行時。
vue官方屬於解釋了:

完整版:同時包含編譯器和運行時的版本。
編譯器:用來將模板字符串編譯成爲 JavaScript 渲染函數的代碼。
運行時:用來建立 Vue 實例、渲染並處理虛擬 DOM 等的代碼。基本上就是除去編譯器的其它一切。

若是你須要在客戶端編譯模板 (好比傳入一個字符串給 template 選項,或掛載到一個元素上並以其 DOM 內部的 HTML 做爲模板),就將須要加上編譯器,即完整版:
// 須要編譯器
new Vue({
  template: '<div>{{ hi }}</div>'
})

// 不須要編譯器
new Vue({
  render (h) {
    return h('div', this.hi)
  }
})

重寫$mount方法就是對template就好了編譯轉換爲render方法
好了這邊就簡要的介紹了vue的入口。

您的點贊是我繼續努力的動力!

相關文章
相關標籤/搜索