【vue源码解读 篇幅一】准备工作03 - Vue.js源码构建

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

构建脚本

    通常一个基于NPM托管的项目都会有一个package.json文件,它是对项目的描述文件,它的内容实际上是一个标准的JSON对象。
    我们通常会配置script字段作为NPM的执行脚本,Vue.js源码构建的脚本如下:

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

这里面总共有3条命令,作用都是构建Vue.js,后面2条是在第一条命令的基础上,添加一些环境参数。
当在命令运行npm run build的时候,实际上就会执行node scripts/build.js,接下来我们来看看它实际是怎么构建的。

构建过程

我们对于构建过程分析是基于源码的,先打开构建的入口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)

这段代码逻辑非常简单,先从配置文件读取配置,再通过命令行参数对构建配置做过滤,这样就可以构建出不同用途的Vue.js了。接下来我们看一下配置文件,在script/config.js中:

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
  },
  // Runtime+compiler CommonJS build (CommonJS)
  'web-full-cjs': {
    entry: resolve('web/entry-runtime-with-compiler.js'),
    dest: resolve('dist/vue.common.js'),
    format: 'cjs',
    alias: { he: './entity-decoder' },
    banner
  },
  // Runtime only (ES Modules). Used by bundlers that support ES Modules,
  // e.g. Rollup & Webpack 2
  'web-runtime-esm': {
    entry: resolve('web/entry-runtime.js'),
    dest: resolve('dist/vue.runtime.esm.js'),
    format: 'es',
    banner
  },
  // Runtime+compiler CommonJS build (ES Modules)
  'web-full-esm': {
    entry: resolve('web/entry-runtime-with-compiler.js'),
    dest: resolve('dist/vue.esm.js'),
    format: 'es',
    alias: { he: './entity-decoder' },
    banner
  },
  // runtime-only build (Browser)
  'web-runtime-dev': {
    entry: resolve('web/entry-runtime.js'),
    dest: resolve('dist/vue.runtime.js'),
    format: 'umd',
    env: 'development',
    banner
  },
  // runtime-only production build (Browser)
  'web-runtime-prod': {
    entry: resolve('web/entry-runtime.js'),
    dest: resolve('dist/vue.runtime.min.js'),
    format: 'umd',
    env: 'production',
    banner
  },
  // 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
  },
  // Runtime+compiler production build  (Browser)
  'web-full-prod': {
    entry: resolve('web/entry-runtime-with-compiler.js'),
    dest: resolve('dist/vue.min.js'),
    format: 'umd',
    env: 'production',
    alias: { he: './entity-decoder' },
    banner
  },
  // ...
}

这里列举了一些Vue.js构建的配置,关于还有一些服务端渲染webpack插件以及weex的打包配置就不列举了。
对于单个配置,它是遵循Rollup的构建规则的。其中entry属性表示构建的入口JS文件地址,dest属性表示构建后的JS文件地址。format属性表示构建的格式,cjs表示构建出来的文件遵循CommonJS规划,es表示构建出来的文件遵循ES Module规范。umd表示构建出来的文件遵循UMD规范。

'web-runtime-cjs': {
    entry: resolve('web/entry-runtime.js'),
    dest: resolve('dist/vue.runtime.common.js'),
    format: 'cjs',
    banner
}

web-runtime-cjs配置为例,它的entryresolve('web/entry-runtime.js'),先来看一下resolve函数的定义。
源码目录:script/config.js

const aliases = require('./alias')
const resolve = p => {
  //获取以/分割的前一部分,比如web
  const base = p.split('/')[0]
  if (aliases[base]) {
    //p.slice(base.length + 1)表示获取/之后的字符串,比如entry-runtime.js
    return path.resolve(aliases[base], p.slice(base.length + 1))
  } else {
    //如果aliases中不存在,比如dist/vue.runtime.common.js
    return path.resolve(__dirname, '../', p)
  }
}

这里的resolve函数实现的非常简单,它先把resolve函数传入的参数p通过/做了分割成数组,然后取数组第一个元素设置为base。在我们这个例子中,参数pweb/entry-runtime.js,那么base则为webbase并不是实际的路径,它的真实路径借助了别名的配置,我们来看一下别名配置的代码,在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')
}

