vue-cli腳手架中webpack配置基礎檔案詳解

weixin_34253539發表於2018-05-10

一、前言

vue-cli是構建vue單頁應用的腳手架,輸入一串指定的命令列從而自動生成vue.js+wepack的專案模板。這其中webpack發揮了很大的作用,它使得我們的程式碼模組化,引入一些外掛幫我們完善功能可以將檔案打包壓縮,圖片轉base64等。後期對專案的配置使得我們對於腳手架自動生成的程式碼的理解更為重要,接下來我將基於webpack3.6.0版本結合文件將檔案各個擊破,純乾料。
重點章節點選檢視:package.jsonconfig/index.jswebpack.base.conf.jswebpack.dev.conf.jswebpack.prod.conf.js

二、主體結構

├─build 
├─config 
├─dist
├─node_modules
├─src
│ ├─assets
│ ├─components
│ ├─router
│ ├─App.vue
│ ├─main.js
├─static
├─.babelrc
├─.editorconfig
├─.gitignore
├─.postcssrc.js
├─index.html
├─package-lock.json
├─package.json
└─README.md

1、 package.json

專案作為一個大家庭,每個檔案都各司其職。package.json來制定名單,需要哪些npm包來參與到專案中來,npm install命令根據這個配置檔案增減來管理本地的安裝包。

{
//從name到private都是package的配置資訊,也就是我們在腳手架搭建中輸入的專案描述
  "name": "shop",//專案名稱:不能以.(點)或者_(下劃線)開頭,不能包含大寫字母,具有明確的的含義與現有專案名字不重複
  "version": "1.0.0",//專案版本號:遵循“大版本.次要版本.小版本”
  "description": "A Vue.js project",//專案描述
  "author": "qietuniu",//作者名字
  "private": true,//是否私有
  //scripts中的子項即是我們在控制檯執行的指令碼的縮寫
  "scripts": {
   //①webpack-dev-server:啟動了http伺服器,實現實時編譯;
   //inline模式會在webpack.config.js入口配置中新增webpack-dev-server/client?http://localhost:8080/的入口,使得我們訪問路徑為localhost:8080/index.html(相應的還有另外一種模式Iframe);
   //progress:顯示打包的進度
    "dev": "webpack-dev-server --inline --progress --config build/webpack.dev.conf.js",  
    "start": "npm run dev",//與npm run dev相同,直接執行開發環境
    "build": "node build/build.js"//使用node執行build檔案
  },
  //②dependencies(專案依賴庫):在安裝時使用--save則寫入到dependencies
  "dependencies": {
    "vue": "^2.5.2",//vue.js
    "vue-router": "^3.0.1"//vue的路由外掛
  },
  //和devDependencies(開發依賴庫):在安裝時使用--save-dev將寫入到devDependencies
  "devDependencies": {
    "autoprefixer": "^7.1.2",//autoprefixer作為postcss外掛用來解析CSS補充字首,例如 display: flex會補充為display:-webkit-box;display: -webkit-flex;display: -ms-flexbox;display: flex。
    //babel:以下幾個babel開頭的都是針對es6解析的外掛。用最新標準編寫的 JavaScript 程式碼向下編譯成可以在今天隨處可用的版本
    "babel-core": "^6.22.1",//babel的核心,把 js 程式碼分析成 ast ,方便各個外掛分析語法進行相應的處理。
    "babel-helper-vue-jsx-merge-props": "^2.0.3",//預製babel-template函式,提供給vue,jsx等使用
    "babel-loader": "^7.1.1",//使專案執行使用Babel和webpack來傳輸js檔案,使用babel-core提供的api進行轉譯
    "babel-plugin-syntax-jsx": "^6.18.0",//支援jsx
    "babel-plugin-transform-runtime": "^6.22.0",//避免編譯輸出中的重複,直接編譯到build環境中
    "babel-plugin-transform-vue-jsx": "^3.5.0",//babel轉譯過程中使用到的外掛,避免重複
    "babel-preset-env": "^1.3.2",//轉為es5,transform階段使用到的外掛之一
    "babel-preset-stage-2": "^6.22.0",//ECMAScript第二階段的規範
    "chalk": "^2.0.1",//用來在命令列輸出不同顏色文字
    "copy-webpack-plugin": "^4.0.1",//拷貝資源和檔案
    "css-loader": "^0.28.0",//webpack先用css-loader載入器去解析字尾為css的檔案,再使用style-loader生成一個內容為最終解析完的css程式碼的style標籤,放到head標籤裡
    "extract-text-webpack-plugin": "^3.0.0",//將一個以上的包裡面的文字提取到單獨檔案中
    "file-loader": "^1.1.4",//③打包壓縮檔案,與url-loader用法類似
    "friendly-errors-webpack-plugin": "^1.6.1",//識別某些類別的WebPACK錯誤和清理,聚合和優先排序,以提供更好的開發經驗
    "html-webpack-plugin": "^2.30.1",//簡化了HTML檔案的建立,引入了外部資源,建立html的入口檔案,可通過此項進行多頁面的配置
    "node-notifier": "^5.1.2",//支援使用node傳送跨平臺的本地通知
    "optimize-css-assets-webpack-plugin": "^3.2.0",//壓縮提取出的css,並解決ExtractTextPlugin分離出的js重複問題(多個檔案引入同一css檔案)
    "ora": "^1.2.0",//載入(loading)的外掛
    "portfinder": "^1.0.13",//檢視程式埠
    "postcss-import": "^11.0.0",//可以消耗本地檔案、節點模組或web_modules
    "postcss-loader": "^2.0.8",//用來相容css的外掛
    "postcss-url": "^7.2.1",//URL上重新定位、內聯或複製
    "rimraf": "^2.6.0",//節點的UNIX命令RM—RF,強制刪除檔案或者目錄的命令
    "semver": "^5.3.0",//用來對特定的版本號做判斷的
    "shelljs": "^0.7.6",//使用它來消除shell指令碼在UNIX上的依賴性,同時仍然保留其熟悉和強大的命令,即可執行Unix系統命令
    "uglifyjs-webpack-plugin": "^1.1.1",//壓縮js檔案
    "url-loader": "^0.5.8",//壓縮檔案,可將圖片轉化為base64
    "vue-loader": "^13.3.0",//VUE單檔案元件的WebPACK載入器
    "vue-style-loader": "^3.0.1",//類似於樣式載入程式,您可以在CSS載入器之後將其連結,以將CSS動態地注入到文件中作為樣式標籤
    "vue-template-compiler": "^2.5.2",//這個包可以用來預編譯VUE模板到渲染函式,以避免執行時編譯開銷和CSP限制
    "webpack": "^3.6.0",//打包工具
    "webpack-bundle-analyzer": "^2.9.0",//視覺化webpack輸出檔案的大小
    "webpack-dev-server": "^2.9.1",//提供一個提供實時過載的開發伺服器
    "webpack-merge": "^4.1.0"//它將陣列和合並物件建立一個新物件。如果遇到函式,它將執行它們,通過演算法執行結果,然後再次將返回的值封裝在函式中
  },
  //engines是引擎,指定node和npm版本
  "engines": {
    "node": ">= 6.0.0",
    "npm": ">= 3.0.0"
  },
  //限制了瀏覽器或者客戶端需要什麼版本才可執行
  "browserslist": [
    "> 1%",
    "last 2 versions",
    "not ie <= 8"
  ]
}

