上一篇已經講了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步驟,前面的加載省略了。