├── scripts ------------------------------- 構建相關的腳本/配置文件
│ ├── git-hooks ------------------------- 存放git鉤子的目錄
│ ├── alias.js -------------------------- 別名配置
│ ├── config.js ------------------------- 生成rollup配置的文件
│ ├── build.js -------------------------- 對 config.js 中全部的rollup配置進行構建
│ ├── ci.sh ----------------------------- 持續集成運行的腳本
│ ├── release.sh ------------------------ 用於自動發佈新版本的腳本
├── dist ---------------------------------- 構建後文件的輸出目錄
├── examples ------------------------------ 存放一些使用Vue開發的應用案例
├── flow ---------------------------------- 類型聲明,使用開源項目 [Flow](https://flowtype.org/)
├── packages ------------------------------ 存放獨立發佈的包的目錄
├── test ---------------------------------- 包含全部測試文件
├── src ----------------------------------- 源碼
│ ├── compiler -------------------------- 編譯器代碼的存放目錄,將 template 編譯爲 render 函數
│ ├── core ------------------------------ 存放通用的,與平臺無關的代碼
│ │ ├── observer ---------------------- 響應系統,包含數據觀測的核心代碼
│ │ ├── vdom -------------------------- 包含虛擬DOM建立(creation)和打補丁(patching)的代碼
│ │ ├── instance ---------------------- 包含Vue構造函數設計相關的代碼
│ │ ├── global-api -------------------- 包含給Vue構造函數掛載全局方法(靜態方法)或屬性的代碼
│ │ ├── components -------------------- 包含抽象出來的通用組件
│ ├── server ---------------------------- 包含服務端渲染(server-side rendering)的相關代碼
│ ├── platforms ------------------------- 包含平臺特有的相關代碼,不一樣平臺的不一樣構建的入口文件也在這裏
│ │ ├── web --------------------------- web平臺
│ │ │ ├── entry-runtime.js ---------- 運行時構建的入口,不包含模板(template)到render函數的編譯器,因此不支持 `template` 選項,咱們使用vue默認導出的就是這個運行時的版本。你們使用的時候要注意
│ │ │ ├── entry-runtime-with-compiler.js -- 獨立構建版本的入口,它在 entry-runtime 的基礎上添加了模板(template)到render函數的編譯器
│ │ │ ├── entry-compiler.js --------- vue-template-compiler 包的入口文件
│ │ │ ├── entry-server-renderer.js -- vue-server-renderer 包的入口文件
│ │ │ ├── entry-server-basic-renderer.js -- 輸出 packages/vue-server-renderer/basic.js 文件
│ │ ├── weex -------------------------- 混合應用
│ ├── sfc ------------------------------- 包含單文件組件(.vue文件)的解析邏輯,用於vue-template-compiler包
│ ├── shared ---------------------------- 包含整個代碼庫通用的代碼
├── package.json -------------------------- 不解釋
├── yarn.lock ----------------------------- yarn 鎖定文件
├── .editorconfig ------------------------- 針對編輯器的編碼風格配置文件
├── .flowconfig --------------------------- flow 的配置文件
├── .babelrc ------------------------------ babel 配置文件
├── .eslintrc ----------------------------- eslint 配置文件
├── .eslintignore ------------------------- eslint 忽略配置
├── .gitignore ---------------------------- git 忽略配置
複製代碼
完整版: 構建後文件包括編譯器+運行時
編譯器: 負責把模板字符串變異爲JS的Render函數
運行時: 負責建立Vue.js實例, 渲染視圖, 使用虛擬DOM算法從新渲染
UMD: 支持經過script標籤在瀏覽器引入
CJS: 用來支持一些低版本打包工具, 由於它們package.json文件的main字段只包含運行時的CJS版本
ESM: 用來支持現代打包工具, 這些打包工具package.json的module字段只包含運行時候的ESM版本
複製代碼
編譯器: 把template變異爲Render函數。vue
// 用到了template就須要編譯器
new Vue({
template: '<div></div>'
})
// 若是自己就是Render函數不須要編譯器
new Vue({
render (h) {
return h('div', this.hi)
}
})
複製代碼
咱們若是使用vue-loader, 那麼*.vue文件模板會在構建時候預編譯成JS, 因此打包完成的文件實際上不須要編譯器的, 只須要引入運行時版本(體積小)便可。webpack
若是確實須要使用完整版只須要在打包工具中配置一個別名。git
// webpack
resolve: {
alias: {
'vue$': 'vue/dist/vue.esm.js',
}
},
複製代碼
咱們知道Vue有不少打包後的版本web
它們都依賴於都process.env.NODE_ENV環境變量, 根據其值來決定選擇什麼模式。 因此咱們能夠在打包工具中配置這些環境變量。算法
在webpack中配置環境變量npm
var webpack = require('webpack');
module.exports = {
...,
plugins: [
// 配置全局變量的插件
new webpack.DefinePlugin({
'NODE_ENV': JSON.stringify('production')
})
]
};
複製代碼
一步步找到Vue的構造函數入口。json
經過查看package.json文件下的scripts命令。api
"dev": "rollup -w -c scripts/config.js --environment TARGET:web-full-dev"
複製代碼
scripts/config.js爲打開的對應配置文件, process.env.TARGET爲web-full-dev。 在scripts/config.js找到對應的配置對象瀏覽器
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
},
}
複製代碼
固然主要生成配置對象是這段代碼緩存
function genConfig (name) {
// opts爲builds裏面對應key的基礎配置對象
const opts = builds[name]
// config是真正要返回的配置對象
const config = {
input: opts.entry,
external: opts.external,
plugins: [
flow(),
alias(Object.assign({}, aliases, opts.alias))
].concat(opts.plugins || []),
output: {
file: opts.dest,
format: opts.format,
banner: opts.banner,
name: opts.moduleName || 'Vue'
},
onwarn: (msg, warn) => {
if (!/Circular/.test(msg)) {
warn(msg)
}
}
}
// built-in vars
const vars = {
__WEEX__: !!opts.weex,
__WEEX_VERSION__: weexVersion,
__VERSION__: version
}
// feature flags
Object.keys(featureFlags).forEach(key => {
vars[`process.env.${key}`] = featureFlags[key]
})
// build-specific env
// 根據不一樣的process.env.NODE_ENV加載不一樣的打包後版本
if (opts.env) {
vars['process.env.NODE_ENV'] = JSON.stringify(opts.env)
}
config.plugins.push(replace(vars))
if (opts.transpile !== false) {
config.plugins.push(buble())
}
Object.defineProperty(config, '_name', {
enumerable: false,
value: name
})
return config
}
if (process.env.TARGET) {
module.exports = genConfig(process.env.TARGET)
} else {
exports.getBuild = genConfig
exports.getAllBuilds = () => Object.keys(builds).map(genConfig)
}
複製代碼
根據配置對象的entry字段:
entry: resolve('web/entry-runtime-with-compiler.js')
複製代碼
以及resolve函數
const aliases = require('./alias')
const resolve = p => {
// web/ weex /server
const base = p.split('/')[0]
if (aliases[base]) {
// 拼接完整的入口文件
return path.resolve(aliases[base], p.slice(base.length + 1))
} else {
return path.resolve(__dirname, '../', p)
}
}
複製代碼
aliases.js文件
const path = require('path')
const resolve = p => path.resolve(__dirname, '../', p)
module.exports = {
vue: resolve('src/platforms/web/entry-runtime-with-compiler'),
compiler: resolve('src/compiler'),
core: resolve('src/core'),
shared: resolve('src/shared'),
web: resolve('src/platforms/web'),
weex: resolve('src/platforms/weex'),
server: resolve('src/server'),
sfc: resolve('src/sfc')
}
複製代碼
找到真正的入口文件爲: vue-dev/src/platforms/web/entry-runtime-with-compiler.js。
在entry-runtime-with-compiler.js文件中發現
import Vue from './runtime/index'
複製代碼
其實這裏主要作的是掛載$mount()方法, 能夠看我以前寫的文章mount掛載函數。
OK回到繼續回到咱們以前話題, 在vue-dev/src/platforms/web/runtime/index.js下發現這裏還不是真正的Vue構造函數
import Vue from './instance/index'
複製代碼
不過也立刻接近了, 繼續查找vue-dev/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'
// Vue構造函數
function Vue (options) {
// 提示必須使用new Vue()
if (process.env.NODE_ENV !== 'production' &&
!(this instanceof Vue)
) {
warn('Vue is a constructor and should be called with the `new` keyword')
}
// 執行初始化操做, 通常_前綴方法都是內部方法
// __init()方法是initMixin裏綁定的
this._init(options)
}
// 在Vue原型上掛載方法
initMixin(Vue)
stateMixin(Vue)
eventsMixin(Vue)
lifecycleMixin(Vue)
renderMixin(Vue)
export default Vue
複製代碼
export function initMixin (Vue: Class<Component>) {
Vue.prototype._init = function (options?: Object) {
// 緩存this
const vm: Component = this
// a uid
vm._uid = uid++
// 這裏只要是開啓config.performance進行性能調試時候一些組件埋點
let startTag, endTag
/* istanbul ignore if */
if (process.env.NODE_ENV !== 'production' && config.performance && mark) {
startTag = `vue-perf-start:${vm._uid}`
endTag = `vue-perf-end:${vm._uid}`
mark(startTag)
}
// a flag to avoid this being observed
// 標識一個對象是 Vue 實例, 避免再次被observed
vm._isVue = true
// merge options
// options是new Vue(options)配置對象
// _isComponent是一個內部屬性, 用於建立組件
if (options && options._isComponent) {
// optimize internal component instantiation
// since dynamic options merging is pretty slow, and none of the
// internal component options needs special treatment.
initInternalComponent(vm, options)
} else {
// 定義實例屬性$options: 用於當前 Vue 實例的初始化選項
vm.$options = mergeOptions(
resolveConstructorOptions(vm.constructor),
options || {},
vm
)
}
/* istanbul ignore else */
if (process.env.NODE_ENV !== 'production') {
initProxy(vm)
} else {
vm._renderProxy = vm
}
// expose real self
// 定義一個內部屬性_self
vm._self = vm
// 執行各類初始化操做
initLifecycle(vm)
initEvents(vm)
initRender(vm)
callHook(vm, 'beforeCreate')
initInjections(vm) // resolve injections before data/props
initState(vm)
initProvide(vm) // resolve provide after data/props
callHook(vm, 'created')
/* istanbul ignore if */
if (process.env.NODE_ENV !== 'production' && config.performance && mark) {
vm._name = formatComponentName(vm, false)
mark(endTag)
measure(`vue ${vm._name} init`, startTag, endTag)
}
// 執行掛載操做
if (vm.$options.el) {
vm.$mount(vm.$options.el)
}
}
}
複製代碼
深刻淺出vue.js