註釋:
①、點這裡→webpack執行時的配置文件傳送門

②、devDependencies和dependencies的區別: devDependencies裡面的外掛只用於開發環境,不用於生產環境,即輔助作用,打包的時候需要,打包完成就不需要了。而dependencies是需要釋出到生產環境的,自始至終都在。比如wepack等只是在開發中使用的包就寫入到devDependencies,而像vue這種專案全程依賴的包要寫入到dependencies
點這裡→更多安裝包文件搜尋頁傳送門

③、file-loader和url-loader的區別:以圖片為例,file-loader可對圖片進行壓縮,但是還是通過檔案路徑進行引入,當http請求增多時會降低頁面效能,而url-loader通過設定limit引數,小於limit位元組的圖片會被轉成base64的檔案,大於limit位元組的將進行圖片壓縮的操作。總而言之,url-loader是file-loader的上層封裝。
點這裡→file-loader 和 url-loader詳解
點這裡→file-loader文件傳送門
點這裡→url-loader文件傳送門

2、.postcssrc.js

.postcssrc.js檔案其實是postcss-loader包的一個配置,在webpack的舊版本可以直接在webpack.config.js中配置,現版本中postcss的文件示例獨立出.postcssrc.js,裡面寫進去需要使用到的外掛

module.exports = {
  "plugins": {
    "postcss-import": {},//①
    "postcss-url": {},//②
    "autoprefixer": {}//③
  }
}

註釋:
①、點這裡→postcss-import文件傳送門
②、點這裡→postcss-url文件傳送門
③、點這裡→autoprefixer文件傳送門

3、 .babelrc

該檔案是es6解析的一個配置

