vue-cli3.0源碼分析@vue/cli-----add和invoke

上一篇已經講了create命令;
那麼這一篇咱們來看一下add和invoke這個命令。之因此放一塊兒講,是由於當add執行的時候,也會去執行invokevue

add

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

invoke

一樣咱們先來看一看入口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內部是怎麼實現的,主要就是分爲如下幾步插件

  1. 信息驗證
  2. 加載插件信息generator/prompts
  3. 運行Generator

信息驗證:

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,用來運行插件的一些內置代碼

Generator

const generator = new Generator(context, {
    pkg,
    plugins: [plugin],
    files: await readFiles(context),
    completeCbs: createCompleteCbs,
    invoking: true
  })

這邊的跟create中同樣效果

最後

router和vuex是直接到Generator步驟,前面的加載省略了。

相關文章
相關標籤/搜索