Выпущен Vue 3.2, как Ю Юйси выпустил Vue.js?

внешний интерфейс JavaScript Vue.js
Выпущен Vue 3.2, как Ю Юйси выпустил Vue.js?

1. Введение

Привет всем, яВакагава. Добро пожаловать, чтобы следовать за мнойПубличный аккаунт Wakagawa Vision, недавно организованныйЧитайте исходный код вместеДействия, если вам интересно, вы можете добавить меня в WeChatruochuan12, долгосрочный обмен и обучение.

написано раньше"Изучение серии "Общая архитектура исходного кода""ВключаютjQuery,underscore,lodash,vuex,sentry,axios,redux,koa,vue-devtools,vuex4Десять статей с исходным кодом.

Написание относительно сложного исходного кода стоило мне времени и энергии, и я не получил много лайков от чтения.На самом деле, это был настоящий удар. С точки зрения объема чтения и выгоды для читателей это не может способствовать непрерывному выпуску статей авторами.

Так что измените свое мнение и напишите несколько относительно простых для понимания статей.На самом деле исходный код не так сложен, как можно себе представить, по крайней мере многие смогут понять.

You Yuxi недавно выпустила версию 3.2. Младшая версия уже3.2.4. Эта статья предназначена для того, чтобы узнать, как выпускается Youda.vuejsДа, изучите исходный код для собственного использования.

эта статья посвященаvue-next/scripts/release.jsфайл, количество строк кода во всем файле составляет всего200Это больше, чем линия, но этому стоит научиться.

Гёте однажды сказал: «Читать хорошую книгу — значит разговаривать с благородными людьми». То же самое можно получить: чтение исходного кода — это тоже способ обучения и общения с автором.

Прочитав эту статью, вы узнаете:

1. 熟悉 vuejs 发布流程
2. 学会调试 nodejs 代码
3. 动手优化公司项目发布流程

Прежде чем подготавливать окружение, давайте предварительно его просмотрим.vuejsпроцесс выпуска.

vue 发布流程

2. Подготовка окружающей среды

Открытымvue-next, Проекты с открытым исходным кодом обычно можно найти вREADME.mdили.github/contributing.mdНайдите рекомендации по содействию.

И Contribution Guide много пишет об участии в разработке проекта. Например, как запустить, какова структура каталогов проекта. Как инвестировать в развитие, какие нужны резервы знаний и т.д.

вам нужно убедитьсяNode.jsверсия10+, иyarnВерсия1.x Yarn 1.x.

вы установилиNode.jsверсия скорее всего ниже10. Самый простой способ — зайти на официальный сайт для переустановки. Также можно использоватьnvmи т.д. управлениеNode.jsВерсия.

node -v
# v14.16.0
# 全局安装 yarn

# 建议克隆我的项目
git clone https://github.com/lxchuan12/vue-next-analysis.git
cd vue-next-analysis/vue-next

# 或者克隆官方项目
git clone https://github.com/vuejs/vue-next.git
cd vue-next

# 安装 yarn
npm install --global yarn
# 安装依赖
yarn # install the dependencies of the project
# yarn release

2.1 Строго проверяйте использование пряжи для установки зависимостей

Тогда давайте посмотримvue-next/package.jsonдокумент.

// vue-next/package.json
{
    "private": true,
    "version": "3.2.4",
    "workspaces": [
        "packages/*"
    ],
    "scripts": {
        // --dry 参数是我加的,如果你是调试 代码也建议加
        // 不执行测试和编译 、不执行 推送git等操作
        // 也就是说空跑,只是打印,后文再详细讲述
        "release": "node scripts/release.js --dry",
        "preinstall": "node ./scripts/checkYarn.js",
    }
}

если вы попытаетесь использоватьnpmЕсли вы устанавливаете зависимости, вы должны получить сообщение об ошибке. Почему он сообщает об ошибке. так какpackage.jsonимеет префиксpreinstall node ./scripts/checkYarn.jsОпределить, является ли обязательным требованием использованиеyarnУстановить.

scripts/checkYarn.jsФайл выглядит следующим образом, то есть вprocess.envНайдите путь выполнения в переменной окруженияnpm_execpath,если неyarnПредупреждение выводится, а процесс заканчивается.

// scripts/checkYarn.js
if (!/yarn\.js$/.test(process.env.npm_execpath || '')) {
  console.warn(
    '\u001b[33mThis repository requires Yarn 1.x for scripts to work properly.\u001b[39m\n'
  )
  process.exit(1)
}