{
//制定轉碼的規則
  "presets": [
  //env是使用babel-preset-env外掛將js進行轉碼成es5,並且設定不轉碼的AMD,COMMONJS的模組檔案,制定瀏覽器的相容
    ["env", {
      "modules": false,
      "targets": {
        "browsers": ["> 1%", "last 2 versions", "not ie <= 8"]
      }
    }],
    "stage-2"
  ],
  
  "plugins": ["transform-vue-jsx", "transform-runtime"]//①
}

註釋:
①、點這裡→transform-vue-jsx文件傳送門
點這裡→transform-runtime文件傳送門

4、src內檔案

我們開發的程式碼都存放在src目錄下,根據需要我們通常會再建一些資料夾。比如pages的資料夾,用來存放頁面讓components資料夾專門做好元件的工作;api資料夾,來封裝請求的引數和方法;store資料夾,使用vuex來作為vue的狀態管理工具,我也常叫它作前端的資料庫等。


①、assets檔案:腳手架自動會放入一個圖片在裡面作為初始頁面的logo。平常我們使用的時候會在裡面建立js,css,img,fonts等資料夾,作為靜態資源呼叫

②、components資料夾:用來存放元件,合理地使用元件可以高效地實現複用等功能,從而更好地開發專案。一般情況下比如建立頭部元件的時候,我們會新建一個header的資料夾,然後再新建一個header.vue的檔案

③、router資料夾:該資料夾下有一個叫index.js檔案,用於實現頁面的路由跳轉,具體使用請點選→vue-router傳送門

④、App.vue:作為我們的主元件,可通過使用<router-view/>開放入口讓其他的頁面元件得以顯示。

⑤、main.js:作為我們的入口檔案,主要作用是初始化vue例項並使用需要的外掛,小型專案省略router時可放在該處

註釋:具體vue的用法可檢視vue官方中文文件傳送門

5、其他檔案

①、.editorconfig:編輯器的配置檔案

②、.gitignore:忽略git提交的一個檔案,配置之後提交時將不會載入忽略的檔案

③、index.html:頁面入口,經過編譯之後的程式碼將插入到這來。

④、package.lock.json:鎖定安裝時的包的版本號,並且需要上傳到git,以保證其他人在npm install時大家的依賴能保證一致

⑤、README.md:可此填寫專案介紹

⑥、node_modules:根據package.json安裝時候生成的的依賴(安裝包)

三、config資料夾

├─config 
│ ├─dev.env.js 
│ ├─index.js 
│ ├─prod.env.js 

1、config/dev.env.js

config內的檔案其實是服務於build的,大部分是定義一個變數export出去。

'use strict'//採用嚴格模式
const merge = require('webpack-merge')//①
const prodEnv = require('./prod.env')
//webpack-merge提供了一個合併函式,它將陣列和合並物件建立一個新物件。
//如果遇到函式,它將執行它們,通過演算法執行結果,然後再次將返回的值封裝在函式中.這邊將dev和prod進行合併
module.exports = merge(prodEnv, {
  NODE_ENV: '"development"'
})

註釋:①、點這裡→webpack-merge文件傳送門

2、config/prod.env.js

當開發是調取dev.env.js的開發環境配置,釋出時呼叫prod.env.js的生產環境配置

'use strict'
module.exports = {
  NODE_ENV: '"production"'
}

3、config/index.js

'use strict'
const path = require('path')

module.exports = {
  dev: {
    // 開發環境下面的配置
    assetsSubDirectory: 'static',//子目錄,一般存放css,js,image等檔案
    assetsPublicPath: '/',//根目錄
    proxyTable: {},//可利用該屬性解決跨域的問題
    host: 'localhost', // 地址
    port: 8080, //埠號設定,埠號佔用出現問題可在此處修改
    autoOpenBrowser: false,//是否在編譯(輸入命令列npm run dev)後開啟http://localhost:8080/頁面,以前配置為true,近些版本改為false,個人偏向習慣自動開啟頁面
    errorOverlay: true,//瀏覽器錯誤提示
    notifyOnErrors: true,//跨平臺錯誤提示
    poll: false, //使用檔案系統(file system)獲取檔案改動的通知devServer.watchOptions
    devtool: 'cheap-module-eval-source-map',//增加除錯,該屬性為原始原始碼(僅限行)不可在生產環境中使用
    cacheBusting: true,//使快取失效
    cssSourceMap: true//程式碼壓縮後進行調bug定位將非常困難,於是引入sourcemap記錄壓縮前後的位置資訊記錄,當產生錯誤時直接定位到未壓縮前的位置,將大大的方便我們除錯
  },

  build: {
  // 生產環境下面的配置
    index: path.resolve(__dirname, '../dist/index.html'),//index編譯後生成的位置和名字,根據需要改變字尾,比如index.php
    assetsRoot: path.resolve(__dirname, '../dist'),//編譯後存放生成環境程式碼的位置
    assetsSubDirectory: 'static',//js,css,images存放資料夾名
    assetsPublicPath: '/',//釋出的根目錄,通常本地打包dist後開啟檔案會報錯,此處修改為./。如果是上線的檔案,可根據檔案存放位置進行更改路徑
    productionSourceMap: true,
    devtool: '#source-map',//①
    //unit的gzip命令用來壓縮檔案,gzip模式下需要壓縮的檔案的副檔名有js和css
    productionGzip: false,
    productionGzipExtensions: ['js', 'css'],
    bundleAnalyzerReport: process.env.npm_config_report
  }
}

