vue源码浅读--(1)文件结构、入口和运行机制

517 阅读4分钟

1.文件结构

├── benchmarks ---------------------------- 基准测试文件
├── dist ---------------------------------- 构建后文件的输出目录
├── examples ------------------------------ 存放一些使用Vue开发的应用案例
├── flow ---------------------------------- JS静态类型检查工具[Flow](https://flowtype.org/)的类型声明 vue2.x用的flow
├── package.json
├── packages ------------------------------ 核心代码之外的独立库
├── scripts ------------------------------- 包含与构建相关的脚本和配置文件
│   ├── alias.js -------------------------- 源码中使用到的模块导入别名
|   ├── build.js -------------------------- 构建相关的文件,一般情况下我们不需要动
│   ├── config.js ------------------------- 项目的构建配置
├── src ----------------------------------- 源码目录
│   ├── compiler -------------------------- 编译器代码,用来将 template 编译为 render 函数
│   │   ├── codegen ----------------------- 存放从抽象语法树(AST)生成render函数的代码
│   │   ├── directives -------------------- 指令定义 v-on ,v-bind, v-if等实现
│   │   ├── optimizer.js ------------------ 分析静态树,优化vdom渲染
│   │   ├── parser ------------------------ 存放将模板字符串转换成元素抽象语法树的代码[html编译]
│   ├── core ------------------------------ 存放通用的,核心代码
│   │   ├── components -------------------- 包含抽象出来的通用组件,目前只有keep-alive
│   │   ├── global-api -------------------- 给Vue构造函数挂载全局方法(静态方法)或属性的代码[全局API,Vue.use(),set(),delete()]
│   │   ├── instance ---------------------- Vue构造函数与原型相关代码
│   │   ├── observer ---------------------- 响应式实现,包含数据观测的核心代码
│   │   ├── util -------------------------- 工具类,props.js
│   │   ├── vdom -------------------------- 虚拟DOM的 creation 和 patching 的代码
│   ├── platforms ------------------------- 不同平台特有的相关代码
│   │   ├── weex -------------------------- weex平台支持
│   │   ├── web --------------------------- web平台支持
│   │   │   ├── entry-runtime.js ---------------- 运行时构建的入口
│   │   │   ├── entry-runtime-with-compiler.js -- 独立构建版本的入口
│   │   │   ├── entry-compiler.js --------------- vue-template-compiler 包的入口文件
│   │   │   ├── entry-server-renderer.js -------- vue-server-renderer 包的入口文件
│   ├── server ---------------------------- 服务端渲染(server-side rendering)的相关代码
│   ├── sfc ------------------------------- 包含单文件组件.vue文件的解析逻辑,用于vue-template-compiler包
│   ├── shared ---------------------------- 整个代码库通用的代码
├── test ---------------------------------- 测试文件
├── types --------------------------------- ts类型声明

benchmarks文件:benchmark的目的主要有两种,一是验证性能,另一个是获得一些基准数据,从而可以与本软件的其他版本或其他同类软件进行比较。通常不使用benchmark做正确性验证。benchmark测试不一定会发生在每个版本的开发期间。有可能仅会在有较大改动的时候才会进行一次benchmark测试。因此频率相对单元测试来说要低很多。

2.入口文件

(1)前端项目入口文件从package.json文件中查看。

可以找到:

"scripts": {
    "dev": "rollup -w -c scripts/config.js --environment TARGET:web-full-dev",
}

rollup是一个类似于webpack的js模块打包器,vue@1.0.10之前还是用的webpack,后改成了rollup, 目的是为了打包体积小一点,初始化速度快一点。 以下是作者本人回答

rollup 只是用于 Vue 发布文件的构建,对用户使用没有直接影响。之前用 webpack 打包,还是会自带一个小型的动态 module 加载机制,并且每个文件是包在一个模块函数里的。rollup 打包通过重命名 import binding 直接把所有文件的函数都放在同一个函数体里面... 所以最终出来的文件会小一些,并且初始化快个十几毫秒的样子。1.0.10 以后 npm 包里包含的是:dist/vue.common.js - 一个 rollup 构建后的单个 CommonJS 文件,package.json 里面的 main 指向这里。用 Webpack 或是 Browserify 打包,获取到的是这个文件。dist/vue.js 和 dist/vue.min.js - 构建后的可直接用于 <script> 引用的文件src - 源文件,ES6(目前只用了模块语法)。package.json 的 esnext:main 指向 src/index.js。用 jspm 或是 rollup 打包,获取的是 ES6 的源文件。---想起来,对于用户来说有一个实际变化就是没有办法再用 require('vue/src/xxx') 的方式直接获取 Vue 内部的模块,不过本来就不推荐这样做。

(2)进入scripts/config.js 文件[命令参数:TARGET: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 //每个包前面的注释,包括:版本、作者、日期
},
...
}

format 编译方式说明:
es:ES Modules,使用ES6的模板语法输出
cjs:CommonJs Module,遵循CommonJs Module规范的文件输出
amd:AMD Module,遵循AMD Module规范的文件输出
umd:支持外链规范的文件输出,此文件可以直接使用script标签

(3)进入web/entry-runtime-with-compiler.js文件

这个文件就是src/platforms/web/entry-runtime-with-compiler.js文件 原因请查看scripts/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')
}

src/platforms/web/entry-runtime-with-compiler.js内容

import Vue from 'core/index'

(4)进入core/index文件

import Vue from './instance/index'

(5)进入./instance/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

当执行new vue()时其实就调用的是这个构造函数,所以可以从此处开始看代码!

3.运行机制图