前言

上文介绍了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来操作文件。


看见了
876 声望16 粉丝

前端开发,略懂后台;


引用和评论

0 条评论