註釋:①點選→devtool文件傳送門

四、build資料夾

├─build 
│ ├─build.js 
│ ├─check-versions.js 
│ ├─utils.js 
│ ├─vue-loader.conf.js 
│ ├─webpack.base.conf.js 
│ ├─webpack.dev.conf.js 
│ ├─webpack.prod.conf.js 

1、build/build.js

該檔案作用,即構建生產版本。package.json中的scripts的build就是node build/build.js,輸入命令列npm run build對該檔案進行編譯生成生產環境的程式碼。

'use strict'
require('./check-versions')()//check-versions:呼叫檢查版本的檔案。加()代表直接呼叫該函式
process.env.NODE_ENV = 'production'//設定當前是生產環境
//下面定義常量引入外掛
const ora = require('ora')//①載入動畫
const rm = require('rimraf')//②刪除檔案
const path = require('path')
const chalk = require('chalk')//③對文案輸出的一個彩色設定
const webpack = require('webpack')
const config = require('../config')//預設讀取下面的index.js檔案
const webpackConfig = require('./webpack.prod.conf')
//呼叫start的方法實現載入動畫,優化使用者體驗
const spinner = ora('building for production...')
spinner.start()
//先刪除dist檔案再生成新檔案,因為有時候會使用hash來命名,刪除整個檔案可避免冗餘
rm(path.join(config.build.assetsRoot, config.build.assetsSubDirectory), err => {
  if (err) throw err
  webpack(webpackConfig, (err, stats) => {
    spinner.stop()
    if (err) throw err
    process.stdout.write(stats.toString({
      colors: true,
      modules: false,
      children: false, // If you are using ts-loader, setting this to true will make TypeScript errors show up during build.
      chunks: false,
      chunkModules: false
    }) + '\n\n')

    if (stats.hasErrors()) {
      process.exit(1)
    }

    console.log(chalk.cyan('  Build complete.\n'))
    console.log(chalk.yellow(
      '  Tip: built files are meant to be served over an HTTP server.\n' +
      '  Opening index.html over file:// won\'t work.\n'
    ))
  })
})

註釋:
①、點這裡→ora文件傳送門
②、點這裡→chalk文件傳送門
③、點這裡→rimraf文件傳送門

2、build/check-version.js

該檔案用於檢測node和npm的版本,實現版本依賴

'use strict'
const chalk = require('chalk')
const semver = require('semver')//①對版本進行檢查
const packageConfig = require('../package.json')
const shell = require('shelljs')

function exec (cmd) {
//返回通過child_process模組的新建子程式,執行 Unix 系統命令後轉成沒有空格的字串
  return require('child_process').execSync(cmd).toString().trim()
}

const versionRequirements = [
  {
    name: 'node',
    currentVersion: semver.clean(process.version),//使用semver格式化版本
    versionRequirement: packageConfig.engines.node//獲取package.json中設定的node版本
  }
]

if (shell.which('npm')) {
  versionRequirements.push({
    name: 'npm',
    currentVersion: exec('npm --version'),// 自動呼叫npm --version命令,並且把引數返回給exec函式,從而獲取純淨的版本號
    versionRequirement: packageConfig.engines.npm
  })
}

