vue-cliシリーズのvue-cli-service全体アーキテクチャの概要.

7538 ワード

概要
vueがプロジェクトを開始するときは、npm run serveを実行する必要があります.このserveの内容はvue-cli-service serveです.プロジェクトの開始キーは、このvue-cli-serviceとそのパラメータserveであることがわかります.次に、サービスで主に何が書かれているかを見てみましょう(主な内容は注釈形式でコードに書かれています).
キーコード
vue-cli-service.js
const semver = require('semver')
const { error } = require('@vue/cli-shared-utils')
const requiredVersion = require('../package.json').engines.node

//   node      vue-cli     。           。
if (!semver.satisfies(process.version, requiredVersion)) {
  error(
    `You are using Node ${process.version}, but vue-cli-service ` +
    `requires Node ${requiredVersion}.
Please upgrade your Node version.` ) process.exit(1) } // cli-service 。 const Service = require('../lib/Service') // service 。 。 cli 。 process.cwd() const service = new Service(process.env.VUE_CLI_CONTEXT || process.cwd()) // 。 const rawArgv = process.argv.slice(2) const args = require('minimist')(rawArgv, { boolean: [ // build 'modern', 'report', 'report-json', 'watch', // serve 'open', 'copy', 'https', // inspect 'verbose' ] }) const command = args._[0] // service 。 npm run serve。 command = "serve"。 service.run(command, args, rawArgv).catch(err => { error(err) process.exit(1) })

Service.js
上にserviceのrunメソッドをインスタンス化して呼び出しました.ここでは、コンストラクション関数からrunまでブラウズすればいいです.
const fs = require('fs')
const path = require('path')
const debug = require('debug')
const chalk = require('chalk')
const readPkg = require('read-pkg')
const merge = require('webpack-merge')
const Config = require('webpack-chain')
const PluginAPI = require('./PluginAPI')
const loadEnv = require('./util/loadEnv')
const defaultsDeep = require('lodash.defaultsdeep')
const { warn, error, isPlugin, loadModule } = require('@vue/cli-shared-utils')

const { defaults, validate } = require('./options')

