App下載

vue-cli-server使用機制經(jīng)驗分享!

猿友 2021-06-24 16:45:24 瀏覽數(shù) (3342)
反饋

在開始接觸前端學習時相信不少的小伙伴都是在考慮要先學那個款架了,那么今天小編就來說說有關于“vue-cli-server使用機制”內容吧!下面是小編收集整理的相關內容希望對大家有所幫助!


一、入口

我們可以在package.json中看到npm run dev其實就是?vue-cli-service serve?,我們在安裝?vue-cli?的時候可以一并將我們的?vue-cli-service?一同安裝,然后執(zhí)行?npm install vue-cli-service --save-dev?這樣就可以在?./node_modules/.bin?目錄下查看到?vue-cli-service?,相關代碼如下:

@IF EXIST "%~dp0\node.exe" (
  "%~dp0\node.exe"  "%~dp0\..\@vue\cli-service\bin\vue-cli-service.js" %*
) ELSE (
  @SETLOCAL
  @SET PATHEXT=%PATHEXT:;.JS;=;%
  node  "%~dp0\..\@vue\cli-service\bin\vue-cli-service.js" %*
)

    

入口路徑:?./node_modules/@vue/cli-service/bin/vue-cli-service.js?。

我們還可以去看下我們的?vue-cli-service.js?核心代碼,如下所示:

const Service = require('../lib/Service')
// 實例化Service
// VUE_CLI_CONTEXT為undefined,所以傳入的值為process.cwd()及項目所在目錄
const service = new Service(process.env.VUE_CLI_CONTEXT || process.cwd())
const rawArgv = process.argv.slice(2)
// minimist解析工具來對命令行參數(shù)進行解析
const args = require('minimist')(rawArgv, {
  boolean: [
    // build
    'modern',
    'report',
    'report-json',
    'watch',
    // serve
    'open',
    'copy',
    'https',
    // inspect
    'verbose'
  ]
})
// { 
//     _: [ 'serve' ],
//     modern: false,
//     report: false,
//     'report-json': false,
//     watch: false,
//     open: false,
//     copy: false,
//     https: false,
//     verbose: false ,
// } 
const command = args._[0]
// 執(zhí)行service方法傳入:'serve'、agrs、['serve','--open',...]
service.run(command, args, rawArgv).catch(err => {
  error(err)
  process.exit(1)
})

二、初始化配置

代碼如下:

module.exports = class Service {
  constructor (context, { plugins, pkg, inlineOptions, useBuiltIn } = {}) {
    process.VUE_CLI_SERVICE = this
    this.initialized = false
    this.context = context
    this.inlineOptions = inlineOptions
    this.webpackChainFns = []
    this.webpackRawConfigFns = []
    this.devServerConfigFns = []
    this.commands = {}
    this.pkgContext = context
    // 獲取package.json中的依賴
    this.pkg = this.resolvePkg(pkg)
    
    // 如果有內聯(lián)插件,不使用package.json中找到的插件
    // 最終得到的plugins為內置插件+@vue/cli-plugin-*
    // {id: 'xxx',apply: require('xxx')}
    this.plugins = this.resolvePlugins(plugins, useBuiltIn)

    // 解析每個命令使用的默認模式
    //{ serve: 'development',
    // build: 'production',
    // inspect: 'development' }
    this.modes = this.plugins.reduce((modes, { apply: { defaultModes }}) => {
      return Object.assign(modes, defaultModes)
    }, {})
  }
  
  // ......
}

我們通過獲取package.json中的依賴,通過read-pkg這個包來讀取我們的package.json文件,并且通過json格式返回賦值給我們的this.pkg;之后初始化相關的插件。代碼如下:

   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/dev',
         './config/prod',
         './config/app'
       ].map(idToPlugin)

       if (inlinePlugins) {
         // ...
       } else {
           // 開發(fā)環(huán)境依賴+生產(chǎn)環(huán)境的依賴中,獲取cli-plugin-*插件
         const projectPlugins = Object.keys(this.pkg.devDependencies || {})
           .concat(Object.keys(this.pkg.dependencies || {}))
           .filter(isPlugin)    // 驗證是否為vue-cli插件 cli-plugin-*
           .map(idToPlugin)
         plugins = builtInPlugins.concat(projectPlugins)
       }
     
     // ...
   }

1、通過初始化模式,從而解析每個命令,代碼如下:

   // 解析每個命令使用的默認模式
   //{ serve: 'development',
   // build: 'production',
   // inspect: 'development' }
   this.modes = this.plugins.reduce((modes, { apply: { defaultModes }}) => {
         return Object.assign(modes, defaultModes)
   }, {})

之后在內聯(lián)插件默認到處?defaultModes?這個文件如下所示:

   // serve.js
   module.exports.defaultModes = {
     serve: 'development'
   }

三、解析命令參數(shù)

我們在這邊通過?minimist?解析工具來對命令行參數(shù)進行解析,如下所示:

const args = require('minimist')(rawArgv, {
  boolean: [
    // build
    // serve
    'open',
    'copy',
    'https',
  ]
})

// 'serve'、agrs、['serve','--open',...]

四、加載插件