module.exports = function () {
  const warnings = []
  for (let i = 0; i < versionRequirements.length; i++) {
    const mod = versionRequirements[i]

    if (!semver.satisfies(mod.currentVersion, mod.versionRequirement)) {
    //上面這個判斷就是如果版本號不符合package.json檔案中指定的版本號,就執行下面錯誤提示的程式碼
      warnings.push(mod.name + ': ' +
        chalk.red(mod.currentVersion) + ' should be ' +
        chalk.green(mod.versionRequirement)
      )
    }
  }

  if (warnings.length) {
    console.log('')
    console.log(chalk.yellow('To use this template, you must update following to modules:'))
    console.log()

    for (let i = 0; i < warnings.length; i++) {
      const warning = warnings[i]
      console.log('  ' + warning)
    }

    console.log()
    process.exit(1)
  }
}

註釋:
①、點這裡→chalk文件傳送門
點這裡→semver文件傳送門

3、build/utils.js

utils是工具的意思,是一個用來處理css的檔案。

'use strict'
const path = require('path')
const config = require('../config')
const ExtractTextPlugin = require('extract-text-webpack-plugin')
const packageConfig = require('../package.json')
//匯出檔案的位置,根據環境判斷開發環境和生產環境,為config檔案中index.js檔案中定義的build.assetsSubDirectory或dev.assetsSubDirectory
exports.assetsPath = function (_path) {
  const assetsSubDirectory = process.env.NODE_ENV === 'production'
    ? config.build.assetsSubDirectory
    : config.dev.assetsSubDirectory
//Node.js path 模組提供了一些用於處理檔案路徑的小工具①
  return path.posix.join(assetsSubDirectory, _path)
}

exports.cssLoaders = function (options) {
  options = options || {}
//使用了css-loader和postcssLoader,通過options.usePostCSS屬性來判斷是否使用postcssLoader中壓縮等方法
  const cssLoader = {
    loader: 'css-loader',
    options: {
      sourceMap: options.sourceMap
    }
  }

  const postcssLoader = {
    loader: 'postcss-loader',
    options: {
      sourceMap: options.sourceMap
    }
  }
  function generateLoaders (loader, loaderOptions) {
    const loaders = options.usePostCSS ? [cssLoader, postcssLoader] : [cssLoader]
    if (loader) {
      loaders.push({
        loader: loader + '-loader',
        //Object.assign是es6語法的淺複製,後兩者合併後複製完成賦值
        options: Object.assign({}, loaderOptions, {
          sourceMap: options.sourceMap
        })
      })
    }
    
    if (options.extract) {
    //ExtractTextPlugin可提取出文字,代表首先使用上面處理的loaders,當未能正確引入時使用vue-style-loader
      return ExtractTextPlugin.extract({
        use: loaders,
        fallback: 'vue-style-loader'
      })
    } else {
    //返回vue-style-loader連線loaders的最終值
      return ['vue-style-loader'].concat(loaders)
    }
  }
  return {
    css: generateLoaders(),//需要css-loader 和 vue-style-loader
    postcss: generateLoaders(),//需要css-loader和postcssLoader  和 vue-style-loader
    less: generateLoaders('less'),//需要less-loader 和 vue-style-loader
    sass: generateLoaders('sass', { indentedSyntax: true }),//需要sass-loader 和 vue-style-loader
    scss: generateLoaders('sass'),//需要sass-loader 和 vue-style-loader
    stylus: generateLoaders('stylus'),//需要stylus-loader 和 vue-style-loader
    styl: generateLoaders('stylus')//需要stylus-loader 和 vue-style-loader
  }
}
exports.styleLoaders = function (options) {
  const output = []
  const loaders = exports.cssLoaders(options)
    //將各種css,less,sass等綜合在一起得出結果輸出output
  for (const extension in loaders) {
    const loader = loaders[extension]
    output.push({
      test: new RegExp('\\.' + extension + '$'),
      use: loader
    })
  }

  return output
}

exports.createNotifierCallback = () => {
//傳送跨平臺通知系統
  const notifier = require('node-notifier')

  return (severity, errors) => {
    if (severity !== 'error') return
//當報錯時輸出錯誤資訊的標題,錯誤資訊詳情,副標題以及圖示
    const error = errors[0]
    const filename = error.file && error.file.split('!').pop()

    notifier.notify({
      title: packageConfig.name,
      message: severity + ': ' + error.name,
      subtitle: filename || '',
      icon: path.join(__dirname, 'logo.png')
    })
  }
}

註釋:
①、path.posix:提供對路徑方法的POSIX(可移植性作業系統介面)特定實現的訪問,即可跨平臺,區別於win32。
path.join:用於連線路徑,會正確使用當前系統的路徑分隔符,Unix系統是"/",Windows系統是""
點選→path用法傳送門

4、vue-loader.conf.js

該檔案的主要作用就是處理.vue檔案,解析這個檔案中的每個語言塊(template、script、style),轉換成js可用的js模組。

