Vue 是前端三大 JavaScript 框架之一。它的核心是响应式原理,并被设计自底向上逐层应用的渐进式框架。
Vue 的核心库只关注视图层,不仅易于上手,还便于与第三方库或既有项目整合。另一方面,当与现代化的工具链以及各种支持类库结合使用时,Vue 也完全能够为复杂的单页应用提供驱动。
源码结构#
Vue 的源码结构非常清晰,在 src 里面的六个文件夹中。
下面是每个文件的简单介绍。
src
├── compiler # 编译相关
├── core # 核心代码
├── platforms # 不同平台的支持
├── server # 服务端渲染
├── sfc # .vue 文件解析
├── shared # 共享代码构建版本#
Vue 的源码支持编译成各种各样的版本。
Vue 可以根据编译后的模块类型分成不同的版本,也可以根据是否含有编译器分为运行时版本和完整版本,还可以根据 Node 环境分为开发版本和生产版本。
Module
cjs:适用于 CommonJs 模块的环境,比如 Node 环境esm:适用于 ES Module 的环境umd:适用于浏览器运行环境,使用script标签引用
Runtime
runtime:只有运行时的代码,没有编译器,此时需要使用vue-loader来编译模板代码,不能编译template上的代码。runtime + compiler:携带编译器,可以编译template上的代码。
ENV
development:开发版本,包含完整的警告和调试模式。production:生产版本,删除了警告,gzip 后只有 33.3KB。
详情可以查看文档
编译入口#
如何查找编译入口呢?
首先我们查看包管理文件package.json中的构建代码的脚本命令。
"scripts": {
// ...
build": "node scripts/build.js"
}再查看构建的脚本文件scripts/build.js的代码
// ...
let builds = require("./config").getAllBuilds();
// filter builds via command line arg
if (process.argv[2]) {
const filters = process.argv[2].split(",");
builds = builds.filter((b) => {
return filters.some(
(f) => b.output.file.indexOf(f) > -1 || b._name.indexOf(f) > -1
);
});
} else {
// filter out weex builds by default
builds = builds.filter((b) => {
return b.output.file.indexOf("weex") === -1;
});
}
build(builds);
// ...里面引入了一个配置文件config.js,查看配置文件scripts/config.js,选择其中的一个编译入口。
const builds = {
// ...
// Runtime+compiler CommonJS build (CommonJS)
"web-full-cjs-dev": {
entry: resolve("web/entry-runtime-with-compiler.js"),
dest: resolve("dist/vue.common.dev.js"),
format: "cjs",
env: "development",
alias: { he: "./entity-decoder" },
banner,
},
};entry属性是编译入口。但是resolve方法中的web是文件别名,查看scripts/alias.js文件别名的代码。
web: resolve("src/platforms/web");最后拼接路径发现编译的入口文件路径是下面这样的。
entry: "src/platforms/web/entry-runtime-with-compiler.js";这才是源码的入口文件,这是一个带有编译器并且会被编译成cjs模块类型的入口文件。
Vue 的出生地#
Vue 的出生地文件是项目最开始的文件,一切代码都是从这里开始。那么要如何查找呢?
打开platforms/web/entry-runtime-with-compiler.js,发现这只是 Web 平台中带编译器的源码入口,并不是 Vue 的出生地文件,继续查找,看下面的代码。
import Vue from "./runtime/index"; // ! 导入 Runtime 版本的 Vue这里导入了 Runtime (运行时)版本的 Vue,这也不是 Vue 的出生地文件,打开这个文件,路径在platforms/web/runtime/index.js中,继续查找,看下面的代码。
import Vue from "core/index";这里引入了 core 文件夹下的 Vue ,因为 core 也是别名,在scripts/alias.js文件中发现。
core: resolve('src/core'),然后打开src/core/index.js文件,发现这个文件也不是出生地文件,看下面的代码。
import Vue from "./instance/index";现在终于找到了 Vue 的出生地文件 ,路径在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";
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);
}
initMixin(Vue);
stateMixin(Vue);
eventsMixin(Vue);
lifecycleMixin(Vue);
renderMixin(Vue);
export default Vue;也就是说,核心代码 core 文件下的 instance 文件夹的index.js才是 Vue 的出生地文件。
总结一些查找路径。
-> 完整版本的的入口文件 entry-runtime-with-compiler.js
-> 运行时版本的入口文件 runtime/index.js
-> 核心代码的入口文件 core/index.js
-> 出生地文化 core/instance/index.js在出生地文件中,函数Vue就是 Vue 的构造函数,一切都是从这个函数开始。
我们在使用 Vue 的时候,是通过使用new方法创建一个实例开始的。
import Vue from "vue";
const vm = new Vue({
//... options
});但是,Vue 创建实际的过程是什么样子的呢?