一起来看看Vue是怎么发布的吧

110 阅读3分钟

本文参加了由公众号@若川视野 发起的每周源码共读活动,点击了解详情一起参与。

本篇文章要讲解的是Vue.js的发布流程,学习资料github.com/vuejs/core

前期准备

  1. 将项目克隆下来后,可以查看package.json文件或者查看官方项目的贡献指南去寻找自己想要得到的信息
  2. 这里我们查看package.json文件,vue的发布是scripts下的release命令
  3. 根据命令我们知道运行的是release.js文件
  4. 在阅读源码的过程中,由于之前对release.js文件中的相关依赖不是很熟悉,在了解了相关依赖后,阅读起来还是相对顺畅的,所以阅读前先了解文件中的相关依赖

相关依赖

minimist www.npmjs.com/package/min… 解析命令行参数选项

chalk www.npmjs.com/package/cha… 终端多色彩输出

semver www.npmjs.com/package/sem… 语义版本器,用于版本比较和验证

enquirer www.npmjs.com/package/enq… 创建CLI提示,用于交互

execa www.npmjs.com/package/exe… 执行终端命令

源码阅读

这一部分定义了一些变量

// 根据命令行参数取得preid
const preId =
  args.preid ||
  (semver.prerelease(currentVersion) && semver.prerelease(currentVersion)[0])
// 根据命令行参数取得dry
const isDryRun = args.dry
// 根据命令行参数取得skipTests
const skipTests = args.skipTests
// 根据命令行参数取得skipBuild
const skipBuild = args.skipBuild
// 读取packages文件夹,过滤掉以.ts结尾和以.开头的文件
const packages = fs
  .readdirSync(path.resolve(__dirname, '../packages'))
  .filter(p => !p.endsWith('.ts') && !p.startsWith('.'))
  
// 要跳过的包
const skippedPackages = []

// 版本递增 可以看semver文档
const versionIncrements = [
  'patch',
  'minor',
  'major',
  ...(preId ? ['prepatch', 'preminor', 'premajor', 'prerelease'] : [])
]

这一部分是函数的定义

// 生成版本函数
const inc = i => semver.inc(currentVersion, i, preId)
const bin = name => path.resolve(__dirname, '../node_modules/.bin/' + name)
// 执行命令函数
const run = (bin, args, opts = {}) =>
  execa(bin, args, { stdio: 'inherit', ...opts })
const dryRun = (bin, args, opts = {}) =>
  console.log(chalk.blue(`[dryrun] ${bin} ${args.join(' ')}`), opts)
const runIfNotDry = isDryRun ? dryRun : run
// 获取包文件夹函数
const getPkgRoot = pkg => path.resolve(__dirname, '../packages/' + pkg)
// 控制台输出函数
const step = msg => console.log(chalk.cyan(msg))

主函数main

发布的主要流程都在main函数中

async main() {}
  // 取得执行命令时要发布的版本号 yarn release 3.2.4
  let targetVersion = args._[0]

  // 没有版本号时,提供选项
  if (!targetVersion) {
    // no explicit version, offer suggestions
    const { release } = await prompt({
      type: 'select',
      name: 'release',
      message: 'Select release type',
      choices: versionIncrements.map(i => `${i} (${inc(i)})`).concat(['custom'])
    })

    // 如果选择的是custom
    if (release === 'custom') {
      targetVersion = (
        await prompt({
          type: 'input',
          name: 'version',
          message: 'Input custom version',
          initial: currentVersion
        })
      ).version
    } else {
      // 否则targetVersion为3.2.5
      targetVersion = release.match(/\((.*)\)/)[1]
    }
  }

上面这段代码在结合调试结果来看,执行yarn release 3.2.4,targetVersion为3.2.4

image.png 直接执行命令没有提供版本号时,就会给我们选择

image.png

// 校验版本是否符合规范
  if (!semver.valid(targetVersion)) {
    throw new Error(`invalid target version: ${targetVersion}`)
  }

  // 确认
  const { yes } = await prompt({
    type: 'confirm',
    name: 'yes',
    message: `Releasing v${targetVersion}. Confirm?`
  })

  if (!yes) {
    return
  }

执行测试

step('\nRunning tests...')
  // 没有skipTests和isDryRun为false时
  if (!skipTests && !isDryRun) {
    await run(bin('jest'), ['--clearCache'])
    await run('yarn', ['test', '--bail'])
  } else {
    console.log(`(skipped)`)
  }

更新所有包

step('\nUpdating cross dependencies...')
  updateVersions(targetVersion)

打包所有包

step('\nBuilding all packages...')
  if (!skipBuild && !isDryRun) {
    await run('yarn', ['build', '--release'])
    // test generated dts files
    step('\nVerifying type declarations...')
    await run('yarn', ['test-dts-only'])
  } else {
    console.log(`(skipped)`)
  }

生成changelog

await run(`yarn`, ['changelog'])

代码提交

 // 文件是否有改变
  const { stdout } = await run('git', ['diff'], { stdio: 'pipe' })
  if (stdout) {
    // 有改变执行git add git commit
    step('\nCommitting changes...')
    await runIfNotDry('git', ['add', '-A'])
    await runIfNotDry('git', ['commit', '-m', `release: v${targetVersion}`])
  } else {
    console.log('No changes to commit.')
  }

发布包

step('\nPublishing packages...')
  for (const pkg of packages) {
    await publishPackage(pkg, targetVersion, runIfNotDry)
  }

push到GitHub

step('\nPushing to GitHub...')
  await runIfNotDry('git', ['tag', `v${targetVersion}`])
  await runIfNotDry('git', ['push', 'origin', `refs/tags/v${targetVersion}`])
  await runIfNotDry('git', ['push'])

  if (isDryRun) {
    console.log(`\nDry run finished - run git diff to see package changes.`)
  }
  ...
  console.log()
}

main函数的主题函数就是这些了,还有些函数的具体实现可以到源码中查看,基本上没什么难度能看懂

总结

这里的总结借用若川哥的一张图

Vue.js发布流程.png