Vue.js 源码构建过程分析

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

Vue.js 为什么选用Rollup,而不是webpack?

  1. Tree-shaking 的优化:Rollup 在 tree-shaking 方面表现得更出色。Tree-shaking 是一种在打包时去除未引用代码的技术,可以帮助减少最终打包文件的大小。Rollup 的静态分析能够更准确地识别和移除未使用的代码,而 webpack 在这方面可能不如 Rollup。
  2. ES6 模块支持:Rollup 原生支持 ES6 模块,而 Vue.js 的源码也是使用 ES6 模块编写的。这种兼容性使得 Rollup 更适合用于打包 Vue.js 的源码。
  3. 简洁和高效:Rollup 的配置相对简洁,没有 webpack 那样的复杂插件系统。这使得 Rollup 在构建过程中更加高效,减少了不必要的复杂性和性能开销。
  4. 生成代码更小: Rollup 在打包库时具有更好的性能和优化能力,能够生成更小、更高效的代码包。

构建脚本

通常一个基于NPM托管的项目都会有一个package.json文件,它是对项目的描述文件,它的内容实际上通常是一个标准的json对象。

我们通常会配置script字段作为NPM得执行脚本,Vue.js源码构建的脚本如下:

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, 第二三条命令加了不同的参数,分别去构建web版,ssr版,weex版的Vue。

当在命令行运行npm run build时,实际上就会执行 node scripts/build.js,接下来我们来看它实际是怎么构建的。

构建过程

我们先从入口文件分析,在 scripts/build.js中:

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文件中拿到builds配置,然后根据命令行输入的 npm run build 后面所带的的参数,过滤出要执行的构建配置,这样就可以构建出不同版本的Vue.js。 接下来我们来看一下配置文件,在config.js 中:

js 复制代码
const builds = {
  // Runtime only (CommonJS). Used by bundlers e.g. Webpack & Browserify
  'web-runtime-cjs-dev': {
    entry: resolve('web/entry-runtime.js'),
    dest: resolve('dist/vue.runtime.common.dev.js'),
    format: 'cjs',
    env: 'development',
    banner
  },
  'web-runtime-cjs-prod': {
    entry: resolve('web/entry-runtime.js'),
    dest: resolve('dist/vue.runtime.common.prod.js'),
    format: 'cjs',
    env: 'production',
    banner
  },
  // 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
  },
  'web-full-cjs-prod': {
    entry: resolve('web/entry-runtime-with-compiler.js'),
    dest: resolve('dist/vue.common.prod.js'),
    format: 'cjs',
    env: 'production',
    alias: { he: './entity-decoder' },
    banner
  },
  // Runtime only ES modules build (for bundlers)
  'web-runtime-esm': {
    entry: resolve('web/entry-runtime.js'),
    dest: resolve('dist/vue.runtime.esm.js'),
    format: 'es',
    banner
  },
  // Runtime+compiler ES modules build (for bundlers)
  '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+compiler ES modules build (for direct import in browser)
  'web-full-esm-browser-dev': {
    entry: resolve('web/entry-runtime-with-compiler.js'),
    dest: resolve('dist/vue.esm.browser.js'),
    format: 'es',
    transpile: false,
    env: 'development',
    alias: { he: './entity-decoder' },
    banner
  },
  // Runtime+compiler ES modules build (for direct import in browser)
  'web-full-esm-browser-prod': {
    entry: resolve('web/entry-runtime-with-compiler.js'),
    dest: resolve('dist/vue.esm.browser.min.js'),
    format: 'es',
    transpile: false,
    env: 'production',
    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构建配置。 对于单个的配置,它要遵循Roullp的构建。其中entry表示入口文件,dest表示构建后的js文件地址,format表示构建的格式,cjs表示构建文件要遵循CommonJS规范,es表示构建文件要遵循ES Module规范,umd表示构建文件要遵循UMD规范。

web-runtime-cjs-dev配置为例,它的entryresolve('web/entry-runtime-with-compiler.js'), 先来看下reslove的定义。 在config.js中:

js 复制代码
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)
  }
}

这段代码中,先把resolve传入的参数p,以/分割成数组,base是数组的的第一个元素。在我们的例子中,参数pweb/entry-runtime.js, 那么 base 则为 webbase并不一个真实的路径,它的真是路径借助了别名配置,我们来看下它的别名配置。在script/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')
}

很显然,这里web对应的真实路径是 resolve('src/platforms/web'),这个路径就找到了源码的web路径。resolve通过path.resolve(aliases[base], p.slice(base.length + 1))找到了它的最终路径。它就在Vue.js 源码中src/platforms/web目录下的entry-runtime.js文件。这样我们就找到了'web-runtime-cjs-dev配置对应的入口文件就找到了。

它经过Roullp打包,最终会在dist目录下生成vue.runtime.common.dev.js文件。

总结

通过Vue.js 源码构建过程分析,我们了解到了不同作用,不同功能的Vue对应的入口文件及最终生成的JS 文件。

相关推荐
BBB努力学习程序设计38 分钟前
CSS Sprite技术:用“雪碧图”提升网站性能的魔法
前端·html
BBB努力学习程序设计44 分钟前
CSS3渐变:用代码描绘色彩的流动之美
前端·html
暴富的Tdy1 小时前
【基于 WangEditor v5 + Vue2 封装 CSDN 风格富文本组件】
vue.js·wangeditor·富文本
冰暮流星1 小时前
css之动画
前端·css
jump6801 小时前
axios
前端
spionbo1 小时前
前端解构赋值避坑指南基础到高阶深度解析技巧
前端
用户4099322502121 小时前
Vue响应式声明的API差异、底层原理与常见陷阱你都搞懂了吗
前端·ai编程·trae
开发者小天1 小时前
React中的componentWillUnmount 使用
前端·javascript·vue.js·react.js
永远的个初学者2 小时前
图片优化 上传图片压缩 npm包支持vue(react)框架开源插件 支持在线与本地
前端·vue.js·react.js
爱吃土豆的马铃薯ㅤㅤㅤㅤㅤㅤㅤㅤㅤ2 小时前
npm i / npm install 卡死不动解决方法
前端·npm·node.js