Vuex源码阅读(4):Vuex 中的代码构建

阅读某个库的源码,除了阅读功能性的代码之外,还应该多多注意工程化部分的代码,例如:Vuex 中的构建脚本和发布脚本,都很有意思,很值得我们借鉴和学习。今天,让我们一起看看 Vuex 的源码是如何构建的。

Vuex 的代码构建使用了 Rollup ,如果对其不怎么了解的话,可以先看看它的官方文档

1,package.json

首先我们应该看 Vuex 项目的 package.json 文件,里面与构建有关的部分如下所示:

{
  "scripts": {
    "build": "npm run build:main && npm run build:logger",
    "build:main": "node scripts/build-main.js",
    "build:logger": "node scripts/build-logger.js"
  }
}

这里定义了三个 npm 脚本命令,在命令行中,我们可以使用 npm run 命令 执行相应的脚本,例如:

npm run build:main
# 等同于执行了
node scripts/build-main.js

我们还可以在 scripts 中定义脚本命令执行其他的 npm 脚本命令,例如上面的 build,它就调用执行了 build:main 和 build:logger 脚本命令,下面简要说明下每个脚本的作用。

  • "build:main": "node scripts/build-main.js":使用 node 执行 scripts/build-main.js 文件,用于构建 Vuex 的功能代码。
  • "build:logger": "node scripts/build-logger.js":使用 node 执行 scripts/build-logger.js 文件,用于构建 logger 插件。
  • "build": "npm run build:main && npm run build:logger":执行 build:main 和 build:logger 脚本命令。

下面以 "build:main": "node scripts/build-main.js" 为例说明下 Vuex 的构建,"build:logger": "node scripts/build-logger.js" 和其思路一样,就不过多赘述了。

2,/scripts/build-main.js

"build:main": "node scripts/build-main.js" 是使用 node 执行 scripts/build-main.js 文件,我们看下这个文件的具体内容:

// scripts/build-main.js
const { run } = require('./build')

const files = [
  'dist/vuex.esm.browser.js',
  'dist/vuex.esm.browser.min.js',
  'dist/vuex.esm.js',
  'dist/vuex.js',
  'dist/vuex.min.js',
  'dist/vuex.common.js'
]

run('rollup.main.config.js', files)

build-main.js 执行 build.js 文件中导出的 run 方法,参数是 'rollup.main.config.js' 以及 files,这个 'rollup.main.config.js' 是 rollup 的配置文件,files 参数与 rollup 的构建无关,它只是为 build.js 中的 checkAllSizes 方法提供参数,具体看 scripts/build.js 的内容。

3,/scripts/build.js

// /scripts/build.js
// fs-extra是系统fs模块的拓展,提供了更多便利的API,并继承了fs模块的API。
const fs = require('fs-extra')
// chalk的作用是:美化终端输出的文本
const chalk = require('chalk')
// execa的作用是:在js代码中执行shell命令
const execa = require('execa')
// gzipSync:node官方库提供的函数,用于压缩文件
const { gzipSync } = require('zlib')
// compress:第三方库brotli提供的函数,用于压缩文件
const { compress } = require('brotli')

// 导出的 run 方法,主线思路在这里。
async function run(config, files) {
  // build(config) 使用 rollup 进行代码的构建;copy() 只是简单地将 src/index.mjs 拷贝到 dist/vuex.mjs。
  // 注意 build(config) 和 copy() 是同时进行的。
  await Promise.all([build(config), copy()])
  // 检查 rollup 构建出的文件的尺寸,将相关信息打印出来
  checkAllSizes(files)
}

// 执行 rollup 构建代码,配置文件是 config
async function build(config) {
  await execa('rollup', ['-c', config], { stdio: 'inherit' })
}

// 简单地将 src/index.mjs 拷贝到 dist/vuex.mjs
async function copy() {
  await fs.copy('src/index.mjs', 'dist/vuex.mjs')
}

// 检查 rollup 构建出的文件的尺寸,将相关信息打印出来
function checkAllSizes(files) {
  console.log()
  // 对每一个文件进行遍历检查
  files.map((f) => checkSize(f))
  console.log()
}

