├── 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