我們可以通過運行service.run方法,加載環(huán)境變量,加載用戶配置,應用插件,代碼如下:

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

    // 加載環(huán)境變量,加載用戶配置,應用插件
    this.init(mode)
  
    args._ = args._ || []
      // 注冊完的命令集里獲取對應的命令
    let command = this.commands[name]
    // ....
    
    const { fn } = command
    return fn(args, rawArgv)
}

1、加載環(huán)境變量,我們從項目中的根目錄下的?.env.(mode)?文件讀取環(huán)境變量,并寫入到?process.env?(聲明環(huán)境變量必須是VUE_APP_*,不然會被過濾掉)代碼如下:

init (mode = process.env.VUE_CLI_MODE) {
    // 加載development.env環(huán)境變量
    if (mode) {
      this.loadEnv(mode)
    }
    this.loadEnv()
    // ...
}
// 加載環(huán)境變量方法(部分)
loadEnv (mode) {
    // 項目路徑/.env.development
    const basePath = path.resolve(this.context, `.env${mode ? `.${mode}` : ``}`)
    // 項目路徑/.env.development.local
    const localPath = `${basePath}.local`

    const load = path => {
        const env = dotenv.config({ path, debug: process.env.DEBUG })
        // 這里會先調用 dotenv(位于 node_modules/dotenv )的 config 函數(shù),最終會返回這樣的格式 { parsed: { YOUR_ENV_KEY: '你設定的環(huán)境變量值' } },并且寫入到process.env里面
        dotenvExpand(env)
        logger(path, env)
    }
    load(localPath)
    load(basePath)
  
    if (mode) {
      const defaultNodeEnv = (mode === 'production' || mode === 'test')
        ? mode
        : 'development'
      if (shouldForceDefaultEnv || process.env.NODE_ENV == null) {
        process.env.NODE_ENV = defaultNodeEnv
      }
    }
  }

2、加載用戶配置

我們這個步驟通過讀取vue.config.js文件內部的配置就可以了,代碼如下:

init (mode = process.env.VUE_CLI_MODE) {
    // 加載用戶配置
    const userOptions = this.loadUserOptions()
    this.projectOptions = defaultsDeep(userOptions, defaults())
}

3、應用插件

//const idToPlugin = id => ({
//    id: id.replace(/^.\//, 'built-in:'),
//    apply: require(id)
//})
init (mode = process.env.VUE_CLI_MODE) {
    // 應用插件
    this.plugins.forEach(({ id, apply }) => {
      apply(new PluginAPI(id, this), this.projectOptions)
    })

    // 從項目配置文件中應用webpack配置
    if (this.projectOptions.chainWebpack) {
      this.webpackChainFns.push(this.projectOptions.chainWebpack)
    }
    if (this.projectOptions.configureWebpack) {
      this.webpackRawConfigFns.push(this.projectOptions.configureWebpack)
    }
  }
}

通過前面初始化插件的時候(resolvePlugins方法)有一個apply屬性,調用這個方法就可以導入對應的插件,這里就是調用了serve.js的方法。


4、注冊命令

我們在在service注冊serve命令,代碼如下:

// serve.js
api.registerCommand('serve', {
    description: 'start development server',
    usage: 'vue-cli-service serve [options] [entry]',
    options: {}
  }, async function serve (args) {
   // ...
});

五、配置啟動服務

最后我們通過執(zhí)行的serve.js 內注冊serve時傳遞的方法。然后等webpack?獲取到配置之后,在通過實例化Compiler 傳遞給webpackDevServer,通過webpackDevServer實現(xiàn)自動編譯和更新,代碼如下:

// serve.js serve函數(shù)
async function serve (args) {
  //創(chuàng)建webpack編譯器
  const compiler = webpack(webpackConfig)
  // compiler.run()即可完成一次編譯打包

  // 創(chuàng)建本地服務
  const server = new WebpackDevServer(compiler, Object.assign({
      clientLogLevel: 'none',
      historyApiFallback: {
        disableDotRule: true,
        rewrites: [
          { from: /./, to: path.posix.join(options.baseUrl, 'index.html') }
        ]
      },
      contentBase: api.resolve('public'),
      watchContentBase: !isProduction,
      hot: !isProduction,
      quiet: true,
      compress: isProduction,
      publicPath: options.baseUrl,
      overlay: isProduction // TODO disable this
        ? false
        : { warnings: false, errors: true }
    }, projectDevServerOptions, {
      https: useHttps,
      proxy: proxySettings,
  }))
  
  return new Promise((resolve, reject) => {
    compiler.hooks.done.tap('vue-cli-service serve', stats => {
        // ...
    })
    // ...
    server.listen(port, host, err => {
        if (err) {
          reject(err)
        }
    })
  })
}

        

我們在這里獲取了?webpack?配置:?api.resolveWebpackConfig()?,之后在通過獲取 ?devServer? 配置 注入 ?webpack-dev-server ?和 ?hot-reload(HRM)?的中間件入口從而創(chuàng)建? webpack-dev-server? 實例 ,而且在啟動webpack-dev-server后,在目標文件夾中是看不到編譯后的文件的,實時編譯后的文件都保存到了內存當中。


總結:

以上就是有關于“vue-cli-server使用機制”的相關內容希望對大家有所幫助當然如果你有更好的方法也可以和大家一起分享了解,更多有關于vue這方面的問題和相關知識我們都可以在W3cschool中進行學習和了解。


0 人點贊