module.exports = class Service {
  constructor (context, { plugins, pkg, inlineOptions, useBuiltIn } = {}) {
    process.VUE_CLI_SERVICE = this
    this.initialized = false
    //           。
    this.context = context
    this.inlineOptions = inlineOptions
    // webpack    。      。          
    this.webpackChainFns = []
    this.webpackRawConfigFns = []
    this.devServerConfigFns = []
    //     。
    this.commands = {}
    // Folder containing the target package.json for plugins
    this.pkgContext = context
    //       pakcage.json  ,      。          
    this.pkg = this.resolvePkg(pkg)
    // **            。**
    this.plugins = this.resolvePlugins(plugins, useBuiltIn)
    
    //    {build: production, serve: development, ... }。               
    //   build          。
    this.modes = this.plugins.reduce((modes, { apply: { defaultModes }}) => {
      return Object.assign(modes, defaultModes)
    }, {})
  }

  init (mode = process.env.VUE_CLI_MODE) {
    if (this.initialized) {
      return
    }
    this.initialized = true
    this.mode = mode

    //   .env      
    if (mode) {
      this.loadEnv(mode)
    }
    // load base .env
    this.loadEnv()

    //          .   vue.config.js
    const userOptions = this.loadUserOptions()
    //                   (       )
    this.projectOptions = defaultsDeep(userOptions, defaults())

    debug('vue:project-config')(this.projectOptions)

    //     。
    this.plugins.forEach(({ id, apply }) => {
      apply(new PluginAPI(id, this), this.projectOptions)
    })

    // wepback      
    if (this.projectOptions.chainWebpack) {
      this.webpackChainFns.push(this.projectOptions.chainWebpack)
    }
    if (this.projectOptions.configureWebpack) {
      this.webpackRawConfigFns.push(this.projectOptions.configureWebpack)
    }
  }


  resolvePlugins (inlinePlugins, useBuiltIn) {
    const idToPlugin = id => ({
      id: id.replace(/^.\//, 'built-in:'),
      apply: require(id)
    })

    let plugins
    
    
    //      。map           {id, apply   }
    //   require(id)   import         。
    //        api 
    // module.exports = (PluginAPIInstance,projectOptions) => {
    //    PluginAPIInstance.registerCommand('cmdName(  npm run serve  serve)', args => {
    //        //           ,          
    //    })
    //    //             
    //}
    //             resolve   。   run->init->      ,      apply  ,
    //             service  。
    const builtInPlugins = [
      './commands/serve',
      './commands/build',
      './commands/inspect',
      './commands/help',
      // config plugins are order sensitive
      './config/base',
      './config/css',
      './config/dev',
      './config/prod',
      './config/app'
    ].map(idToPlugin)
    
    // inlinePlugins  inline   。             ,         ['./commands/serve'...] ,   
    // ['@vue/cli-plugin-babel','@vue/cli-plugin-eslint','@vue/cli-service']。
    //           ,    。
    if (inlinePlugins) {
        //...
    } else {
        //...       
      plugins = builtInPlugins.concat(projectPlugins)
    }

    // Local plugins   package.json        ,      。

    return plugins
  }

  async run (name, args = {}, rawArgv = []) {
    // mode dev  prod?
    const mode = args.mode || (name === 'build' && args.watch ? 'development' : this.modes[name])

    //       、  、    
    this.init(mode)

    args._ = args._ || []
    let command = this.commands[name]
    if (!command && name) {
      error(`command "${name}" does not exist.`)
      process.exit(1)
    }
    if (!command || args.help) {
      command = this.commands.help
    } else {
      args._.shift() // remove command itself
      rawArgv.shift()
    }
    //     。  vue-cli-service serve  ,  serve  。
    const { fn } = command
    return fn(args, rawArgv)
  }

  //   vue.config.js      。        。
  loadUserOptions () {
    //       ,       
    // require(vue.config.js)
    return resolved
  }
}

PluginAPI
ここでは主にpluginの登録とサービスインスタンスを接続しています.抽象化されたコードは次のとおりです.
class PluginAPI {

  constructor (id, service) {
    this.id = id
    this.service = service
  }
  //  service init   
  //        ,     。
  // // apply plugins.
  //    apply           。    PluginAPI         (  vue.config.js)      
  //   PluginAPIInstance.registerCommand  ,      service  。
  //  this.plugins.forEach(({ id, apply }) => {
  //    apply(new PluginAPI(id, this), this.projectOptions)
  //  })
  registerCommand (name, opts, fn) {
    if (typeof opts === 'function') {
      fn = opts
      opts = null
    }
    this.service.commands[name] = { fn, opts: opts || {}}
  }


}

module.exports = PluginAPI

まとめ
vue-cli-serviceのnew Serviceでプラグイン情報をロードし、Serviceインスタンスのplugins変数にキャッシュします.コマンドラインパラメータが得られたら、new Serviceのrunメソッドでコマンドを実行します.このrunメソッドではinitメソッドを呼び出してプロジェクト内の構成情報(デフォルト&ユーザのマージ)を取得する、例えばユーザの構成がvueである.config.jsで.InitプロセスではpluginAPIというクラスを介してserviceとプラグインpluginsを関連付けます.関係はサービスに保存されます.commandsで.最後にcommands[cmdArgName]でこのメソッドを呼び出し,プラグインメソッドの呼び出しを完了した.
初めて読むと、コマンドモードの実際の応用が見えただけです.考えられるのは、新しいプラグインを追加するときは、プラグインのファイルを1つ追加するだけで、他のファイルの論理を変更する必要はありません.他の部分は、もう少しゆっくり味わってみましょう...