准备
源码的获取
源码目录结构
├── benchmarks 性能、基准测试
├── dist 构建打包的输出目录
├── examples 案例目录
├── flow flow 语法的类型声明
├── packages 一些额外的包,比如:负责服务端渲染的包 vue-server-renderer、配合 vue-loader 使用的的 vue-template-compiler,还有 weex 相关的
│ ├── vue-server-renderer
│ ├── vue-template-compiler
│ ├── weex-template-compiler
│ └── weex-vue-framework
├── scripts 所有的配置文件的存放位置,比如 rollup 的配置文件
├── src vue 源码目录
│ ├── compiler 编译器
│ ├── core Vue 核心库
│ │ ├── components 全局组件,比如 keep-alive
│ │ ├── config.js 一些默认配置项
│ │ ├── global-api 全局 API,比如熟悉的:Vue.use()、Vue.component() 等
│ │ ├── instance Vue 实例相关的,比如 Vue 构造函数就在这个目录下
│ │ ├── observer 响应式原理
│ │ ├── util 工具方法
│ │ └── vdom 虚拟 DOM 相关,比如熟悉的 patch 算法就在这儿
│ ├── platforms 平台相关的编译器代码
│ │ ├── web
│ │ └── weex
│ ├── server 服务端渲染相关
├── test 测试目录
├── types TS 类型声明
安装依赖
npm i
sourcemap
学习源码的过程中需要我们可以在控制台看见执行代码的定位所在,所以需要打开代码地图
在 package.json 文件中的 dev 脚本添加参数 --sourcemap
"dev": "rollup -w -c scripts/config.js --sourcemap --environment TARGET:web-full-dev"
运行项目
npm run dev
关于运行报错(plugin Rollup Core) Error: Could not load 的解决方案
原因:rollup-plugin-alias对windows的兼容不好
1.下载 github.com/ideayuye/ro… 并覆盖掉本地文件夹 \node_modules\rollup-plugin-alias
2.进入rollup-plugin-alias文件夹,依次执行npm i ,npm run build.
3.重新启动vue项目。
寻找入口文件
npm run dev
像rollup,webpack这类打包工具构建的时候会设置一个配置文件,里面会设置入口,打包工具会根据这个配置文件找到所有的依赖,再打包到一个文件。
npm run dev, 我们找到 package.json 中script下的 dev,可得知rollup打包构建的时候执行的这条命令。
npm run dev
## "dev": "rollup -w -c scripts/config.js --sourcemap --environment TARGET:web-full-dev"
scripts/config.js ----- 配置文件
--sourcemap ----- 代码地图
--environment TARGET --- 设置环境变量 TARGET 为 web-full-dev
script/ config.js
// srcipt/config.js
...
// 判断环境变量是否有 TARGET
// 如果有的话 使用 genConfig() 生成 rollup 配置文件
if (process.env.TARGET) {
module.exports = genConfig(process.env.TARGET)
} else {
exports.getBuild = genConfig
exports.getAllBuilds = () => Object.keys(builds).map(genConfig)
}
由于有设置环境变量TARGET,genConfig函数执行并且传入这个环境变量,值为 web-full-dev,那么这个环境变量有什么用呢?
genConfig
// srcipt/config.js
function genConfig (name) {
const opts = builds[name]
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)
}
}
}
接着继续找到变量 build, 根据name 找到匹配的变量 "web-full-dev"
// srcipt/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
},
...
}
entry 打包带文件入口 再接着查看resolve
// script/alias.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')
}
// srcipt/config.js
...
const aliases = require('./alias')
const resolve = p => {
// 根据路径中的前半部分去alias中找别名
const base = p.split('/')[0]
if (aliases[base]) {
return path.resolve(aliases[base], p.slice(base.length + 1))
} else {
return path.resolve(__dirname, '../', p)
}
}
...
结果
截取参数p 即传入的 "web/entry-runtime-with-compiler.js" 中的斜杆, 即 变量 base === web
根据base 可得到 aliases[base] === resolve('src/platforms/web')
p.slice(base.length + 1) 根据斜杆切割参数p 得到的是 entry-runtime-with-compiler.js
最终 我们可以得出
genConfig函数中配置对象 config的input值等于 绝对路径
src/platforms/web/entry-runtime-with-compiler.js
它就是我们要找到入口文件,这个入口包括了编译器和运行时