'use strict'
const utils = require('./utils')
const config = require('../config')
const isProduction = process.env.NODE_ENV === 'production'
const sourceMapEnabled = isProduction
  ? config.build.productionSourceMap
  : config.dev.cssSourceMap
//處理專案中的css檔案,生產環境和測試環境預設是開啟sourceMap,而extract中的提取樣式到單獨檔案只有在生產環境中才需要
module.exports = {
  loaders: utils.cssLoaders({
    sourceMap: sourceMapEnabled,
    extract: isProduction
  }),
  cssSourceMap: sourceMapEnabled,
  cacheBusting: config.dev.cacheBusting,
   // 在模版編譯過程中,編譯器可以將某些屬性,如 src 路徑,轉換為require呼叫,以便目標資源可以由 webpack 處理.
  transformToRequire: {
    video: ['src', 'poster'],
    source: 'src',
    img: 'src',
    image: 'xlink:href'
  }
}

5、webpack.base.conf.js

webpack.base.conf.js是開發和生產共同使用提出來的基礎配置檔案,主要實現配製入口,配置輸出環境,配置模組resolve和外掛等

'use strict'
const path = require('path')
const utils = require('./utils')
const config = require('../config')
const vueLoaderConfig = require('./vue-loader.conf')

function resolve (dir) {
//拼接出絕對路徑
  return path.join(__dirname, '..', dir)
}
module.exports = {
//path.join將路徑片段進行拼接,而path.resolve將以/開始的路徑片段作為根目錄,在此之前的路徑將會被丟棄
//path.join('/a', '/b') // 'a/b',path.resolve('/a', '/b') // '/b'
  context: path.resolve(__dirname, '../'),
  //配置入口,預設為單頁面所以只有app一個入口
  entry: {
    app: './src/main.js'
  },
  //配置出口,預設是/dist作為目標資料夾的路徑
  output: {
    path: config.build.assetsRoot,//路徑
    filename: '[name].js',//檔名
    publicPath: process.env.NODE_ENV === 'production'
      ? config.build.assetsPublicPath
      : config.dev.assetsPublicPath//公共存放路徑
  },
  resolve: {
  //自動的擴充套件字尾,比如一個js檔案,則引用時書寫可不要寫.js
    extensions: ['.js', '.vue', '.json'],
    //建立路徑的別名,比如增加'components': resolve('src/components')等
    alias: {
      'vue$': 'vue/dist/vue.esm.js',
      '@': resolve('src'),
    }
  },
  //使用外掛配置相應檔案的處理方法
  module: {
    rules: [
    //使用vue-loader將vue檔案轉化成js的模組①
      {
        test: /\.vue$/,
        loader: 'vue-loader',
        options: vueLoaderConfig
      },
      //js檔案需要通過babel-loader進行編譯成es5檔案以及壓縮等操作②
      {
        test: /\.js$/,
        loader: 'babel-loader',
        include: [resolve('src'), resolve('test'), resolve('node_modules/webpack-dev-server/client')]
      },
      //圖片、音像、字型都使用url-loader進行處理,超過10000會編譯成base64③
      {
        test: /\.(png|jpe?g|gif|svg)(\?.*)?$/,
        loader: 'url-loader',
        options: {
          limit: 10000,
          name: utils.assetsPath('img/[name].[hash:7].[ext]')
        }
      },
      {
        test: /\.(mp4|webm|ogg|mp3|wav|flac|aac)(\?.*)?$/,
        loader: 'url-loader',
        options: {
          limit: 10000,
          name: utils.assetsPath('media/[name].[hash:7].[ext]')
        }
      },
      {
        test: /\.(woff2?|eot|ttf|otf)(\?.*)?$/,
        loader: 'url-loader',
        options: {
          limit: 10000,
          name: utils.assetsPath('fonts/[name].[hash:7].[ext]')
        }
      }
    ]
  },
  //以下選項是Node.js全域性變數或模組,這裡主要是防止webpack注入一些Node.js的東西到vue中 
  node: 
    setImmediate: false,
    dgram: 'empty',
    fs: 'empty',
    net: 'empty',
    tls: 'empty',
    child_process: 'empty'
  }
}

註釋:
①、點選→vue-loader文件傳送門
②、點選→babel-loader文件傳送門

6、webpack.dev.conf.js

