前言

上文介绍了vue create的大抵过程,上面要介绍vue addvue invoke的执行过程,其实vue add实质上还是执行vue invoke,只不过多了插件装置的相干逻辑而已

vue create

如果你想在一个曾经被创立好的我的项目中装置一个插件,能够应用 vue add 命令:
program  .command('add <plugin> [pluginOptions]')  .description('install a plugin and invoke its generator in an already created project')  .option('--registry <url>', 'Use specified npm registry when installing dependencies (only for npm)')  .allowUnknownOption()  .action((plugin) => {        require('../lib/add')(plugin, minimist(process.argv.slice(3)))  })
  • plugin: 装置的插件名称
  • minimist(process.argv.slice(3)): pluginOptions对象

add.js

文件门路: ``

module.exports = (...args) => {  return add(...args).catch(err => {    error(err)    if (!process.env.VUE_CLI_TEST) {      process.exit(1)    }  })}
  • add func
/**  * @param pluginToAdd 待增加插件名称  * @param pluginToAdd 额定参数  * @param context 命令行执行门路  */async function add (pluginToAdd, options = {}, context = process.cwd()) {  if (!(await confirmIfGitDirty(context))) {    return  }  // for `vue add` command in 3.x projects  const servicePkg = loadModule('@vue/cli-service/package.json', context)  if (servicePkg && semver.satisfies(servicePkg.version, '3.x')) {    // special internal "plugins"    if (/^(@vue\/)?router$/.test(pluginToAdd)) {      return addRouter(context)    }    if (/^(@vue\/)?vuex$/.test(pluginToAdd)) {      return addVuex(context)    }  }  const pluginRe = /^(@?[^@]+)(?:@(.+))?$/  const [    // eslint-disable-next-line    _skip,    pluginName,    pluginVersion  ] = pluginToAdd.match(pluginRe)  const packageName = resolvePluginId(pluginName)  log()  log(`  Installing ${chalk.cyan(packageName)}...`)  log()  const pm = new PackageManager({ context })  if (pluginVersion) {    await pm.add(`${packageName}@${pluginVersion}`)  } else if (isOfficialPlugin(packageName)) {    const { latestMinor } = await getVersions()    await pm.add(`${packageName}@~${latestMinor}`)  } else {    await pm.add(packageName, { tilde: true })  }  log(`${chalk.green('✔')}  Successfully installed plugin: ${chalk.cyan(packageName)}`)  log()  const generatorPath = resolveModule(`${packageName}/generator`, context)  if (generatorPath) {    invoke(pluginName, options, context)  } else {    log(`Plugin ${packageName} does not have a generator to invoke`)  }}

首先执行confirmIfGitDirty办法,它会判断是否为执行命令行门路是否为git仓库,不是则继续执行上面流程,如果是则判断是git仓库,则判断仓库状态是否已提交,如果没有提交则提醒用户。
接下来则是判断@vue/cli-service是否为3.x版本,如果是,则再判断是否为vue add routervue add vuex去执行装置插件步骤,不过4.0版本开始,routervuex这个2个cli插件被拆分成@vue/cli-plugin-router@vue/cli-plugin-vuex,不作为外部的非凡插件。理解即可。
而后就是通过resolvePluginId获取残缺的插件名称。通过上文的提到的PackageManager实例pm来下载插件。接着通过resolveModule办法动态创建require语句来引入插件的generator文件夹。若存在该门路,则间接调用invoke办法执行插件的generator逻辑。而invoke正是vue invoke命令调用的执行函数。

vue-cli插件相干文档

插件的 Generator 局部通常在你想要为我的项目扩大包依赖,创立新的文件或者编辑曾经存在的文件时须要。

在 CLI 插件外部,generator 应该放在 generator.js 或者 generator/index.js 文件中。它将在以下两个场景被调用:

  • 我的项目初始创立期间,CLI 插件被作为我的项目创立 preset 的一部分被装置时。
  • 当插件在我的项目创立实现和通过 vue add 或者 vue invoke 独自调用被装置时。

具体更多请查看官网

vue invoke

module.exports = (...args) => {  return invoke(...args).catch(err => {    error(err)    if (!process.env.VUE_CLI_TEST) {      process.exit(1)    }  })}
async function runGenerator (context, plugin, pkg = getPkg(context)) {  const isTestOrDebug = process.env.VUE_CLI_TEST || process.env.VUE_CLI_DEBUG  const afterInvokeCbs = []  const afterAnyInvokeCbs = []  const generator = new Generator(context, {    pkg,    plugins: [plugin],    files: await readFiles(context),    afterInvokeCbs,    afterAnyInvokeCbs,    invoking: true  })  log()  log(`  Invoking generator for ${plugin.id}...`)  await generator.generate({    extractConfigFiles: true,    checkExisting: true  })  const newDeps = generator.pkg.dependencies  const newDevDeps = generator.pkg.devDependencies  const depsChanged =    JSON.stringify(newDeps) !== JSON.stringify(pkg.dependencies) ||    JSON.stringify(newDevDeps) !== JSON.stringify(pkg.devDependencies)  if (!isTestOrDebug && depsChanged) {    log(`  Installing additional dependencies...`)    log()    const pm = new PackageManager({ context })    await pm.install()  }  if (afterInvokeCbs.length || afterAnyInvokeCbs.length) {    logWithSpinner('⚓', `Running completion hooks...`)    for (const cb of afterInvokeCbs) {      await cb()    }    for (const cb of afterAnyInvokeCbs) {      await cb()    }    stopSpinner()    log()  }  log(`${chalk.green('✔')}  Successfully invoked generator for plugin: ${chalk.cyan(plugin.id)}`)  const changedFiles = getChangedFiles(context)  if (changedFiles.length) {    log(`   The following files have been updated / added:\n`)    log(chalk.red(changedFiles.map(line => `     ${line}`).join('\n')))    log()    log(      `   You should review these changes with ${chalk.cyan(        'git diff'      )} and commit them.`    )    log()  }  generator.printExitLogs()}

实质上还是生成Generator实例,并调用generator来操作文件。