软件系统开发定制vue-cli-service serve都干了啥(源码探究--@vue/cli 4.5.15)

1、软件系统开发定制我问了自己一个问题?

  • 软件系统开发定制为什么命令行输入 vue-cli-service serve 我们的vue软件系统开发定制项目就能通过http协议访问?

2、软件系统开发定制带着这个问题,软件系统开发定制去看看源码,寻找答案

2.1、vue-cli-service源码
  • 代码片段

    const Service = require('../lib/Service')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',    'inline-vue',    'watch',    // serve    'open',    'copy',    'https',    // inspect    'verbose'  ]})const command = args._[0]service.run(command, args, rawArgv).catch(err => {  error(err)  process.exit(1)})
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
  • 解读

    • 1)、引入Service类,并实例一个service 对象

    • 2)、解析命令行输入参数

      • 当我们 执行 vue-cli-service serve 命令,打印一下参数看看
    • 3)、调用service 对象的run方法,传入解析的命令参数

  • 到这里肯定不行,我们继续去看 Service类的run 方法的源码

2.2、Service.run源码
  • 代码片段

    async run (name, args = {}, rawArgv = []) {    // resolve mode    // prioritize inline --mode    // fallback to resolved default modes from plugins or development if --watch is defined    const mode = args.mode || (name === 'build' && args.watch ? 'development' : this.modes[name])    // --skip-plugins arg may have plugins that should be skipped during init()    this.setPluginsToSkip(args)    // load env variables, load user config, apply plugins    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 || args.h) {      command = this.commands.help    } else {      args._.shift() // remove command itself      rawArgv.shift()    }    const { fn } = command    return fn(args, rawArgv)  }
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
  • 解读

    • 1)、解析模式
    • 2)、设置需要在执行 init 期间,需要跳过处理的插件
    • 3)、加载环境变量、加载用户配置、应用插件
    • 4)、根据name选择需要执行command(我也没想好用啥名字称呼,就叫他command)
    • 5)、处理异常
    • 6)、执行command
  • 看到这里,最后又执行了command,command干了啥呢?