'use strict'
const utils = require('./utils')
const webpack = require('webpack')
const config = require('../config')
//通過webpack-merge實現webpack.dev.conf.js對wepack.base.config.js的繼承
const merge = require('webpack-merge')
const path = require('path')
const baseWebpackConfig = require('./webpack.base.conf')
const CopyWebpackPlugin = require('copy-webpack-plugin')
const HtmlWebpackPlugin = require('html-webpack-plugin')
//美化webpack的錯誤資訊和日誌的外掛①
const FriendlyErrorsPlugin = require('friendly-errors-webpack-plugin')
const portfinder = require('portfinder')// 檢視空閒埠位置,預設情況下搜尋8000這個埠②
const HOST = process.env.HOST//③processs為node的一個全域性物件獲取當前程式的環境變數,即host
const PORT = process.env.PORT && Number(process.env.PORT)

const devWebpackConfig = merge(baseWebpackConfig, {
  module: {
  //規則是工具utils中處理出來的styleLoaders,生成了css,less,postcss等規則
    rules: utils.styleLoaders({ sourceMap: config.dev.cssSourceMap, usePostCSS: true })
  },

  devtool: config.dev.devtool,  //增強除錯,上文有提及
  //此處的配置都是在config的index.js中設定好了
  devServer: {//④
    clientLogLevel: 'warning',//控制檯顯示的選項有none, error, warning 或者 info
    //當使用 HTML5 History API 時,任意的 404 響應都可能需要被替代為 index.html
    historyApiFallback: {
      rewrites: [
        { from: /.*/, to: path.posix.join(config.dev.assetsPublicPath, 'index.html') },
      ],
    },
    hot: true,//熱載入
    contentBase: false,
    compress: true,//壓縮
    host: HOST || config.dev.host,
    port: PORT || config.dev.port,
    open: config.dev.autoOpenBrowser,//除錯時自動開啟瀏覽器
    overlay: config.dev.errorOverlay
      ? { warnings: false, errors: true }
      : false,// warning 和 error 都要顯示
    publicPath: config.dev.assetsPublicPath,
    proxy: config.dev.proxyTable,//介面代理
    quiet: true, //控制檯是否禁止列印警告和錯誤,若用FriendlyErrorsPlugin 此處為 true
    watchOptions: {
      poll: config.dev.poll,//// 檔案系統檢測改動
    }
  },
  plugins: [
    new webpack.DefinePlugin({
      'process.env': require('../config/dev.env')
    }),
    new webpack.HotModuleReplacementPlugin(),//⑤模組熱替換外掛,修改模組時不需要重新整理頁面
    new webpack.NamedModulesPlugin(), // 顯示檔案的正確名字
    new webpack.NoEmitOnErrorsPlugin(),//當webpack編譯錯誤的時候,來中端打包程式,防止錯誤程式碼打包到檔案中
    // https://github.com/ampedandwired/html-webpack-plugin
    // 該外掛可自動生成一個 html5 檔案或使用模板檔案將編譯好的程式碼注入進去⑥
    new HtmlWebpackPlugin({
      filename: 'index.html',
      template: 'index.html',
      inject: true
    }),
    new CopyWebpackPlugin([//複製外掛
      {
        from: path.resolve(__dirname, '../static'),
        to: config.dev.assetsSubDirectory,
        ignore: ['.*']//忽略.*的檔案
      }
    ])
  ]
})

module.exports = new Promise((resolve, reject) => {
  portfinder.basePort = process.env.PORT || config.dev.port
  //查詢埠號
  portfinder.getPort((err, port) => {
    if (err) {
      reject(err)
    } else {
    //埠被佔用時就重新設定evn和devServer的埠
      process.env.PORT = port
      devWebpackConfig.devServer.port = port
      //友好地輸出資訊
      devWebpackConfig.plugins.push(new FriendlyErrorsPlugin({
        compilationSuccessInfo: {
          messages: [`Your application is running here: http://${devWebpackConfig.devServer.host}:${port}`],
        },
        onErrors: config.dev.notifyOnErrors
        ? utils.createNotifierCallback()
        : undefined
      }))
      resolve(devWebpackConfig)
    }
  })
})

註釋:
①、點選→friendly-errors-webpack-plugin文件傳送門
②、點選→process文件傳送門
③、點選→babel-loader文件傳送門
④、點選→devtool文件傳送門
⑤、點選→webpack的HotModuleReplacementPlugin文件傳送門
⑥、點選→html-webpack-plugin文件傳送門

7、webpack.prod.conf.js