很显然,这里web对应的真实路径是path.resolve(__dirname, '../src/platforms/web'),这个路径就找到了Vue.js源码的web目录。然后resolve函数通过path.resolve(aliases[base], p.slice(base.length + 1))找到了最终路径,他就是Vue.js源码web目录下的entry-runtime.js。因此,web-runtime-cjs配置对应的入口文件就找到了。
它经过Rollup的构建打包后,最终会在dist目录下生成vue.runtime.common.js

Runtime Only VS Runtime+Compiler

通常我们利用vue-cli去初始化我们的vue.js项目的时候会询问我们用Runtime Only版本的还是Runtime+Compiler版本。下面我们来对比这两个版本。

  • Runtime Only
        我们在使用Runtime Only版本的Vue.js的时候,通常需要借助如webpack的vue-loader工具把.vue文件编译成JavaScript,因为是在编译阶段做的,所以它只包含运行时的Vue.js代码,因此代码体积也会更轻量。
  • Runtime+Compiler
        .我们如果没有对代码做预编译,但又使用了Vue的template属性并传入一个字符串,则需要在客户端编译模板,如下所示:
// 需要编译器的版本
new Vue({
  template: '<div>{{ hi }}</div>'
})

// 这种情况不需要
new Vue({
  render (h) {
    return h('div', this.hi)
  }
})

因为在Vue.js 2.0中,最终渲染都是通过render函数,如果写template属性,则需要编译成render函数,那么这个编译过程会发生运行时,所以需要带有编译器的版本。

很显然,这个编译过程对性能会有一定损耗,所以通常我们更推荐使用Runtime-Only的Vue.js。

总结

通过这一节的分析,我们可以了解到Vue.js的构建打包过程,也知道了不同作用和功能的Vue.js,他们对应的入口以及最终编译生成的JS文件。尽管在实际开发过程中我们会用Runtime Only版本开发比较多,但为了分析Vue的编译过程,我们这门课重点分析的源码是Runtime+Compiler的Vue.js。

Vue.js源码全方位深入解析最新下载地址.rar Vue.js源码全方位深入,帮你更深入了解vue 第1章 准备工作 介绍了 Flow、Vue.js源码目录设计、Vue.js源码构建方式,以及从入口开始分析了 Vue.js 的初始化过程。 第2章 数据驱动 详细讲解了模板数据到 DOM 渲染的过程,从 new Vue 开始,分析了 mount、render、update、patch 等流程。 第3章 组件化 分析了组件化的实现原理,并且分析了组件周边的原理实现,包括合并配置、生命周期、组件注册、异步组件。 第4章 深入响应式原理(上) 详细讲解了数据的变化如何驱动视图的变化,分析了响应式对象的创建,依赖收 集、派发更新的实现过程,一些特殊情况的处理,并对比了计算属性和侦听属性的实现,最后分析了组件更新的过程。 第5章 深入响应式原理(下) 详细讲解了数据的变化如何驱动视图的变化,分析了响应式对象的创建,依赖收集、派发更新的实现过程,一些特殊情况的处理,并对比了计算属性和侦听属性的实现,最后分析了组件更新的过程。 第6章 -编译(上) 从编译的入口函数开始,分析了编译的三个核心流程的实现:parse -> optimize -> codegen。 第7章 -编译(下) 从编译的入口函数开始,分析了编译的三个核心流程的实现:parse -> optimize -> codegen。 第8章 -扩展(上) 详细讲解了 event、v-model、slot、keep-alive、transition、transition-group 等常用功能的原理实现,该章节作为一个可扩展章节,未来会分析更多 Vue 提供的特性。 第9章 -扩展(中) 详细讲解了 event、v-model、slot、keep-alive、transition、transition-group 等常用功能的原理实现,该章节作为一个可扩展章节,未来会分析更多 Vue 提供的特性。 第10章 -扩展(下) 详细讲解了 event、v-model、slot、keep-alive、transition、transition-group 等常用功能的原理实现,该章节作为一个可扩展章节,未来会分析更多 Vue 提供的特性。 第11章 Vue-Router 分析了 Vue-Router 的实现原理,从路由注册开始,分析了路由对象、matcher,并深入分析了整个路径切换的实现过程和细节。 第12章 Vuex 分析了 Vuex 的实现原理,深入分析了它的初始化过程,常用 API 以及插件部分的实现。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值