2.3、寻找command
  • 代码片段0(./node_modules/@vue/cli-service/lib/Service.js)

        this.commands = {}    // If there are inline plugins, they will be used instead of those    // found in package.json.    // When useBuiltIn === false, built-in plugins are disabled. This is mostly    // for testing.    this.plugins = this.resolvePlugins(plugins, useBuiltIn)
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
  • 解读

    • 1)、在service类内声明了commands存储command
    • 2)、在service类内声明了plugins存储plugins插件
  • 代码片段1 (./node_modules/@vue/cli-service/lib/Service.js)

    resolvePlugins (inlinePlugins, useBuiltIn) {    const idToPlugin = id => ({      id: id.replace(/^.\//, 'built-in:'),      apply: require(id)    })    let plugins    const builtInPlugins = [      './commands/serve',      './commands/build',      './commands/inspect',      './commands/help',      // config plugins are order sensitive      './config/base',      './config/css',      './config/prod',      './config/app'    ].map(idToPlugin)    if (inlinePlugins) {      plugins = useBuiltIn !== false        ? builtInPlugins.concat(inlinePlugins)        : inlinePlugins    } else {      const projectPlugins = Object.keys(this.pkg.devDependencies || {})        .concat(Object.keys(this.pkg.dependencies || {}))        .filter(isPlugin)        .map(id => {          if (            this.pkg.optionalDependencies &&            id in this.pkg.optionalDependencies          ) {            let apply = () => {}            try {              apply = require(id)            } catch (e) {              warn(`Optional dependency ${id} is not installed.`)            }            return { id, apply }          } else {            return idToPlugin(id)          }        })      plugins = builtInPlugins.concat(projectPlugins)    }    // Local plugins    if (this.pkg.vuePlugins && this.pkg.vuePlugins.service) {      const files = this.pkg.vuePlugins.service      if (!Array.isArray(files)) {        throw new Error(`Invalid type for option 'vuePlugins.service', expected 'array' but got ${typeof files}.`)      }      plugins = plugins.concat(files.map(file => ({        id: `local:${file}`,        apply: loadModule(`./${file}`, this.pkgContext)      })))    }    return plugins  }
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45
    • 46
    • 47
    • 48
    • 49
    • 50
    • 51
    • 52
    • 53
    • 54
    • 55
    • 56
    • 57
    • 58
    • 59
    • 60
    • 61
    • 62
  • 解读

    • 1)、解析内置插件
    • 2)、如果有行内插件,只需解析行内和内置插件行内插件(内置插件包含:内置命令和其他插件)
    • 3)、如果没有行内插件,解析 package.json 中和内置的插件
    • 4)、解析本地的插件
    • 5)、上一步中,将解析的插件存储在plugins上
  • 代码片段2

    init (mode = process.env.VUE_CLI_MODE) {    // apply plugins.    this.plugins.forEach(({ id, apply }) => {      if (this.pluginsToSkip.has(id)) return      apply(new PluginAPI(id, this), this.projectOptions)    })  }
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
  • 解读 (init中部分代码)

    • 1)、使用插件
    • 2)、我们去看下插件内的源码(serve为例)
  • 代码片段3(./node_modules/@vue/cli-service/lib/commands/serve.js)

    module.exports = (api, options) => {  api.registerCommand('serve', {    description: 'start development server',    usage: 'vue-cli-service serve [options] [entry]',    options: {      '--open': `open browser on server start`,      '--copy': `copy url to clipboard on server start`,      '--stdin': `close when stdin ends`,      '--mode': `specify env mode (default: development)`,      '--host': `specify host (default: ${defaults.host})`,      '--port': `specify port (default: ${defaults.port})`,      '--https': `use https (default: ${defaults.https})`,      '--public': `specify the public network URL for the HMR client`,      '--skip-plugins': `comma-separated list of plugin names to skip for this run`    }  }, async function serve (args) { 	......  })}
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
  • 解读

    • 1)、直接调用了第一个参数的registerCommand方法
    • 2)、去看一下registerCommand方法
  • 代码片段4 (./node_modules/@vue/cli-service/lib/PluginAPI.js)

    class PluginAPI {  /**   * @param {string} id - Id of the plugin.   * @param {Service} service - A vue-cli-service instance.   */  constructor (id, service) {    this.id = id    this.service = service  }  registerCommand (name, opts, fn) {    if (typeof opts === 'function') {      fn = opts      opts = null    }    this.service.commands[name] = { fn, opts: opts || {}}  }}
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
  • 解读

    • 1)、从上面部分源码结合片段3可以看出,registerCommand方法将 serve 函数 存入的commands中了
  • 至此,当我们运行serve命令时,找到了 Service.run 函数中执行的 command 了,就是 serve.js 中的 serve 函数

  • 接下来,看看 serve.js 的源码

