這是一個對Vue.js
源碼解析的系列,會持續更新,歡迎關注;話很少說,下面咱們就從怎麼讀Vue.js
源碼開始。html
首先咱們先看看Vue.js
源碼的項目結構:Vue.js源碼GitHubvue
咱們先了解一下src
這個目錄的各模塊分工:node
src
├── compiler # 編譯相關
├── core # 核心代碼
├── platforms # 不一樣平臺的支持
├── server # 服務端渲染
├── sfc # .vue 文件解析
├── shared # 共享代碼
複製代碼
compiler
模塊包含Vue.js
了全部編譯相關的代碼。它包括把模板解析成AST
語法樹,AST
語法樹優化,代碼生成等功能。webpack
core
目錄包含了Vue.js
的核心代碼,包括內置組件、全局 API 封裝,Vue 實例化、觀察者、虛擬 DOM、工具函數等。git
Vue.js
是一個跨平臺的MVVM
框架,它能夠跑在 web
上,也能夠配合weex
跑在native
客戶端上。platform
是Vue.js
的入口,會分別打包成運行在 web
上和weex
上的Vue.js
。github
這是與服務端渲染相關的部分,這部分代碼是跑在服務端的Node.js
。web
一般咱們開發Vue.js
都會藉助webpack
構建,而後經過.vue
單文件來編寫組件,這個模塊的功能就是將.vue
文件內容解析成一個 JavaScript
的對象。npm
這裏是Vue.js
定義的一些共享工具方法,會供以上模塊所共享。json
大概瞭解了以上模塊功能後,咱們就知道了對於web
端的源碼,咱們主要分析的就是core
模塊。weex
想一想日常咱們使用vue
的時候是經過npm
來安裝使用的,那說明Vue.js
其實就是一個 node
包,但它是基於Rollup
構建的,但咱們也能夠用webpack
的一些打包思路去理解它,若是對webpack
和node
包還不太瞭解的同窗能夠看看我以前寫的webpack4.x最詳細入門講解和不會發布node包?進來看看
簡單理解的話就是Vue.js
經過構建工具將其打包,這個包會導出一個Vue
構造函數供咱們使用。
因此咱們從Vue.js
源碼中的package.json
文件入手,由於其包含了打包的一些配置記錄,主要了解兩個地方:
"module": "dist/vue.runtime.esm.js"
複製代碼
這個配置能夠理解爲出口或者入口,理解爲出口時就是指它會導出一個Vue
構造函數供咱們使用;理解爲入口的話就從這個vue.runtime.esm.js
開始集成Vue.js
作須要的代碼。
"scripts": {"build": "node scripts/build.js"}
複製代碼
能夠理解爲打包入口,會經過build.js
找到Vue.js
所須要的依賴代碼,而後對其進行打包,全部咱們能夠從這裏入手,去scripts/build.js
路徑下的build.js
文件中看看:
// scripts/build.js
let builds = require('./config').getAllBuilds()
...
build(builds)
複製代碼
// scripts/config.js
const builds = {
// Runtime only (CommonJS). Used by bundlers e.g. Webpack & Browserify
'web-runtime-cjs': {
entry: resolve('web/entry-runtime.js'),
dest: resolve('dist/vue.runtime.common.js'),
format: 'cjs',
banner
},
// Runtime+compiler CommonJS build (CommonJS)
'web-full-cjs': {
entry: resolve('web/entry-runtime-with-compiler.js'),
dest: resolve('dist/vue.common.js'),
format: 'cjs',
alias: { he: './entity-decoder' },
banner
},
// Runtime only (ES Modules). Used by bundlers that support ES Modules,
// e.g. Rollup & Webpack 2
'web-runtime-esm': {
entry: resolve('web/entry-runtime.js'),
dest: resolve('dist/vue.runtime.esm.js'),
format: 'es',
banner
},
// Runtime+compiler CommonJS build (ES Modules)
'web-full-esm': {
entry: resolve('web/entry-runtime-with-compiler.js'),
dest: resolve('dist/vue.esm.js'),
format: 'es',
alias: { he: './entity-decoder' },
banner
},
// runtime-only build (Browser)
'web-runtime-dev': {
entry: resolve('web/entry-runtime.js'),
dest: resolve('dist/vue.runtime.js'),
format: 'umd',
env: 'development',
banner
},
// ...
}
exports.getAllBuilds = () => Object.keys(builds).map(genConfig)
複製代碼
以上邏輯其實就是從配置文件config.js
中讀取配置,再對構建配置作過濾,進而根據不一樣配置構建出不一樣用途的Vue.js
,其中就有web
使用的兩個版本:Runtime Only
和 Runtime + Compiler
:
Runtime Only
在使用Runtime Only
版本的Vue.js
的時候,一般須要藉助如webpack
的vue-loader
工具把.vue
文件編譯成JavaScript
,由於是在編譯階段作的,因此它只包含運行時的Vue.js
代碼,所以代碼體積也會更輕量。
Runtime + Compiler
若是沒有對代碼作預編譯,但又使用了Vue
的template
屬性並傳入一個字符串,則須要在客戶端編譯模板,因此須要帶有編譯器的版本,即Runtime + Compiler
。
由於後續系列咱們會將到編譯模塊,全部咱們就從帶編譯器的版本入手,即以上入口是entry: resolve('web/entry-runtime-with-compiler.js'),
的文件,根據源碼的路徑解析咱們獲得最終的文件路徑是src/platforms/web/entry-runtime-with-compiler.js
。
// src/platforms/web/entry-runtime-with-compiler.js
import Vue from './runtime/index'
// ...
export default Vue
複製代碼
能夠看到這個文件不是定義Vue
構造函數的地方,也是從其餘文件引入,而後再加工導出,那咱們從./runtime/index
這個文件繼續找:
// src/platforms/web/runtime/index
import Vue from 'core/index'
// ...
export default Vue
複製代碼
依舊如此……,繼續往上找,最終通過幾回查找,在src/core/instance/index.js
中能夠看到Vue
的真身:
// src/core/instance/index.js
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) {
// 判斷是不是開發環境且必須是new調用
if (process.env.NODE_ENV !== 'production' &&
!(this instanceof Vue)
) {
warn('Vue is a constructor and should be called with the `new` keyword')
}
// new一個實例時會調用_init方法,該方法在下面的initMixin(Vue)中有定義
this._init(options)
}
initMixin(Vue)
stateMixin(Vue)
eventsMixin(Vue)
lifecycleMixin(Vue)
renderMixin(Vue)
export default Vue
複製代碼
因此到此咱們終於看到了Vue
的廬山真面目,能夠看到Vue
是一個構造函數,且通過了一系列的Mixin
,進而在Vue
的原型上拓展方法。
經過以上梳理,咱們大概瞭解到了咱們平時使用的Vue
是怎麼來的,後續系列會繼續對源碼進行梳理,若是對你的有幫助的話歡迎來波關注!
相關參考:Vue.js源碼全方位深刻解析