Vue 源码分析( 一 )目录结构、版本、入口
1、Vue 源码目录结构
dist:打包之后生成的结果目录
examples:代码示例
scripts:配置文件
src:源代码目录compiler: 编译相关 (将template模板转换成render函数)core:核心components:定义了自带的 keep-alive 组件global-api:Vue内的静态方法instance:Vue构造函数、初始化、生命周期observer:响应式util:公共成员vdom:虚拟domplatforms:平台相关web:web 平台相关代码weex:app 相关代码server:服务端渲染代码sfc:将单文件组件转化为js对象
test:单元测试
2、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”,并将用不到的代码排除出最终的包。
- ES6 模块与 CommonJS 模块的差异 https://www.cnblogs.com/chenwenhao/p/12153332.html
运行时与编译器的区别
- 运行时:由于此版本不包含编译器,所以无法使用 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 = genConfigexports.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 => webconst base = p.split('/')[0]if (aliases[base]) {// __dirname + src/platforms/web + entry-runtime-with-compiler.jsreturn 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