2.4、serve.js 源码
  • 代码片段
    const defaults = {  host: '0.0.0.0',  port: 8080,  https: false}module.exports = (api, options) => {  api.registerCommand('serve', {    description: 'start development server',    usage: 'vue-cli-service serve [options] [entry]',    options: {      '--open': `open browser on server start`,      '--copy': `copy url to clipboard on server start`,      '--stdin': `close when stdin ends`,      '--mode': `specify env mode (default: development)`,      '--host': `specify host (default: ${defaults.host})`,      '--port': `specify port (default: ${defaults.port})`,      '--https': `use https (default: ${defaults.https})`,      '--public': `specify the public network URL for the HMR client`,      '--skip-plugins': `comma-separated list of plugin names to skip for this run`    }  }, async function serve (args) {      const WebpackDevServer = require('webpack-dev-server')      // create server      const server = new WebpackDevServer(compiler, Object.assign({      logLevel: 'silent',      clientLogLevel: 'silent',      historyApiFallback: {        disableDotRule: true,        rewrites: genHistoryApiFallbackRewrites(options.publicPath, options.pages)      },      contentBase: api.resolve('public'),      watchContentBase: !isProduction,      hot: !isProduction,      injectClient: false,      compress: isProduction,      publicPath: options.publicPath,      overlay: isProduction // TODO disable this        ? false        : { warnings: false, errors: true }    }, projectDevServerOptions, {      https: useHttps,      proxy: proxySettings,      // eslint-disable-next-line no-shadow      before (app, server) {        // launch editor support.        // this works with vue-devtools & @vue/cli-overlay        app.use('/__open-in-editor', launchEditorMiddleware(() => console.log(          `To specify an editor, specify the EDITOR env variable or ` +          `add "editor" field to your Vue project config.`        )))        // allow other plugins to register middlewares, e.g. PWA        api.service.devServerConfigFns.forEach(fn => fn(app, server))        // apply in project middlewares        projectDevServerOptions.before && projectDevServerOptions.before(app, server)      },      // avoid opening browser      open: false    }))  })}
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45
    • 46
    • 47
    • 48
    • 49
    • 50
    • 51
    • 52
    • 53
    • 54
    • 55
    • 56
    • 57
    • 58
    • 59
    • 60
    • 61
    • 62
  • 解读
    • 1)、从代码中可以看出,vue-cli 用了 webpack-dev-server 启动开发服务。

3、回答开始提到的问题

  • 1)、执行 vue-cli-service serve 命令
  • 2)、解析参数行内插件、本地插件、package.json中的插件、内置插件
  • 3)、执行run 方法
  • 4)、init方法内配置插件,webpack配置函数存入webpackChainFns,命令函数存入commands
  • 5)、调用 serve 命令 对应的 command 函数
  • 6)、使用webpack-dev-serve 搭建开发服务器

4、vue-cli-service serve 从运行命令到本地访问流程图解

