前言
上文介绍了vue create的大抵过程,上面要介绍vue add
和vue 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 router
或vue add vuex
去执行装置插件步骤,不过4.0版本开始,router
和vuex
这个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
来操作文件。