'use strict'
const path = require('path')
const utils = require('./utils')
const webpack = require('webpack')
const config = require('../config')
const merge = require('webpack-merge')
const baseWebpackConfig = require('./webpack.base.conf')
const CopyWebpackPlugin = require('copy-webpack-plugin')
const HtmlWebpackPlugin = require('html-webpack-plugin')
const ExtractTextPlugin = require('extract-text-webpack-plugin')
const OptimizeCSSPlugin = require('optimize-css-assets-webpack-plugin')
const UglifyJsPlugin = require('uglifyjs-webpack-plugin')

const env = require('../config/prod.env')

const webpackConfig = merge(baseWebpackConfig, {
  module: {
  //呼叫utils.styleLoaders的方法
    rules: utils.styleLoaders({
      sourceMap: config.build.productionSourceMap,//開啟除錯的模式。預設為true
      extract: true,
      usePostCSS: true
    })
  },
  devtool: config.build.productionSourceMap ? config.build.devtool : false,
  output: {
    path: config.build.assetsRoot,
    filename: utils.assetsPath('js/[name].[chunkhash].js'),
    chunkFilename: utils.assetsPath('js/[id].[chunkhash].js')
  },
  plugins: [
    new webpack.DefinePlugin({
      'process.env': env
    }),
    new UglifyJsPlugin({
      uglifyOptions: {
        compress: {//壓縮
          warnings: false//警告:true保留警告,false不保留
        }
      },
      sourceMap: config.build.productionSourceMap,
      parallel: true
    }),
    new ExtractTextPlugin({//抽取文字。比如打包之後的index頁面有style插入,就是這個外掛抽取出來的,減少請求
      filename: utils.assetsPath('css/[name].[contenthash].css'),  
      allChunks: true,
    }),
    
    new OptimizeCSSPlugin({//優化css的外掛
      cssProcessorOptions: config.build.productionSourceMap
        ? { safe: true, map: { inline: false } }
        : { safe: true }
    }),
   
    new HtmlWebpackPlugin({//html打包
      filename: config.build.index,
      template: 'index.html',
      inject: true,
      minify: {//壓縮
        removeComments: true,//刪除註釋
        collapseWhitespace: true,//刪除空格
        removeAttributeQuotes: true//刪除屬性的引號   
      },
     
      chunksSortMode: 'dependency'//模組排序,按照我們需要的順序排序
    }),
   
    new webpack.HashedModuleIdsPlugin(),
    new webpack.optimize.ModuleConcatenationPlugin(),
    new webpack.optimize.CommonsChunkPlugin({//抽取公共的模組
      name: 'vendor',
      minChunks (module) {   
        return (
          module.resource &&
          /\.js$/.test(module.resource) &&
          module.resource.indexOf(
            path.join(__dirname, '../node_modules')
          ) === 0
        )
      }
    }),
    new webpack.optimize.CommonsChunkPlugin({
      name: 'manifest',
      minChunks: Infinity
    }),
    new webpack.optimize.CommonsChunkPlugin({
      name: 'app',
      async: 'vendor-async',
      children: true,
      minChunks: 3
    }),
    new CopyWebpackPlugin([//複製,比如打包完之後需要把打包的檔案複製到dist裡面
      {
        from: path.resolve(__dirname, '../static'),
        to: config.build.assetsSubDirectory,
        ignore: ['.*']
      }
    ])
  ]
})

if (config.build.productionGzip) {
  const CompressionWebpackPlugin = require('compression-webpack-plugin')

  webpackConfig.plugins.push(
    new CompressionWebpackPlugin({
      asset: '[path].gz[query]',
      algorithm: 'gzip',
      test: new RegExp(
        '\\.(' +
        config.build.productionGzipExtensions.join('|') +
        ')$'
      ),
      threshold: 10240,
      minRatio: 0.8
    })
  )
}

if (config.build.bundleAnalyzerReport) {
  const BundleAnalyzerPlugin = require('webpack-bundle-analyzer').BundleAnalyzerPlugin
  webpackConfig.plugins.push(new BundleAnalyzerPlugin())
}

module.exports = webpackConfig

註釋:webpack.prod.conf.js詳細內容

五、結語

第一篇博文總在想要寫點什麼,就根據自己的經驗加查了下文件寫了這麼一篇工具類的文章,由於有些外掛有些用法會重複,所以按照文章先後,寫過用法或者給過連結的外掛,在後面的文章就省略了,有時間的建議從頭開始,如果單獨看某章節的話遇到不懂的語法或外掛可全文查詢,也可以點選更多安裝包傳送門進行查詢閱讀。本文將vue本身自帶的英文註釋刪除了,但英文註釋非常有用可以仔細閱讀,希望對大家學習vue和webpack都有所幫助。

尊重原創,如需轉載請註明出處!

相關文章