// 检查每个文件的函数,打印出尺寸相关的信息
function checkSize(file) {
  // 使用 fs 读取文件
  const f = fs.readFileSync(file)
  // 文件原始的尺寸
  const minSize = (f.length / 1024).toFixed(2) + 'kb'
  // 使用 gzipSync 压缩文件
  const gzipped = gzipSync(f)
  // 使用 gzipSync 压缩过后的文件大小
  const gzippedSize = (gzipped.length / 1024).toFixed(2) + 'kb'
  // 使用 compress 压缩文件
  const compressed = compress(f)
  // 使用 compress 压缩过后的文件大小
  const compressedSize = (compressed.length / 1024).toFixed(2) + 'kb'
  // 使用 console.log() 输出相关信息,这里借助 chalk 输出带有样式的文本。
  console.log(
    `${chalk.gray(
      chalk.bold(file)
    )} size:${minSize} / gzip:${gzippedSize} / brotli:${compressedSize}`
  )
}

module.exports = { run }

其中的 run 方法就是构建的主要思路,上面的代码都添加了注释,请自行查看。copy() 和 checkAllSizes(files) 比较简单,现在说说 build(config),build() 函数执行 rollup 构建操作,rollup 的配置文件是 config,在这里就是 'rollup.main.config.js',我们来看看这个 'rollup.main.config.js' 文件。

4,rollup.main.config.js

import { createEntries } from './rollup.config'

export default createEntries([
  { input: 'src/index.js', file: 'dist/vuex.esm.browser.js', format: 'es', browser: true, transpile: false, env: 'development' },
  { input: 'src/index.js', file: 'dist/vuex.esm.browser.min.js', format: 'es', browser: true, transpile: false, minify: true, env: 'production' },
  { input: 'src/index.js', file: 'dist/vuex.esm.js', format: 'es', env: 'development' },
  { input: 'src/index.cjs.js', file: 'dist/vuex.js', format: 'umd', env: 'development' },
  { input: 'src/index.cjs.js', file: 'dist/vuex.min.js', format: 'umd', minify: true, env: 'production' },
  { input: 'src/index.cjs.js', file: 'dist/vuex.common.js', format: 'cjs', env: 'development' }
])

这个文件导出了用于构建 Vuex 功能代码的配置。我们可以看到,这个文件并不是直接导出配置,而是借助了 rollup.config.js 中的 createEntries 函数创建 rollup 配置,我们到 rollup.config.js 文件中具体看一下。

5,rollup.config.js

这里的主要内容是 rollup 的配置,可以点击 rollup 官网 查看 rollup 的使用方法。

import buble from '@rollup/plugin-buble'
import replace from '@rollup/plugin-replace'
import resolve from '@rollup/plugin-node-resolve'
import commonjs from '@rollup/plugin-commonjs'
import { terser } from 'rollup-plugin-terser'
import pkg from './package.json'

const banner = `/*!
 * vuex v${pkg.version}
 * (c) ${new Date().getFullYear()} Evan You
 * @license MIT
 */`

export function createEntries(configs) {
  return configs.map((c) => createEntry(c))
}

function createEntry(config) {
  const c = {
    input: config.input,
    plugins: [],
    output: {
      banner,
      file: config.file,
      format: config.format
    },
    onwarn: (msg, warn) => {
      if (!/Circular/.test(msg)) {
        warn(msg)
      }
    }
  }

  if (config.format === 'umd') {
    c.output.name = c.output.name || 'Vuex'
  }

  c.plugins.push(replace({
    __VERSION__: pkg.version,
    __DEV__: config.format !== 'umd' && !config.browser
      ? `(process.env.NODE_ENV !== 'production')`
      : config.env !== 'production'
  }))

  if (config.transpile !== false) {
    c.plugins.push(buble())
  }

  c.plugins.push(resolve())
  c.plugins.push(commonjs())

  if (config.minify) {
    c.plugins.push(terser({ module: config.format === 'es' }))
  }

  return c
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值