Если вы хотите игнорировать это решение предварительного крючка, вы можете использоватьyarn --ignore-scriptsЗаказ. Есть также задние крючкиpost.Для получения более подробной информации ознакомьтесь с документацией по npm.

2.2 Отладка файла vue-next/scripts/release.js

Тогда давайте научимся отлаживатьvue-next/scripts/release.jsдокумент.

объявить мое здесьVSCodeверсия1.59.0должен1.50.0Его можно отладить в соответствии со следующими шагами.

code -v
# 1.59.0

оказатьсяvue-next/package.jsonоткрыть файл, затем вscriptsвыше будетdebug(отладка), после нажатия выберитеrelease. для входа в режим отладки.

debugger

В это время терминал будет таким, как показано на рисунке ниже, сDebugger attached.вывод. Выложите фото в это время.

terminal

Для получения дополнительной информации об отладке nodejs вы можете просмотреть официальную документацию.

После того, как вы научитесь отлаживать, сначала примерно пройдитесь по процессу, поставьте несколько точек останова в ключевых местах и ​​пройдитесь еще несколько раз, и тогда вы сможете угадать цель исходного кода.

3 Некоторые описания зависимостей и объявления функций в начале файла

Мы можем следить за точками останова и смотреть на введение некоторых зависимостей и объявлений функций в начале файла.

3.1 Часть 1

// vue-next/scripts/release.js
const args = require('minimist')(process.argv.slice(2))
// 文件模块
const fs = require('fs')
// 路径
const path = require('path')
// 控制台
const chalk = require('chalk')
const semver = require('semver')
const currentVersion = require('../package.json').version
const { prompt } = require('enquirer')

// 执行子进程命令   简单说 就是在终端命令行执行 命令
const execa = require('execa')

Через зависимости мы можемnode_modulesНайдите зависимости соответствующей установки. также найти егоREADMEиgithubсклад.

3.1.1 Минимальный анализ параметров командной строки

minimist

Проще говоря, эта библиотека анализирует параметры командной строки. Глядя на пример, нам легче понять передачу параметров и результаты парсинга.

$ node example/parse.js -a beep -b boop
{ _: [], a: 'beep', b: 'boop' }

$ node example/parse.js -x 3 -y 4 -n5 -abc --beep=boop foo bar baz
{ _: [ 'foo', 'bar', 'baz' ],
  x: 3,
  y: 4,
  n: 5,
  a: true,
  b: true,
  c: true,
  beep: 'boop' }
const args = require('minimist')(process.argv.slice(2))

вprocess.argvПервый и второй элементыNodeПолные пути файловой системы к исполняемым файлам и исполняемым файлам JavaScript, независимо от того, вводите ли вы их как таковые.

3.1.2 меловой терминал: многоцветный вывод.

chalk

Проще говоря, это используется для многоцветного вывода терминала.

3.1.3 Семантическое управление версиями semver

semver

Семантическая версия реализации NodeJS, используемая для сравнения проверки версий и т. д. Вы можете увидеть это в симатической версии.Семантическое управление версиями 2.0.0 Документация

Формат версии: основной номер версии. дополнительный номер версии. номер редакции. Правила увеличения номера версии следующие:
Основной номер версии: когда вы вносите несовместимые изменения API,
Второстепенный номер версии: когда вы добавляете функции, совместимые с предыдущими версиями,
Номер редакции: когда вы вносите исправления для проблем обратной совместимости.
Предыдущий номер версии и информация о компиляции версии могут быть добавлены в конце «основной номер версии. дополнительный номер версии. номер редакции» в качестве расширения.

3.1.4 Опрашивающий в интерактивном режиме запрашивает CLI

Проще говоря, он интерактивно запрашивает у пользователя ввод.

enquirer

3.1.5 команда выполнения execa

Проще говоря, он выполняет команды, аналогичные вводу команд в терминал самим, например,echo 若川.

execa

// 例子
const execa = require('execa');

(async () => {
  const {stdout} = await execa('echo', ['unicorns']);
  console.log(stdout);
  //=> 'unicorns'
})();

Прочитав первую часть, переходим ко второй части.

3.2 Часть II

// vue-next/scripts/release.js

// 对应 yarn run release --preid=beta
// beta
const preId =
  args.preid ||
  (semver.prerelease(currentVersion) && semver.prerelease(currentVersion)[0])
// 对应 yarn run release --dry
// true
const isDryRun = args.dry
// 对应 yarn run release --skipTests
// true 跳过测试
const skipTests = args.skipTests
// 对应 yarn run release --skipBuild 
// true
const skipBuild = args.skipBuild

