上一篇已经讲了create命令;
那么这一篇咱们来看一下add和invoke这个命令。之因此放一块儿讲,是由于当add执行的时候,也会去执行invokevue
vue add vue-cli-plugin-xxx 或 vue add @vue/xxx
经过这种形式就是vue-cli3.0内部能识别的插件了
首先来看一下入口vuex
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))) })
入口比较简单,接下来咱们来看一下add.js文件vue-cli
async function add (pluginName, options = {}, context = process.cwd()) { // special internal "plugins" // 这边对@vue/router和@vue/vuex这2个插件作特殊处理,直接从cli-service下拉模块 if (/^(@vue\/)?router$/.test(pluginName)) { return addRouter(context) } if (/^(@vue\/)?vuex$/.test(pluginName)) { return addVuex(context) } const packageName = resolvePluginId(pluginName) // 解析插件名 log() log(`📦 Installing ${chalk.cyan(packageName)}...`) log() const packageManager = loadOptions().packageManager || (hasProjectYarn(context) ? 'yarn' : 'npm') // 是用什么安装 npm、yarn await installPackage(context, packageManager, options.registry, packageName) // 开始安装插件 log(`${chalk.green('✔')} Successfully installed plugin: ${chalk.cyan(packageName)}`) log() const generatorPath = resolveModule(`${packageName}/generator`, context) // 解析路径 // 开始加载插件下面的generator if (generatorPath) { invoke(pluginName, options, context) } else { log(`Plugin ${packageName} does not have a generator to invoke`) } }
这边也比较简单一目了然。npm
async function addRouter (context) { const inquirer = require('inquirer') const options = await inquirer.prompt([{ name: 'routerHistoryMode', type: 'confirm', message: `Use history mode for router? ${chalk.yellow(`(Requires proper server setup for index fallback in production)`)}` }]) invoke.runGenerator(context, { id: 'core:router', apply: loadModule('@vue/cli-service/generator/router', context), options }) } async function addVuex (context) { invoke.runGenerator(context, { id: 'core:vuex', apply: loadModule('@vue/cli-service/generator/vuex', context) }) }
这2个就是单独添加router和vuexjson
exports.resolvePluginId = id => { // already full id // e.g. vue-cli-plugin-foo, @vue/cli-plugin-foo, @bar/vue-cli-plugin-foo if (pluginRE.test(id)) { return id } // scoped short // e.g. @vue/foo, @bar/foo if (id.charAt(0) === '@') { const scopeMatch = id.match(scopeRE) if (scopeMatch) { const scope = scopeMatch[0] const shortId = id.replace(scopeRE, '') return `${scope}${scope === '@vue/' ? `` : `vue-`}cli-plugin-${shortId}` } } // default short // e.g. foo return `vue-cli-plugin-${id}` }
将@vue/xxx的形状解析为vue-cli-plugin-xxxapp
这边的主要流程就是安装插件并注入invokeasync
一样咱们先来看一看入口ui
program .command('invoke <plugin> [pluginOptions]') .description('invoke the generator of a plugin in an already created project') .option('--registry <url>', 'Use specified npm registry when installing dependencies (only for npm)') .allowUnknownOption() .action((plugin) => { require('../lib/invoke')(plugin, minimist(process.argv.slice(3))) })
在add中的代码与入口调用是同样的,都是经过调用invoke.jsurl
invoke(pluginName, options, context)
那么就来看看invoke.js内部是怎么实现的,主要就是分为如下几步插件
const pkg = getPkg(context) // package文件 // attempt to locate the plugin in package.json const findPlugin = deps => { if (!deps) return let name // official if (deps[(name = `@vue/cli-plugin-${pluginName}`)]) { return name } // full id, scoped short, or default short if (deps[(name = resolvePluginId(pluginName))]) { return name } } const id = findPlugin(pkg.devDependencies) || findPlugin(pkg.dependencies) // 在devDependencies和dependencies依赖中寻找vue-cli插件 if (!id) { throw new Error( `Cannot resolve plugin ${chalk.yellow(pluginName)} from package.json. ` + `Did you forget to install it?` ) }
以上验证是否存在package.json文件,以及package文件内是否安装了vue-cli插件
const pluginGenerator = loadModule(`${id}/generator`, context) // 加载插件下的generator文件 if (!pluginGenerator) { throw new Error(`Plugin ${id} does not have a generator.`) } // resolve options if no command line options (other than --registry) are passed, // and the plugin contains a prompt module. // eslint-disable-next-line prefer-const let { registry, ...pluginOptions } = options if (!Object.keys(pluginOptions).length) { let pluginPrompts = loadModule(`${id}/prompts`, context) // 加载插件下的prompts,对话 if (pluginPrompts) { if (typeof pluginPrompts === 'function') { pluginPrompts = pluginPrompts(pkg) } if (typeof pluginPrompts.getPrompts === 'function') { pluginPrompts = pluginPrompts.getPrompts(pkg) } pluginOptions = await inquirer.prompt(pluginPrompts) } }
以上就是加载了generator和prompts,用来运行插件的一些内置代码
const generator = new Generator(context, { pkg, plugins: [plugin], files: await readFiles(context), completeCbs: createCompleteCbs, invoking: true })
这边的跟create中同样效果
router和vuex是直接到Generator步骤,前面的加载省略了。