5、vue-cli-service serve 除了启动一个本地服务器,还做了什么?

  • 代码片段

     // configs that only matters for dev server    api.chainWebpack(webpackConfig => {      if (process.env.NODE_ENV !== 'production' && process.env.NODE_ENV !== 'test') {        webpackConfig          .devtool('eval-cheap-module-source-map')        webpackConfig          .plugin('hmr')            .use(require('webpack/lib/HotModuleReplacementPlugin'))        // https://github.com/webpack/webpack/issues/6642        // https://github.com/vuejs/vue-cli/issues/3539        webpackConfig          .output            .globalObject(`(typeof self !== 'undefined' ? self : this)`)        if (!process.env.VUE_CLI_TEST && options.devServer.progress !== false) {          webpackConfig            .plugin('progress')            .use(require('webpack/lib/ProgressPlugin'))        }      }    })// expose advanced stats    if (args.dashboard) {      const DashboardPlugin = require('../webpack/DashboardPlugin')      ;(webpackConfig.plugins = webpackConfig.plugins || []).push(new DashboardPlugin({        type: 'serve'      }))    }// entry arg    const entry = args._[0]    if (entry) {      webpackConfig.entry = {        app: api.resolve(entry)      }    }
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
  • 解读(serve默认)

    • 1)、配置sorcemap,源码映射

    • 2)、配置hmr,热更新

    • 3)、设置globalObject,全局变量

    • 4)、设置progress,进度条

    • 5)、如果参数有,则设置dashboard,构建过程可视化

    • 6)、设置entry,入口文件

    • 7)、设置webpackDevServer

    • 8)、还会设置用户的

      • ./node_modules/@vue/cli-service/lib/commands/serve.js
      // resolve webpack config    const webpackConfig = api.resolveWebpackConfig()
      • 1
      • 2
      • ./node_modules/@vue/cli-service/lib/PluginAPI.js
      /**   * Resolve the final raw webpack config, that will be passed to webpack.   *   * @param {ChainableWebpackConfig} [chainableConfig]   * @return {object} Raw webpack config.   */  resolveWebpackConfig (chainableConfig) {    return this.service.resolveWebpackConfig(chainableConfig)  }
      • 1
      • 2
      • 3
      • 4
      • 5
      • 6
      • 7
      • 8
      • 9
      • ./node_modules/@vue/cli-service/lib/Service.js
        // apply webpack configs from project config file    if (this.projectOptions.chainWebpack) {      this.webpackChainFns.push(this.projectOptions.chainWebpack)    }  resolveChainableWebpackConfig () {    const chainableConfig = new Config()    // apply chains    this.webpackChainFns.forEach(fn => fn(chainableConfig))    return chainableConfig  }   resolveWebpackConfig (chainableConfig = this.resolveChainableWebpackConfig()) {    if (!this.initialized) {      throw new Error('Service must call init() before calling resolveWebpackConfig().')    }    // get raw config    let config = chainableConfig.toConfig()    const original = config    // apply raw config fns    this.webpackRawConfigFns.forEach(fn => {      if (typeof fn === 'function') {        // function with optional return value        const res = fn(config)        if (res) config = merge(config, res)      } else if (fn) {        // merge literal values        config = merge(config, fn)      }    })    // #2206 If config is merged by merge-webpack, it discards the __ruleNames    // information injected by webpack-chain. Restore the info so that    // vue inspect works properly.    if (config !== original) {      cloneRuleNames(        config.module && config.module.rules,        original.module && original.module.rules      )    }    // check if the user has manually mutated output.publicPath    const target = process.env.VUE_CLI_BUILD_TARGET    if (      !process.env.VUE_CLI_TEST &&      (target && target !== 'app') &&      config.output.publicPath !== this.projectOptions.publicPath    ) {      throw new Error(        `Do not modify webpack output.publicPath directly. ` +        `Use the "publicPath" option in vue.config.js instead.`      )    }    if (      !process.env.VUE_CLI_ENTRY_FILES &&      typeof config.entry !== 'function'    ) {      let entryFiles      if (typeof config.entry === 'string') {        entryFiles = [config.entry]      } else if (Array.isArray(config.entry)) {        entryFiles = config.entry      } else {        entryFiles = Object.values(config.entry || []).reduce((allEntries, curr) => {          return allEntries.concat(curr)        }, [])      }      entryFiles = entryFiles.map(file => path.resolve(this.context, file))      process.env.VUE_CLI_ENTRY_FILES = JSON.stringify(entryFiles)    }    return config  }
      • 1
      • 2
      • 3
      • 4
      • 5
      • 6
      • 7
      • 8
      • 9
      • 10
      • 11
      • 12
      • 13
      • 14
      • 15
      • 16
      • 17
      • 18
      • 19
      • 20
      • 21
      • 22
      • 23
      • 24
      • 25
      • 26
      • 27
      • 28
      • 29
      • 30
      • 31
      • 32
      • 33
      • 34
      • 35
      • 36
      • 37
      • 38
      • 39
      • 40
      • 41
      • 42
      • 43
      • 44
      • 45
      • 46
      • 47
      • 48
      • 49
      • 50
      • 51
      • 52
      • 53
      • 54
      • 55
      • 56
      • 57
      • 58
      • 59
      • 60
      • 61
      • 62
      • 63
      • 64
      • 65
      • 66
      • 67
      • 68
      • 69
      • 70
      • 71
      • 72
      • 73

6、模仿vue-cli-service serve ,使用node动手搭建一个很基础的静态资源服务器

网站建设定制开发 软件系统开发定制 定制软件开发 软件开发定制 定制app开发 app开发定制 app开发定制公司 电商商城定制开发 定制小程序开发 定制开发小程序 客户管理系统开发定制 定制网站 定制开发 crm开发定制 开发公司 小程序开发定制 定制软件 收款定制开发 企业网站定制开发 定制化开发 android系统定制开发 定制小程序开发费用 定制设计 专注app软件定制开发 软件开发定制定制 知名网站建设定制 软件定制开发供应商 应用系统定制开发 软件系统定制开发 企业管理系统定制开发 系统定制开发