Vue源码——准备工作

源码目录

Vue.js 的源码都在 src 目录下,结构如下:

src
├── compiler        # 编译相关 (模板解析成 ast 语法树及优化)可以构建时也可以运行时
├── core            # 核心代码 (内置组件、全局 API 封装,Vue 实例化、观察者、虚拟 DOM、工具函数等)
├── platforms       # 不同平台的支持(入口:web和weex)
├── server          # 服务端渲染(跑在服务端的 Node.js)
├── sfc             # .vue 文件解析
├── shared          # 共享代码(浏览器端和服务端的共享工具方法)

源码构建

Vue.js 源码是基于 Rollup 构建的,它的构建相关配置都在 scripts 目录下。

构建脚本

通常一个npm托管项目都会有个package.json文件,它是对项目的描述文件,内容为一个标准JSON对象。
通常会配置 script 字段作为 NPM 的执行脚本:

{
  "script": {
    "build": "node scripts/build.js",
    "build:ssr": "npm run build -- web-runtime-cjs,web-server-renderer",
    "build:weex": "npm run build -- weex"
  }
}

可以看到这三条命令都是通过script/build.js构建 vue.js ,不同的是后面两条命令增加了环境参数。

构建过程

script/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中的配置,然后通过命令行参数做配置过滤。
然后看一下script/confit.js,以web-runtime-cjs为例:

const builds = {
  // Runtime only (CommonJS). Used by bundlers e.g. Webpack & Browserify
  'web-runtime-cjs': {
    entry: resolve('web/entry-runtime.js'),//入口
    dest: resolve('dist/vue.runtime.common.js'),//目标文件
    format: 'cjs',//构建格式
    banner
  }
}

构建格式分为:

格式名称 遵循规范
cjs CommonJS
es ES Module
umd UMD

entry看起,它的entryresolve(‘web/entry-runtime.js’),那么看一下resolve方法:

const aliases = require('./alias')
const resolve = p => {
  const base = p.split('/')[0]
  if (aliases[base]) {
    return path.resolve(aliases[base], p.slice(base.length + 1))
  } else {
    return path.resolve(__dirname, '../', p)
  }
}

它将传入参数通过/分割为数组,取第一个元素设置为base,此时base并不是实际路径,而是别名。
可以看到这里别名在script/alias中:

const path = require('path')

module.exports = {
  vue: path.resolve(__dirname, '../src/platforms/web/entry-runtime-with-compiler'),
  compiler: path.resolve(__dirname, '../src/compiler'),
  core: path.resolve(__dirname, '../src/core'),
  shared: path.resolve(__dirname, '../src/shared'),
  web: path.resolve(__dirname, '../src/platforms/web'),
  weex: path.resolve(__dirname, '../src/platforms/weex'),
  server: path.resolve(__dirname, '../src/server'),
  entries: path.resolve(__dirname, '../src/entries'),
  sfc: path.resolve(__dirname, '../src/sfc')
}

这样就可以通过对应目录找到真实的入口路径。
../src/platforms/web/entry-runtime.js,经过 Rollup 的构建打包,最终在dist目录下生成vue.runtime.common.js

Runtime Only 和 Runtime+Compiler

当用 vue-cli 去初始化Vue项目时会询问用Runtime Only 版本的还是 Runtime + Compiler 版本,对比一下:

  • Runtime Only

需要使用 webpack 的 vue-loader 工具将.vue文件编译为JavaScript。由于做了预编译,所以它只包含运行代码,因此代码体积更轻量。

  • Runtime + Compiler

如果没有对代码做预编译,但又使用了 Vue 的 template 属性并传入一个字符串,则需要在客户端编译模板。

可以看下两种版本:

// 需要编译器的版本
new Vue({
  template: '
{{ hi }}
' }) // 这种情况不需要 new Vue({ render (h) { return h('div', this.hi) } })

由于最终渲染都是通过render函数,因此当写了templete属性时就需要编译。
由于编译的性能损耗,因此更推荐使用Runtime-Only 的 Vue.js。

你可能感兴趣的:(Vue源码——准备工作)