// 读取 packages 文件夹,过滤掉 不是 .ts文件 结尾 并且不是 . 开头的文件夹
const packages = fs
  .readdirSync(path.resolve(__dirname, '../packages'))
  .filter(p => !p.endsWith('.ts') && !p.startsWith('.'))

Вторую часть относительно легко продолжить, чтобы увидеть третью часть.

3.3 Часть III

// vue-next/scripts/release.js

// 跳过的包
const skippedPackages = []

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

const inc = i => semver.inc(currentVersion, i, preId)

Эта часть может быть не совсем понята.incзаключается в создании версии. Больше можно посмотретьдокументация

semver.inc('3.2.4', 'prerelease', 'beta')
// 3.2.5-beta.0

3.4 Часть 4

Четвертая часть объявляет некоторые функции сценария выполнения и т.д.

// vue-next/scripts/release.js

// 获取 bin 命令
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))

3.4.1 функция корзины

Получатьnode_modules/.bin/Команда в каталоге, весь файл используется один раз.

bin('jest')

Эквивалентно запуску в командном терминале, корневой каталог проекта./node_modules/.bin/jestЗаказ.

3.4.2 запустить, высушитьВыполнить, запустить, если не высохнуть

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

runНа самом деле запускайте команды в терминале, напримерyarn build --release

dryRunне работает, простоconsole.log();напечатать 'сборка пряжи --release'

runIfNotDryЕсли это не пустой запуск, выполните команду. Параметр isDryRun вводится через консоль.yarn run release --dryВот и всеtrue.runIfNotDryОн просто печатает и не выполняет команды. Преимущество этого дизайна в том, что иногда вы не хотите отправлять напрямую, вы должны сначала посмотреть на результат выполнения команды. Должен сказать, Ю Да просто умеет играть.

существуетmainПодобные советы также можно увидеть в конце функции. Можно использоватьgit diffСначала посмотрите на модификацию файла.

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

Прочитав некоторые введения в зависимости и объявления функций в начале файла, давайте посмотримmainОсновная функция входа.

4 основной основной процесс

Раздел 4, в основномmainФункциональный анализ разборки.

4.1 Основная функция сортировки процессов

const chalk = require('chalk')
const step = msg => console.log(chalk.cyan(msg))
// 前面一堆依赖引入和函数定义等
async function main(){
  // 版本校验

  // run tests before release
  step('\nRunning tests...')
  // update all package versions and inter-dependencies
  step('\nUpdating cross dependencies...')
  // build all packages with types
  step('\nBuilding all packages...')

  // generate changelog
  step('\nCommitting changes...')

  // publish packages
  step('\nPublishing packages...')

  // push to GitHub
  step('\nPushing to GitHub...')
}

main().catch(err => {
  console.error(err)
})

вышеmainВ функции отсутствуют многие конкретные реализации функций. Далее разбираемmainфункция.

4.2 Подтвердите выпуск версии

Хотя первый код относительно длинный, его легко понять. Главное, подтвердить версию, которая будет выпущена.

При отладке давайте взглянем на два скриншота этого абзаца, там легко разобраться.

终端输出选择版本号

终端输入确认版本号

// 根据上文 mini 这句代码意思是 yarn run release 3.2.4 
// 取到参数 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'])
  })

// 选自定义
  if (release === 'custom') {
    targetVersion = (
      await prompt({
        type: 'input',
        name: 'version',
        message: 'Input custom version',
        initial: currentVersion
      })
    ).version
  } else {
    // 取到括号里的版本号
    targetVersion = release.match(/\((.*)\)/)[1]
  }
}

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

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

// false 直接返回
if (!yes) {
  return
}

4.3 Выполнение тестовых случаев

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

4.4 Обновите номера версий всех пакетов и номера версий внутренних зависимостей, связанных с vue.

Эта часть предназначена для обновления корневого каталогаpackage.jsonномер версии и всеpackagesномер версии.

// update all package versions and inter-dependencies
step('\nUpdating cross dependencies...')
updateVersions(targetVersion)
function updateVersions(version) {
  // 1. update root package.json
  updatePackage(path.resolve(__dirname, '..'), version)
  // 2. update all packages
  packages.forEach(p => updatePackage(getPkgRoot(p), version))
}

4.4.1 updatePackage Номер версии пакета обновления

