(封面图片来源于网络,侵删)
「这是我参与2022首次更文挑战的第1天,活动详情查看:2022首次更文挑战」。
vue作为目前最流行的前端框架之一,其设计理念、开发效率给前端开发者们提供了许多便利。随着vue3的出现,vue在前端领域中更是占据了举足轻重的地位。鉴于对vue的热爱,我也一直想从源码开始深入学习一下,但前前后后看了几次源码都一直没提笔,这次终于开始了。 本文针对vue2.6.14的源码进行学习,最近会持续更新。
一、vue源码下载
二、vue构建
使用开发工具打开vue源码,安装项目相关依赖,安装完成会在当前文件下生成node-modules文件夹。
cd vue-dev
npm install
执行npm run build指令,生成打包后的文件,注:这里生成的文件,在后续本地调试验证时可直接利用script标签引入。这步执行后会在当前目录下生成dist文件夹,dist文件夹下存放了vue生成的打包后文件。在README.md文件中,可查询这几种不同文件之间的区别。具体如何打包,在后面文章中介绍。
vue在执行完上述操作之后,就完成了引入文件的生成工作。
三、vue源码执行
我们知道,package.js文件中,定义了脚本的执行方式。找到packages.js文件下的script属性,查找dev命令。可看到vue-dev项目,使用了rollup进行打包,同时在执行dev命令的时候,主要读取了scripts/config.js下的文件,TARGET为web-full-dev。
"dev": "rollup -w -c scripts/config.js --sourcemap --environment TARGET:web-full-dev",
根据命令找到scripts/configs.js文件。在config.js文件下,我们可以看到有两个函数,其他均为变量,从函数入手,找到该文件的执行入口。
在dev命令下,有TARGET属性,TARGET:web-full-dev,该TARGET属性作为process.env的系统变量被读取,同时作为参数传给genConfig函数,genConfig函数会根据当前TARGET属性,读取响应配置。在这里文件执行genConfig函数,同时作为模块函数被导出。
// script/config.js
// 根据TARGET属性,判断执行
if (process.env.TARGET) {
module.exports = genConfig(process.env.TARGET)
} else {
exports.getBuild = genConfig
exports.getAllBuilds = () => Object.keys(builds).map(genConfig)
}
在genConfig中,主要生成了rollup的配置属性,其中包括input、output、plugins、external等,这些属性的内容实际又来源于builds对象,该对象中定义了TARGET属性对应的配置内容,builds对象中,定义了基本属性,可根据web-full-dev查找。根据设置的配置项,可找到实际的入口文件,在web/entry-runtime-with-compiler.js中。同时根据script/alias.js文件夹下对web别名的定义,可以定位到其所在目录为src/platforms/web
// web-full-dev属性对应配置项
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
},
}
// genConfig函数读取了配置属性
function genConfig (name) {
const opts = builds[name]
const config = {
input: opts.entry,
external: opts.external,
plugins: [...],
output: {
file: opts.dest,
...
},
onwarn: (msg, warn) => {
if (!/Circular/.test(msg)) {
warn(msg)
}
}
}
// scrpit/alias.js 别名文件
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')
}
四、src文件夹下的执行方式
通过config文件内容执行,我们找到了源码程序的入口文件,在web/entry-runtime-with-compiler.js,同时在alias.js文件下,可以找到web别名对应的文件夹路径。在该文件下,找到Vue的定义文件,在runtime/index下,runtime/index文件中,继续查找Vue定义文件,在core/index下,继续查看Core/index文件,查找到instance/index文件。
因此,一步步查找,可以找到Vue的真正入口,在core/instance/index.js文件下。
// alias.js
web: resolve('src/platforms/web'), 寻找web别名对应的文件夹在src/platforms/web下
// web/entry-runtime-with-compiler.js
import Vue from './runtime/index' 该文件夹中引入的Vue入口文件
// runtime/index.js
import Vue from 'core/index' 寻找到core下的index文件
// core/index.js
import Vue from './instance/index' 寻找到真正入口,在instance中
在core/instance/index.js文件中,定义了Vue
// vue初始化入口
function Vue (options) {
if (process.env.NODE_ENV !== 'production' &&
!(this instanceof Vue)
) {
warn('Vue is a constructor and should be called with the `new` keyword')
}
this._init(options)
}
五、如何调试vue源文件
在执行完build命令后,会在dist文件夹下生成不同文件,上方截图已经表明,vue.js一般包含了整个运行与编译环节的所有代码,我们一般通过引入该文件,进行代码调试。
在vue-dev项目中,也提供了测试文件夹。在src/example下,我们可以在该文件夹下,自定义文件夹或文件,引入dise/vue.js就可以对我们的源码进行相关调试验证。
本文主要从vue源码的构建、vue源码如何查找入口、vue源码如何调试三方面进行了详细介绍,下一章将学习vue选项初始化与数据响应式原理。