Vue 源码分析( 一 )目录结构、版本、入口
1、Vue 源码目录结构
dist:打包之后生成的结果目录
examples:代码示例
scripts:配置文件
src:源代码目录
compiler: 编译相关 (将template模板转换成render函数)
core:核心
components:定义了自带的 keep-alive 组件
global-api:Vue内的静态方法
instance:Vue构造函数、初始化、生命周期
observer:响应式
util:公共成员
vdom:虚拟dom
platforms:平台相关
web:web 平台相关代码
weex:app 相关代码
server:服务端渲染代码
sfc:将单文件组件转化为js对象
test:单元测试
2、Vue的不同构建版本
Vue 版本说明
说明
- 完整版:同时包含 编译器 和 运行时
- 编译器:用于将模板字符串编译为 JavaScript 渲染函数的代码,体积大、效率低( 约3000行代码)
- 运行时:用于创建 Vue 实例、渲染并处理虚拟DOM等的代码,体积小、效率高
- UMD:UMD 版本通用的模板版本,支持多种模块方式,vue.js 默认文件就是运行时 + 编译器的 UMD 版本,可以在html中引入 vue.js 来使用
- CommonJS(cjs):CommonJS 版本用来配合老的打包工具比如 Browserify 或 webpack 1
- MS Module:从 2.6 开始 Vue 会提供两个 ES Modules(ESM),为现代打包工具提供的版本
- ESM 格式被设计为可以被静态分析的,所以可以利用这一点进行 “tree-shaking”,并将用不到的代码排除出最终的包。
运行时与编译器的区别
- 运行时:由于此版本不包含编译器,所以无法使用 template 进行模板渲染
- 编译器:将 template 转换成 render 函数,所以可以使用 template 或者 render 函数进行渲染
vue-cli 使用的 Vue 版本
- 使用命令 vue inspect > output.js ( 非有效 webpack 配置文件 )
- 在运行以上命令后,在 output.js 文件中的 resolve.alias.vue$ 配置为 "vue/dist/vue.runtime.esm.js" ($为webpack语法,精确匹配的意思)
- 可以看出vue-cli 使用的是 运行时的 ESM 版本
3、入口文件
npm run dev
# "dev": "rollup -w -c scripts/config.js --sourcemap --environment TARGET:web-full-dev"
# -environment TARGET:web-full-dev 设置了环境变量 TARGET
通过以上的命令,去 scripts/config.js 内查看 rollup 配置对象,可以看到 genConfig 方法返回了配置对象 config
/* 导出配置 */
if (process.env.TARGET) {
module.exports = genConfig(process.env.TARGET)
} else {
exports.getBuild = genConfig
exports.getAllBuilds = () => Object.keys(builds).map(genConfig)
}
- genConfig 方法内通过 builds[name]返回基础配置,在合并其他配置后返回最后的 config
function genConfig (name) { // process.env.TARGET = "web-full-dev"
const opts = builds[name]
const config = {
input: opts.entry, // 入口文件位置
...
}
...
return config
}
- 观察 builds ,定义各种 模式 和 环境 的入口、出口、模块化方式、模式等配置项
- 入口配置项 entry,resolve('web/entry-runtime-with-compiler.js')
const builds = {
'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
},
...
}
- 通过 resolve 方法,返回当前模式的绝对路径, 并调用 path.resolve 方法合并 aliases[base] 和 p.slice(base.length + 1)
const aliases = require('./alias') // 拼装 web 的 绝对路径
const resolve = p => {
// 根据路径中的前半部分去alias中找别名
// p => web/entry-runtime-with-compiler.js base => web
const base = p.split('/')[0]
if (aliases[base]) {
// __dirname + src/platforms/web + entry-runtime-with-compiler.js
return path.resolve(aliases[base], p.slice(base.length + 1))
} else {
return path.resolve(__dirname, '../', p)
}
}
- 通过 base === “web”,调用 resolve('src/platforms/web')
- resolve 方法返回 path.resolve(__dirname, '../','src/platforms/web' ) 返回当前入口的绝对路径
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')
}
查找到入口文件 srcplatformswebentry-runtime-with-compiler.js