function updatePackage(pkgRoot, version) {
  const pkgPath = path.resolve(pkgRoot, 'package.json')
  const pkg = JSON.parse(fs.readFileSync(pkgPath, 'utf-8'))
  pkg.version = version
  updateDeps(pkg, 'dependencies', version)
  updateDeps(pkg, 'peerDependencies', version)
  fs.writeFileSync(pkgPath, JSON.stringify(pkg, null, 2) + '\n')
}

В основном это три модификации.

1. 自己本身 package.json 的版本号
2. packages.json 中 dependencies 中 vue 相关的依赖修改
3. packages.json 中 peerDependencies 中 vue 相关的依赖修改

Одна картинка стоит тысячи слов. мы выступаемyarn release --dryЗаднийgit diffПросмотреноgitИзменено, некоторые скриншоты выглядят следующим образом.

更新的版本号举例

4.4.2 updateDeps обновляет номер версии внутренних зависимостей, связанных с vue.

function updateDeps(pkg, depType, version) {
  const deps = pkg[depType]
  if (!deps) return
  Object.keys(deps).forEach(dep => {
    if (
      dep === 'vue' ||
      (dep.startsWith('@vue') && packages.includes(dep.replace(/^@vue\//, '')))
    ) {
      console.log(
        chalk.yellow(`${pkg.name} -> ${depType} -> ${dep}@${version}`)
      )
      deps[dep] = version
    }
  })
}

Одна картинка стоит тысячи слов. Выполняем в терминалеyarn release --dry. Вы увидите, что это выход.

更新 Vue 相关依赖的终端输出

Это вывод этого кода.

console.log(
  chalk.yellow(`${pkg.name} -> ${depType} -> ${dep}@${version}`)
)

4.5 Упаковать и скомпилировать все пакеты

// build all packages with types
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)`)
}

4.6 Создать журнал изменений

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

yarn changelogСоответствующий скрипт естьconventional-changelog -p angular -i CHANGELOG.md -s.

4.7 Отправить код

После обновления номера версии есть изменения в файлах, поэтомуgit diff. Есть ли изменения в файлах и есть ли коммиты.

git add -A git commit -m 'release: v${targetVersion}'

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

4.8 Релизный пакет

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

Эта функция относительно длинная, поэтому внимательно ее рассматривать не нужно, проще говоря, этоyarn publishпакет выпуска. мыyarn release --dryПосле этого вывод этой функции в терминал выглядит следующим образом:

发布终端输出命令

Стоит упомянуть, что еслиvueПо умолчанию естьtagзаnext. когдаVue 3.xудаляется по умолчанию.

} else if (pkgName === 'vue') {
  // TODO remove when 3.x becomes default
  releaseTag = 'next'
}

Вот почему мы сейчас устанавливаемvue3все ещеnpm i vue@nextЗаказ.

async function publishPackage(pkgName, version, runIfNotDry) {
  // 如果在 跳过包里 则跳过
  if (skippedPackages.includes(pkgName)) {
    return
  }
  const pkgRoot = getPkgRoot(pkgName)
  const pkgPath = path.resolve(pkgRoot, 'package.json')
  const pkg = JSON.parse(fs.readFileSync(pkgPath, 'utf-8'))
  if (pkg.private) {
    return
  }

  // For now, all 3.x packages except "vue" can be published as
  // `latest`, whereas "vue" will be published under the "next" tag.
  let releaseTag = null
  if (args.tag) {
    releaseTag = args.tag
  } else if (version.includes('alpha')) {
    releaseTag = 'alpha'
  } else if (version.includes('beta')) {
    releaseTag = 'beta'
  } else if (version.includes('rc')) {
    releaseTag = 'rc'
  } else if (pkgName === 'vue') {
    // TODO remove when 3.x becomes default
    releaseTag = 'next'
  }

  // TODO use inferred release channel after official 3.0 release
  // const releaseTag = semver.prerelease(version)[0] || null

  step(`Publishing ${pkgName}...`)
  try {
    await runIfNotDry(
      'yarn',
      [
        'publish',
        '--new-version',
        version,
        ...(releaseTag ? ['--tag', releaseTag] : []),
        '--access',
        'public'
      ],
      {
        cwd: pkgRoot,
        stdio: 'pipe'
      }
    )
    console.log(chalk.green(`Successfully published ${pkgName}@${version}`))
  } catch (e) {
    if (e.stderr.match(/previously published/)) {
      console.log(chalk.red(`Skipping already published: ${pkgName}`))
    } else {
      throw e
    }
  }
}

4.9 Отправить на гитхаб

// push to GitHub
step('\nPushing to GitHub...')
// 打 tag
await runIfNotDry('git', ['tag', `v${targetVersion}`])
// 推送 tag
await runIfNotDry('git', ['push', 'origin', `refs/tags/v${targetVersion}`])
// git push 所有改动到 远程  - github
await runIfNotDry('git', ['push'])
// yarn run release --dry

// 如果传了这个参数则输出 可以用 git diff 看看更改

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

// 如果 跳过的包,则输出以下这些包没有发布。不过代码 `skippedPackages` 里是没有包。
// 所以这段代码也不会执行。
// 我们习惯写 arr.length !== 0 其实 0 就是 false 。可以不写。
if (skippedPackages.length) {
  console.log(
    chalk.yellow(
      `The following packages are skipped and NOT published:\n- ${skippedPackages.join(
        '\n- '
      )}`
    )
  )
}
console.log()

мыyarn release --dryПосле этого вывод этой функции в терминал выглядит следующим образом:

发布到github

Здесь будем разбирать и анализироватьmainфункция.

Весь процесс понятен.

1. 确认要发布的版本
2. 执行测试用例
3. 更新所有包的版本号和内部 vue 相关依赖版本号
    3.1 updatePackage 更新包的版本号
    3.2 updateDeps 更新内部 vue 相关依赖的版本号
4. 打包编译所有包
5. 生成 changelog
6. 提交代码
7. 发布包
8. 推送到 github

Резюме с картинкой:

vue 发布流程

прочитай этоvue-next/scripts/release.js, вы также можете посмотреть, если вы заинтересованыvue-next/scriptsДругие коды в папке имеют относительно мало строк, но преимущества относительно велики.

5. Резюме

Изучив эту статью, мы узнали об этом.

1. 熟悉 vuejs 发布流程
2. 学会调试 nodejs 代码
3. 动手优化公司项目发布流程

В то же время рекомендуется использовать его самостоятельноVSCodeБольше отладки, больше исполнения в терминале, больше понимания и переваривания.

vuejsМы можем напрямую копировать и вставлять множество кодов в опубликованные файлы, чтобы оптимизировать собственный процесс публикации. Например, при написании небольшой программы относительно вероятно, что она будет часто выпускаться, и вы можете использовать этот набор кода для взаимодействия сminiprogram-ci, а также некоторые настройки для оптимизации.

О мини-программахciЗагрузите и поделитесь еще двумя статьями.

Непрерывное построение апплета WeChat на основе CI

Инженерная практика автоматического построения и выпуска апплета малой перфокартыХотя статья не последняяminiprogram-ci, но эта сцена написана более развернуто.

Конечно, выпуск версии также может использовать открытый исходный код.release-it.

При этом мы можем:

вводитьgit flow,управлятьgitфилиал. Я думаю, что многие люди не знаютwindows git bashуже поддерживается по умолчаниюgit flowЗаказ.

вводитьhuskyиlint-stagedОтправитьcommitвремя использоватьESLintДождитесь отправки кода подтверждения для прохождения теста.

Внедрение модульных тестовjest, тестировать ключевые служебные функции и т. д.

вводитьconventional-changelog

вводитьgit-czинтерактивныйgit commit.

И так далее, чтобы стандартизировать процесс вашего собственного проекта. Если кандидат, глядя наvuejsВыпускают исходный код, активно оптимизируют собственные проекты. Думаю, интервьюер увидит в этом кандидате бонус.

Преимущество просмотра исходного кода проекта с открытым исходным кодом в том, что, с одной стороны, он может расширить кругозор, а с другой стороны, его можно использовать самому, и доход относительно высок.

Наконец, добро пожаловать, чтобы добавить меня на WeChatruochuan12общаться, участвоватьЧитайте исходный код вместеДействия, все вместе изучают исходный код и вместе добиваются прогресса.


о

Автор: Чанг ИВакагаваНазвание смешано в реках и озерах. По дороге на фронт | Энтузиасты РРТ | Знаю очень мало, только хорошо учусь.
Публичный аккаунт Wakagawa Vision, стремящийся помочь развитию фронтенда в течение 5 лет
Блог Вакагавы
segmentfaultКолонна Вакагава Видение, открылВидение ВакагаваКолонка, добро пожаловать на внимание~
Колонка самородков, добро пожаловать, обратите внимание~
Колонна видений Чжиху Руочуань, открылВидение ВакагаваКолонка, добро пожаловать на внимание~
github blog, спроситьstar^_^~