1. 程式人生 > 實用技巧 >webpack實用配置

webpack實用配置

版本號

  以entry.js打包為bundle.js為例,出口的filename可以設定為[id]、[name]、[hash]、[chunkhash]等替換形式,如下所示

var webpack = require('webpack');
module.exports = {
  entry: './entry.js', //入口檔案
  output: {
    path: __dirname,//出口路徑
    filename: '[id]-[name]-[hash].js'//出口名稱
  }
}

  則出口檔案為0-main-0c1dce21f6c5db455fb4.js

  如果index.html要引用打包後的js檔案,由於檔名稱不確定,並不好解決。這時,就需要使用html-webpack-plugin外掛。該外掛並不是內建外掛,所以需要安裝

npm install html-webpack-plugin

  HtmlWebpackPlugin簡化了HTML檔案的建立,以便為webpack包提供服務。這對於在檔名中包含每次會隨著變異會發生變化的雜湊的webpack bundle尤其有用

var webpack = require('webpack');
var HtmlWebpackPlugin = require('html-webpack-plugin');
module.exports = {
  entry: './entry.js', //入口檔案
  output: {
    path: __dirname,//出口路徑
    filename: '[id]-[name]-[hash].js'//出口名稱
  },
  plugins: [
    new HtmlWebpackPlugin({
      title: 'match',//生成的html檔案的標題為'match'
      filename: 'index.html'//生成的html檔名稱為'index.html'
    })
  ]
}

  通過以上的配置,如果在當前路徑,index.html不存在,則生成;如果存在,則替換

  [注意]如果htmlwebpackplugin不進行配置,引數為空,plugins: [new HtmlWebpackPlugin()]。預設地,生成的html檔名稱為'index.html',標題為'Webpack APP'

【標籤位置】

  htmlwebpackplugin外掛的常用設定是設定script標籤插入的位置,預設插入到body標籤中,但可以使用inject:'head',設定插入到head標籤中

var webpack = require('webpack');
var HtmlWebpackPlugin = require('html-webpack-plugin');
module.exports = {
  entry: './entry.js', //入口檔案
  output: {
    path: __dirname,//出口路徑
    filename: 'js/[id]-[name]-[hash].js'//出口名稱
  },
  plugins: [
    new HtmlWebpackPlugin({
      inject:'head',//將script標籤插入到head標籤中
      filename: 'index-[hash].html',//生成的html檔名稱為'index.html'
    })
  ]
}

【圖示設定】

  設定favicon屬性,可以設定網頁的小圖示

var webpack = require('webpack');
var HtmlWebpackPlugin = require('html-webpack-plugin');
module.exports = {
  entry: './entry.js', //入口檔案
  output: {
    path: __dirname,//出口路徑
    filename: 'js/[id]-[name]-[hash].js'//出口名稱
  },
  plugins: [
    new HtmlWebpackPlugin({
      favicon:'./icon.ico'
    })
  ]
}

【壓縮】

  設定minify屬性,可以壓縮html檔案,預設為false,即不壓縮

var webpack = require('webpack');
var HtmlWebpackPlugin = require('html-webpack-plugin');
module.exports = {
  entry: './entry.js', //入口檔案
  output: {
    path: __dirname,//出口路徑
    filename: 'js/[id]-[name]-[hash].js'//出口名稱
  },
  plugins: [
    new HtmlWebpackPlugin({
      minify:{// 壓縮html
          removeComments: true,//刪除註釋
          collapseWhitespace:true//刪除空格
      }
    })
  ]
}

  使用webpack打包後的index.html程式碼如下

<!DOCTYPE html><html><head><meta charset="UTF-8"><title>Webpack App</title></head><body><script type="text/javascript" src="js/0-main-8128c0c26a4449da7a05.js"></script></body></html>

模板檔案

  HtmlWebpackPlugin除了提供模組版本號的功能,還可以使用模板檔案

  例如,模板檔案為template.html,內容如下

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>template</title>
</head>
<body>
<script src="test.js"></script>
<div>test</div>    
</body>
</html>

  webpack配置檔案如下

var webpack = require('webpack');
var HtmlWebpackPlugin = require('html-webpack-plugin');
module.exports = {
  entry: './entry.js', //入口檔案
  output: {
    path: __dirname,//出口路徑
    filename: 'js/[id]-[name]-[hash].js'//出口名稱
  },
  plugins: [
    new HtmlWebpackPlugin({
      filename: 'index-[hash].html',//生成的html檔名稱為'index.html'
      template:'template/template.html'//模板檔案為'template.html'
    })
  ]
}

  生成的index-[hash].html以'template.html'檔案為模板

  [注意]如果在htmlwebpackplugin中使用了模板,則指定title不會生效,因為要以模板的title為準  

var webpack = require('webpack');
var HtmlWebpackPlugin = require('html-webpack-plugin');
module.exports = {
  entry: './entry.js', //入口檔案
  output: {
    path: __dirname,//出口路徑
    filename: 'js/[id]-[name]-[hash].js'//出口名稱
  },
  plugins: [
    new HtmlWebpackPlugin({
      title:'test',
      filename: 'index-[hash].html',//生成的html檔名稱為'index.html'
      template:'template/template.html'//模板檔案為'template.html'
    })
  ]
}

【傳參】

  模組檔案當然是可以傳參的,一般地,使用ejs語法。例如,在模板檔案中,使用<%= htmlWebpackPlugin.options.title %>,即可讀取htmlWebpackPlugin外掛中'title'屬性的值

  [注意]模板檔案中的'htmlWebpackPlugin'是固定的,不能隨意更改。與webpack.config.js檔案中,require()該外掛的命名無關

//webpack.config.js
var webpack = require('webpack');
var HtmlWebpackPlugin = require('html-webpack-plugin');
module.exports = {
  entry: './entry.js', //入口檔案
  output: {
    path: __dirname,//出口路徑
    filename: 'js/[id]-[name]-[hash].js'//出口名稱
  },
  plugins: [
    new HtmlWebpackPlugin({
      title:'test',
      template:'template/template.html',//模板檔案為'template.html'
      dateData: new Date() 
    })
  ]
}
//template.html
<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title><%= htmlWebpackPlugin.options.title  %></title>
</head>
<body>
<div><%=htmlWebpackPlugin.options.dateData %></div>
</body>
</html>

【模板元件】

  下面利用模板元件組合成一個html檔案,以ejs模板語言為例

//webpack.config.js
var webpack = require('webpack');
var HtmlWebpackPlugin = require('html-webpack-plugin');
module.exports = {
  entry: './entry.js', //入口檔案
  output: {
    path: __dirname,//出口路徑
    filename: 'js/[id]-[name]-[hash].js'//出口名稱
  },
  plugins: [
    new HtmlWebpackPlugin({
      template:'template/template.html'})
  ]
}
//template.html
<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>Document</title>
</head>
<body>
<div>
    <% include  template/header.html %>
</div>
<ul>  
    <% var arr = [1,2,3,4,5] %>
    <% for(var i = 0; i < arr.length; i++){ %>  
        <li><%=arr[i] %></li>  
    <% } %>  
</ul>  
<div>
    <% include  template/footer.html %>
</div>
</body>
</html>
//header.html
<div>我是頭部</div>
//footer.html
<div>我是尾部</div>

  執行結果報錯,提示子模板載入失敗

  這是因為HtmlWebpackPlugin外掛並不具備ejs模板語言所有的功能,其中一個就是不能識別<%include %>語句,這時需要安裝一個ejs-compiled-loader

npm install ejs-compiled-loader

  安裝完成後,修改配置檔案如下,表示使用ejs-compiled-loader來編譯template.html

  [注意]該外掛中的include路徑相對於webpack配置檔案的位置,而不是模板檔案template.html的位置

var webpack = require('webpack');
var HtmlWebpackPlugin = require('html-webpack-plugin');
module.exports = {
  entry: './entry.js', //入口檔案
  output: {
    path: __dirname,//出口路徑
    filename: 'js/[id]-[name]-[hash].js'//出口名稱
  },
  plugins: [
    new HtmlWebpackPlugin({
      template:'ejs-compiled-loader!template/template.html'})
  ]
}

  結果如下

多頁面

  對於多頁面來說,一般地,有多個入口檔案。不同的html頁面輸出對應不同的入口檔案。外掛plugins()是一個數組,每new一個HtmlWebpackPlugin(),就可以輸出一個html頁面。這裡有兩個重要的屬性:chunks和excludeChunks,chunks表示所包含的入口檔案,excludeChunks表示要排除的入口檔案

//webpack.config.js
var webpack = require('webpack');
var HtmlWebpackPlugin = require('html-webpack-plugin');
module.exports = {
  entry: {
      a:'./src/js/a.js',
      b:'./src/js/b.js',
      c:'./src/js/c.js'
  },
  output: {
    path: __dirname,//出口路徑
    filename: 'js/[id]-[name]-[hash].js'//出口名稱
  },
  plugins: [
    new HtmlWebpackPlugin({
          filename:'a.html',
          template:'src/template/template.html',
          title:'this is a',
          chunks:['a']
    }),
    new HtmlWebpackPlugin({
          filename:'b.html',
          template:'src/template/template.html',
          title:'this is b',
          chunks:['b']
    }),
    new HtmlWebpackPlugin({
          filename:'c.html',
          template:'src/template/template.html',
          title:'this is c',
          excludeChunks:['a','b']
    }),    
  ]
}

  結果如下

//a.html
<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>this is a</title>
</head>
<body>
<div></div>
<script type="text/javascript" src="js/2-a-9828ea84bd8c12c19b5f.js"></script></body>
</html>

//b.html
<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>this is b</title>
</head>
<body>
<div></div>
<script type="text/javascript" src="js/1-b-9828ea84bd8c12c19b5f.js"></script></body>
</html>

//c.html
<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>this is c</title>
</head>
<body>
<div></div>
<script type="text/javascript" src="js/0-c-9828ea84bd8c12c19b5f.js"></script></body>
</html>

內聯

  在前面的例子中,都是以連結的形式引入入口檔案的。有時,為了追求效能,會將其處理為內聯的形式。這裡就需要安裝一個擴充套件外掛html-webpack-inline-source-plugin,專門用來處理入口檔案內聯的

$ npm install --save-dev html-webpack-inline-source-plugin

  該外掛的使用很簡單,使用require()語句引入後,在外掛plugins()新建一個html-webpack-inline-source-plugin物件,然後在html-webpack-plugin物件中新增inlineSource屬性即可

inlineSource: '.(js|css)$' // embed all javascript and css inline
//webpack.config.js
var webpack = require('webpack');
var HtmlWebpackPlugin = require('html-webpack-plugin');
var HtmlWebpackInlineSourcePlugin = require('html-webpack-inline-source-plugin');

module.exports = {
  entry: './entry.js',
  output:{
    path: __dirname,//出口路徑
    filename: 'js/[id]-[name]-[hash].js'//出口名稱
  },
  plugins: [
    new HtmlWebpackPlugin({
        inlineSource: '.(js|css)$'
    }),
    new HtmlWebpackInlineSourcePlugin()
  ]
}

  結果如下

<!DOCTYPE html>
<html>
  <head>
    <meta charset="UTF-8">
    <title>Webpack App</title>
  </head>
  <body>
  <script type="text/javascript">/******/ (function(modules) { // webpackBootstrap
/******/     // The module cache
/******/     var installedModules = {};
/******/
/******/     // The require function
/******/     function __webpack_require__(moduleId) {
/******/
/******/         // Check if module is in cache
/******/         if(installedModules[moduleId]) {
/******/             return installedModules[moduleId].exports;
/******/         }
/******/         // Create a new module (and put it into the cache)
/******/         var module = installedModules[moduleId] = {
/******/             i: moduleId,
/******/             l: false,
/******/             exports: {}
/******/         };
/******/
/******/         // Execute the module function
/******/         modules[moduleId].call(module.exports, module, module.exports, __webpack_require__);
/******/
/******/         // Flag the module as loaded
/******/         module.l = true;
/******/
/******/         // Return the exports of the module
/******/         return module.exports;
/******/     }
/******/
/******/
/******/     // expose the modules object (__webpack_modules__)
/******/     __webpack_require__.m = modules;
/******/
/******/     // expose the module cache
/******/     __webpack_require__.c = installedModules;
/******/
/******/     // identity function for calling harmony imports with the correct context
/******/     __webpack_require__.i = function(value) { return value; };
/******/
/******/     // define getter function for harmony exports
/******/     __webpack_require__.d = function(exports, name, getter) {
/******/         if(!__webpack_require__.o(exports, name)) {
/******/             Object.defineProperty(exports, name, {
/******/                 configurable: false,
/******/                 enumerable: true,
/******/                 get: getter
/******/             });
/******/         }
/******/     };
/******/
/******/     // getDefaultExport function for compatibility with non-harmony modules
/******/     __webpack_require__.n = function(module) {
/******/         var getter = module && module.__esModule ?
/******/             function getDefault() { return module['default']; } :
/******/             function getModuleExports() { return module; };
/******/         __webpack_require__.d(getter, 'a', getter);
/******/         return getter;
/******/     };
/******/
/******/     // Object.prototype.hasOwnProperty.call
/******/     __webpack_require__.o = function(object, property) { return Object.prototype.hasOwnProperty.call(object, property); };
/******/
/******/     // __webpack_public_path__
/******/     __webpack_require__.p = "";
/******/
/******/     // Load entry module and return exports
/******/     return __webpack_require__(__webpack_require__.s = 0);
/******/ })
/************************************************************************/
/******/ ([
/* 0 */
/***/ (function(module, exports) {

document.write('It works.')

/***/ })
/******/ ]);</script></body>
</html>

babel

  下面使用babel來進行es最新標準的程式碼向es5程式碼的轉換,首先需要安裝babel核心程式,及babel-loader

npm install babel-loader babel-core 

  在使用babel-loader進行程式碼轉換之前,要先了解到ecmascript標準變化很快,且瀏覽器支援情況不同。所以,出現了'es2015'、'es2016'、'es2017'、'latest'、'env(new)'等多個不同的標準。這時,要需要來選擇從哪個標準進行轉換,需要安裝外掛babel-preset-env

npm install babel-preset-env 

  在 webpack 配置物件中,需要新增 babel-loader 到 module 的 loaders 列表中。webpack的配置檔案如下所示

const path = require('path');
module.exports = { entry:{ app:'./src/app.js', }, output:{ path:path.resolve(__dirname,'src'), filename: '[name].bundle.js' }, module: { rules: [{ test: /\.js$/, use: { loader: 'babel-loader', options: { presets: ['env'] } } }] }, }

  關於path有兩種寫法,除了上面的配置檔案的寫法外,另一種寫法如下所示。但是,儘量不要使用__dirname + '/src'的寫法,在某些引數中,該寫法無法生效

path: __dirname +  "/src"

  在命令列中執行webpack命令進行打包,打包過程如下

  打包前的檔案為app.js,內容如下

() => {
  return a + b;
};
Array.from('1,2,3');
new Set;

  打包後的檔案為app.bundle.js,主要內容如下

/******/ ([
/* 0 */
/***/ (function(module, exports, __webpack_require__) {
"use strict";
(function () {
  return a + b;
});
Array.from('1,2,3');
new Set();

/***/ })
/******/ ]);

  經過babel轉換後的js檔案存在兩個問題:

  1、打包速度較慢

  2、部分ES2017的新語法沒有轉換為ES5的程式碼

  下面對這兩個問題分別進行處理

【打包速度】

  loader的test屬性表示該loader必須滿足的條件,上面程式碼中使用/\.js$/ 來匹配,也許會去編譯 node_modules 目錄或者其他不需要的原始碼。這樣會大大增加webpack的編譯時間

  要排除 node_modules,就要使用 loaders 配置的 exclude 選項,表示哪些除外,exclude:/node_modules/

module.exports = {
  entry:{
    app:'./src/app.js',
  },
  output:{
    path:__dirname+'/src/',
    filename: '[name].bundle.js'
  },
  module: {
    rules: [{
      test: /\.js$/,
      exclude: /node_modules/,
      use: {
        loader: 'babel-loader',
        options: { presets: ['env'] }
      }
    }]
  },
}

  [注意]exclude除了支援正則表示式外,還支援字串形式,寫法如下。如果用__dirname +'/node_modules'的形式則不會生效

const path = require('path');
exclude:path.resolve(__dirname, "node_modules")

  打包過程如下

  當node-modules檔案部分較大時,速度提升會更明顯

  除了exclude選項,還可以使用include選項,能夠明確被打包的檔案時,使用include將使打包速度更快

module.exports = {
  entry:{
    app:'./src/app.js',
  },
  output:{
    path:__dirname+'/src/',
    filename: '[name].bundle.js'
  },
  module: {
    rules: [{
      test: /\.js$/,
      include: /src/,
      use: {
        loader: 'babel-loader',
        options: { presets: ['env'] }
      }
    }]
  },
}

  [注意]include的另一種寫法如下所示

const path = require('path');
include: path.resolve(__dirname, 'src')

  打包過程如下

  耗費時間有所減小

  cacheDirectory選項值預設為false,當為true時,指定的目錄將用來快取 loader 的執行結果。之後的 webpack 構建,將會嘗試讀取快取,來避免在每次執行時,可能產生的、高效能消耗的 Babel 重新編譯過程

const path = require('path');
module.exports = {
  entry: {
    app: './src/app.js',
  },
  output: {
    path: path.resolve(__dirname, 'src'),
    filename: '[name].bundle.js'
  },
  module: {
    rules: [{
      test: /\.js$/,
      include: path.resolve(__dirname, 'src'),
      use: {
        loader: 'babel-loader',
        options: { 
          presets: ['env'],
          cacheDirectory:true
        }
      }
    }]
  },
}

  耗費時間減少了100ms,效果很好

  解決了babel編譯速度後,下面來解決ES新語法不被轉換的問題

【babel-polyfill】

  Babel預設只轉換新的JavaScript句法(syntax),而不轉換新的API,比如Iterator、Generator、Set、Maps、Proxy、Reflect、Symbol、Promise等全域性物件,以及一些定義在全域性物件上的方法(比如Object.assign)都不會轉碼。

  舉例來說,ES6在Array物件上新增了Array.from方法。Babel就不會轉碼這個方法。如果想讓這個方法執行,必須使用babel-polyfill。babel-polyfill是一個全域性墊片,為開發應用準備的

npm install babel-polyfill

  在指令碼頭部加入下面程式碼即可使用

import 'babel-polyfill';

  app.js的檔案內容如下

import 'babel-polyfill';
() => {
  return a + b;
};
Array.from('1,2,3');
new Set;

  由下圖所示,轉換後的檔案大小超過了200kb

【babel-plugin-transform-runtime】

  相當於babel-polyfill來說,babel-plugin-transform-runtime更加適用,它是一個區域性墊片,為開發框架準備

npm install babel-plugin-transform-runtime babel-runtime

  app.js檔案如下所示

() => {
  return a + b;
};
Array.from('1,2,3');
new Set;

  配置檔案如下所示

const path = require('path');
module.exports = {
  entry: {
    app: './src/app.js',
  },
  output: {
    path: path.resolve(__dirname, 'src'),
    filename: '[name].bundle.js'
  },
  module: {
    rules: [{
      test: /\.js$/,
      include: path.resolve(__dirname, 'src'),
      use: {
        loader: 'babel-loader',
        options: { 
          presets: ['env'],
          cacheDirectory:true,
          plugins: ['transform-runtime']
        }
      }
    }]
  },
}

  轉換過程如下所示

  轉換後的檔案app.bundle.js主要內容如下所示

(function () {
  return a + b;
});
(0, _from2.default)('1,2,3');
new _set2.default();

CSS

  在webpack入門博文中由介紹過CSS外掛的簡單使用,接下來將詳細介紹

  首先,要安裝css-loader和style-loader,css-loader用於讀取並載入css檔案,style-loader將它插入到頁面中

  [特別注意]在處理css時,最好不要使用include、exclude等屬性。include、exclude屬性是加快babel轉換速度的,和css沒什麼關係,而且會添亂

npm install css-loader style-loader
//app.js
require('./css/common.css');
//common.css
body{margin: 0;background-color: red}
//webpack.config.js
var webpack = require('webpack');
var HtmlWebpackPlugin = require('html-webpack-plugin');
module.exports = {
  entry: './src/app.js',
  output:{
    path: __dirname,//出口路徑
    filename: 'js/[name].bundle.js'//出口名稱
  },
  module:{
      rules:[
          {
              test:/\.css$/,
              use:[ 'style-loader', 'css-loader' ]
          }
      ]
  },
  plugins: [
    new HtmlWebpackPlugin({})
  ]
}

  效果如下

【自動字首】

  頁面載入CSS往往並不像上面的情況這麼簡單,需要處理很多問題,其中一個就是瀏覽器字首問題。對於某些屬性來說,比如transform,不同瀏覽器的版本對其支援程度不同,瀏覽器字首也不同。這時,就需要能夠根據實際情況,自動增加字首,而postcss-loader就是這樣的工具,而且功能要強大的多

  首先,先安裝postcss-loader

npm install postcss-loader

  然後,安裝postcss的自動字首的外掛autoprefixer

npm install autoprefixer

  配置如下

//common.css
body{transform: scale(0);background-color: red}
//app.js
require('./css/common.css');
//webpack.config.js
var webpack = require('webpack');
var HtmlWebpackPlugin = require('html-webpack-plugin');
module.exports = {
  entry: './src/app.js',
  output:{
    path: __dirname,//出口路徑
    filename: 'js/[name].bundle.js'//出口名稱
  },
  module:{
      rules:[
          {
              test:/\.css$/,
              use:[ 'style-loader', 
                    'css-loader',                    
                    {
                        loader: 'postcss-loader',
                        options: {plugins: [require('autoprefixer')]}            
                    }
                 ]
          }
      ]
  },
  plugins: [
    new HtmlWebpackPlugin({})
  ]
}

  結果如下

  如果css檔案中出現@import,則有兩種處理方式,一種是將postcss檔案單獨寫成配置檔案postcss.config.js

//common.css
@import './flex.css';
body{transform: scale(0);background-color: red}
//flex.css
body{display:flex;}
//app.js
require('./css/common.css');

//webpack.config.js
var webpack = require('webpack');
var HtmlWebpackPlugin = require('html-webpack-plugin');
module.exports = {
  entry: './src/app.js',
  output:{
    path: __dirname,//出口路徑
    filename: 'js/[name].bundle.js'//出口名稱
  },
  module:{
      rules:[
          {
              test:/\.css$/,
              use:[ 'style-loader', 
                  { loader: 'css-loader',
                    options: {importLoaders: 1} 
                  },
                'postcss-loader'
                ]
          }
      ]
  },
  plugins: [
    new HtmlWebpackPlugin({})
  ]
}

//postcss.config.js
module.exports = {
 plugins:[require('autoprefixer')]
}

  結果如下

  另一種需要安裝postcss-import外掛

npm install postcss-import
//common.css
@import './flex.css';
body{transform: scale(0);background-color: red}
//flex.css
body{display:flex;}
//app.js
require('./css/common.css');

//webpack.config.js
var webpack = require('webpack');
var HtmlWebpackPlugin = require('html-webpack-plugin');
module.exports = {
  entry: './src/app.js',
  output:{
    path: __dirname,//出口路徑
    filename: 'js/[name].bundle.js'//出口名稱
  },
  module:{
      rules:[
          {
              test:/\.css$/,
              use:[ 'style-loader', 
                  { loader: 'css-loader',
                    options: {importLoaders: 1 } 
                  },
                  {
                    loader: 'postcss-loader',
                    options: {plugins: [
                          require('postcss-import'),
                          require('autoprefixer')
                        ]
                    }     
                  }
                ]
          }
      ]
  },
  plugins: [
    new HtmlWebpackPlugin({})
  ]
}

  結果如下

【sass】

  首先,需要安裝sass-loader及node-sass

  [注意]關於node-sass安裝的問題移步至此

npm install sass-loader node-sass

  由於sass-loader中已經自帶了關於@import處理的問題。所以,不需要css-loader及postcss-loader的額外處理

//layer.scss
@import './flex.scss';
body{
    background-color:green;
    div{
        width: 400px;
    }
}
//flex.scss
.flex{display:flex;}
//app.js
require('./components/layer/layer.scss');

//webpack.config.js
var webpack = require('webpack');
var HtmlWebpackPlugin = require('html-webpack-plugin');
module.exports = {
  entry: './src/app.js',
  output:{
    path: __dirname,//出口路徑
    filename: 'js/[name].bundle.js'//出口名稱
  },
  module:{
      rules:[
          {
              test:/\.scss$/,
              use:[    'style-loader', 
                      'css-loader',
                    {
                        loader: 'postcss-loader',
                        options: {plugins: [require('autoprefixer')]}            
                    },
                    'sass-loader'
                 ]
          }
      ]
  },
  plugins: [
    new HtmlWebpackPlugin({})
  ]
}

  結果如下

【分離CSS】

  預設地,CSS作為模組資源被打包到入口js檔案中。有時,需要把CSS檔案分離出來,這時就需要用到extract-text-webpack-plugin外掛

npm install extract-text-webpack-plugin

  該外掛的配置如下

var webpack = require('webpack');
var HtmlWebpackPlugin = require('html-webpack-plugin');
var ExtractTextPlugin = require('extract-text-webpack-plugin');
module.exports = {
  entry: './src/app.js',
  output:{
    path: __dirname,//出口路徑
    filename: 'js/[name].bundle.js'//出口名稱
  },
  module:{
      rules:[
          {
                  test:/\.scss$/,
                use: ExtractTextPlugin.extract({
                  fallback: 'style-loader',
                  use:[ 'css-loader',
                        {
                            loader: 'postcss-loader',
                            options: {plugins: [require('autoprefixer')]}            
                        },
                        'sass-loader'
                     ]
                })              
          }
      ]
  },
  plugins: [
    new HtmlWebpackPlugin({}),
    new ExtractTextPlugin("styles.css")
  ]
}

  結果如下,該外掛將入口檔案中引用的 *.css,移動到獨立分離的 CSS 檔案。因此,你的樣式將不再內嵌到 JS bundle 中,而是會放到一個單獨的 CSS 檔案(即 styles.css)當中。 如果樣式檔案大小較大,這會做更快提前載入,因為 CSS bundle 會跟 JS bundle 並行載入

圖片資源

  webpack在處理圖片、音樂、電影等資原始檔時,需要使用file-loader

npm install file-loader

  預設情況下,使用file-loader生成的檔案的檔名就是檔案內容的MD5雜湊值並保留原始副檔名

  file-loader的配置項如下所示

name  [hash].[ext] 為檔案配置自定義檔名模板
context this.options.context 配置自定義檔案 context,預設為 webpack.config.js context
publicPath  __webpack_public_path__ 為檔案配置自定義 public 釋出目錄
outputPath 'undefined' 為檔案配置自定義 output 輸出目錄
useRelativePath false 如果希望為每個檔案生成一個相對 url 的 context 時,應該將其設定為 true
emitFile true 預設情況下會生成檔案,可以通過將此項設定為 false 來禁止(例如,使用了服務端的 packages)

  以引入圖片資源例,有以下幾種情況

  1、通過css檔案的background屬性引入

//webpack.config.js
var webpack = require('webpack');
var HtmlWebpackPlugin = require('html-webpack-plugin');
module.exports = {
  entry: './entry.js', //入口檔案
  output: {
    path: __dirname,//出口路徑
    filename: 'js/[id]-[name]-[hash].js'//出口名稱
  },
  module:{

      rules:[
          {
              test:/\.css$/,
              use:[ 'style-loader', 'css-loader' ]
          },
          {
              test:/\.(png|jpg|gif|svg)$/i,
              use:'file-loader'
          }
      ]
  },  
  plugins: [
    new HtmlWebpackPlugin()
  ]
}
//entry.js
require('./src/css/common.css');
//common.css
body{background: url('../img/eg_bulbon.gif')}

  結果如下

  2、通過模板html檔案img標籤引入,這時需要使用${require('')}將相對路徑包裹一次

//webpack.config.js
var webpack = require('webpack');
var HtmlWebpackPlugin = require('html-webpack-plugin');
module.exports = {
  entry: './entry.js', //入口檔案
  output: {
    path: __dirname,//出口路徑
    filename: 'js/[id]-[name]-[hash].js'//出口名稱
  },
  module:{
      rules:[
          {
              test:/\.css$/,
              use:[ 'style-loader', 'css-loader' ]
          },
          {
              test:/\.(png|jpg|gif|svg)$/i,
              use:'file-loader'
          }
      ]
  },  
  plugins: [
    new HtmlWebpackPlugin({
        template:'template/template.html'
    })
  ]
}
//template.html
<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>Document</title>
</head>
<body>
<img src="${require('../src/img/eg_bulbon.gif')}" alt="">
</body>
</html>

  結果如下

  3、若模板使用ejs-compiled-loader外掛,則無法使用${require('')}語句,需要使用HtmlWebpackPlugin傳參來構造絕對路徑

//webpack.config.js
var webpack = require('webpack');
var HtmlWebpackPlugin = require('html-webpack-plugin');
module.exports = {
  entry: './entry.js', //入口檔案
  output: {
    path: __dirname + '/dist',//出口路徑
    filename: 'js/[id]-[name]-[hash].js'//出口名稱
  },
  module:{
      rules:[
          {
              test:/\.css$/,
              use:[ 'style-loader', 'css-loader' ]
          },
          {
              test:/\.(png|jpg|gif|svg)$/i,
              use:'file-loader'
          }
      ]
  },  
  plugins: [
    new HtmlWebpackPlugin({
        template:'ejs-compiled-loader!template/template.html',
        file:__dirname
    })
  ]
}
//template.html
<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>Document</title>
</head>
<body>
<div>
    <% include  template/header.html %>
</div>
</body>
</html>
//header.html
<img src="<%=htmlWebpackPlugin.options.file%>\src\img\eg_bulbon.gif" alt="">

  結果如下

【file-loader引數】

  檔名模板佔位符有如下幾種

[ext] 資源副檔名
[name] 資源的基本名稱
[path] 資源相對於 context 查詢引數或者配置的路徑
[hash] 內容的雜湊值,預設為十六進位制編碼的 md5
[<hashType>:hash:<digestType>:<length>] 可選配置
  其他的 hashType, 即 sha1, md5, sha256, sha512
  其他的 digestType, 即 hex, base26, base32, base36, base49, base52, base58, base62, base64
  length 字元的長度
[N] 當前檔名按照查詢引數 regExp 匹配後獲得到第 N 個匹配結果
{
  test:/\.(png|jpg|gif|svg)$/i,
  use:[{
              loader:'file-loader',
            options: {
                name:'[name]-[hash:5].[ext]'
            }  
        }]
}

  或者

{
  test:/\.(png|jpg|gif|svg)$/i,
  use:['file-loader?name=[name]-[hash:5].[ext]']
}

  結果如下

【url-loader】

  url-loader功能類似於file-loader,但是在檔案大小(單位byte)低於指定的限制時,可以返回一個dataURL

  可以通過傳遞查詢引數(query parameter)來指定限制(預設為不限制)

  如果檔案大小超過限制,將轉為使用 file-loader,所有的查詢引數也會傳過去

npm install url-loader

  圖片的大小為1.1kb,下面將限制設定為2000,則圖片將以base64格式傳遞

{
  test:/\.(png|jpg|gif|svg)$/i,
  use:['url-loader?limit=2000']
}

  結果如下

  如果將限制大小設定為1000,圖片以src的形式傳遞

{
  test:/\.(png|jpg|gif|svg)$/i,
  use:[{
              loader:'url-loader',
            options: {
                limit:1000,
                name:'[name]-[hash:5].[ext]'
            }  
        }]
}

【image-webpack-loader】

  使用image-webpack-loader來壓縮圖片

npm install image-webpack-loader

  image-webpack-loader的配置項如下

 options: {
        mozjpeg: {
          progressive: true,
          quality: 65
        },
        optipng: {
          enabled: false,
        },
        pngquant: {
          quality: 80,
          speed: 4
        },
        gifsicle: {
          interlaced: false,
        },
        webp: {
          quality: 75
        }
      }

  外掛一張大小為4.1kb的名稱為'm.jpg'的圖片,配置如下

{
  test:/\.(png|jpg|gif|svg)$/i,
  use:[
    'url-loader?limit=1000&name=[name]-[hash:5].[ext]',
    'image-webpack-loader'
  ]
}

  結果如下所示,生成大小為3.28kb,名稱為'm-c7083.jpg'的圖片

【雪碧圖】

  在webpack中自動生成雪碧圖,需要使用postcss-sprits外掛

npm install postcss-sprites

  配置非常簡單

  "plugins": {
    "postcss-sprites": {
       spritePath: 'dist/assets/imgs/sprites/'
    }
  }
}

載入第三方庫

  如果是載入的遠端CDN庫,則在HTML檔案內直接使用script標籤引入即可

<script src="https://cdn.bootcss.com/jquery/3.3.1/core.js"></script>

  這樣,在檔案中可以直接使用jQuery

  如果jQuery是通過npm儲存到本地,則需要使用ProvidePlugin外掛來自動載入模組,而不必到處importrequire

new webpack.ProvidePlugin({
  $: 'jquery',
  jQuery: 'jquery'
})

  然後在我們任意原始碼中:

// in a module
$('#item'); // <= 起作用
jQuery('#item'); // <= 起作用
// $ 自動被設定為 "jquery" 輸出的內容

  如果jQuery是儲存在一個自定義的目錄中的,則需要還需要設定別名

resolve:{
  alias:{
    jquery$:path.resolve(__dirname,'src/libs/jquery.min.js')
  }
}

  除了使用providePlugin,還可以使用imports-loader

module: {
  rules: [
    {
      test: path.resolve(__dirname,"src/app.js"),
      use: [
    loader: 'imports-loader',
        options: {$:'jquery'}     
   ]
    }
  ]
}

代理遠端介面

  使用webpack-dev-server的proxy功能,可以代理遠端介面。實際上,它使用的是http-proxy-middleware外掛

  常用引數如下

target:代理指向的地址
changeOrigin:改變源URL(預設false)
headers:設定http請求頭
pathRewrite:重定向介面請求
logLevel:控制檯顯示資訊

  在localhost:3000上有後端服務的話,可以這樣啟用代理:

proxy: {
  "/api": "http://localhost:3000"
}

  如果伺服器給出500錯誤,則需要新增changeOrigin

proxy: {
  "/api": {
    target: "http://localhost:3000",
    changeOrigin: true
  }
}

實用配置

  下面將使用webpack搭建一個實用的開發環境

var webpack = require('webpack');
var HtmlWebpackPlugin = require('html-webpack-plugin');
var ExtractTextPlugin = require('extract-text-webpack-plugin');
module.exports = {
    entry: './src/app.js',//入口檔案
    output:{
        path: __dirname,//出口路徑
        filename: 'js/[name].bundle.js'//出口名稱
    },
    module:{
        rules:[
            {
                test:/\.scss$/,
                use: ExtractTextPlugin.extract({
                    fallback: 'style-loader',
                     use:[ 
                             'css-loader',
                            {
                                loader: 'postcss-loader',
                                //自動新增字首
                                options: {plugins: [require('autoprefixer')]}            
                            },
                            'sass-loader'
                        ]
                })              
            },
            {
                test:/\.js$/,
                include:/\.\/src/,
                use:{
                        loader: 'babel-loader',
                        //將最新標準的js程式碼翻譯為es5程式碼
                        options:{presets: ['env']}
                    }
            },
            {
                test:/\.(png|jpg|gif|svg)$/i,
                use:[
                        //當圖片大小大於1000byte時,以[name]-[hash:5].[ext]的形式輸出
                        //當圖片大小小於1000byte時,以baseURL的形式輸出
                        'url-loader?limit=1000&name=[name]-[hash:5].[ext]',
                        //壓縮圖片
                        'image-webpack-loader'
                    ]
            }
          ]
    },
    plugins: [
          //使用模板生成html檔案
        new HtmlWebpackPlugin({template:'ejs-compiled-loader!template/template.html'}),
        //分離出css到style.css
        new ExtractTextPlugin("style.css")
    ]
}

webpack配置檔案

1、webpack可以處理js,json檔案,不能處理其他檔案
2、生產環境比開發環境多一個壓縮js程式碼
3、生產環境和開發環境可將ES6模組編譯成瀏覽器能識別的模組

/*webpack.config.js*/

//resolve用來拼接絕對路徑的方法
const {resolve} = require('path');

module.exports = {
    entry: './src/js/index.js',  //入口檔案,webpack從這裡開始打包
    output: {
    filename: 'build.js',  //輸出檔名
    path: resolve(__dirname, 'build')  //輸出路徑,一般採用絕對路徑
    //__dirname nodejs的變數,代表當前檔案目錄的絕對路徑
    },
    //loader處理那些非JavaScript檔案
    module: {
        rules:[
        //詳細loader配置,不同檔案配置不同loader
        ]
    },
    plugins: [
    //外掛,執行更大更復雜的任務
    ],
    //打包模式
    mode: 'development'  //development:開發環境,production:生產環境

HTML配置

/* 
loader:1、下載 2、使用(配置loader)
plugins:1、下載 2、引入 3、使用
*/
const {resolve} = require('path')
const HtmlWebpackPlugin = require('html-webpack-plugin')

module.exports = {
  entry:'./src/index.js',
  output:{
    filename:'build.js',
    path:resolve(__dirname, 'build')
  },
  module:{
    rules:[

    ]
  },
  plugins:[
    //html-webpack-plugin
    //功能:預設建立一個空的HTML,自動引入打包輸出的所有資源(JS/Css)
    //需求:需要有結構的HTML檔案
    new HtmlWebpackPlugin({
      // 複製./src/index.html檔案,並自動引入打包輸出的所有資源
      template: './src/index.html',
      // 壓縮html
      minify: {
        // 移除空格
        collapseWhitespace: true,
        // 移除註釋
        removeComments: true,
      },
    }
      )
  ],
  mode: 'development'
}

CSS配置

const {resolve} = require('path');
const HtmlWebpackPlugin = require('html-webpack-plugin')
const MiniCssExtractPlugin = require('mini-css-extract-plugin')
const OptimizeCssAssetsWebpackPlugin = require('optimize-css-assets-webpack-plugin')


// 複用loader
/*css相容性處理:postcss-->postcss-loader postcss-preset-env
   (package.josn)
   "browserslist": {
   //開發環境-->設定node環境變數:process.env.NODE_ENV = development
    "development": [
      "last 1 chrome version",  相容最近版本的瀏覽器
      "last 1 firefox version",
      "last 1 safari version"
    ],
    //生產環境,預設看生產環境
    "production": [
      ">0.2%", 相容99.8%的瀏覽器
      "not dead",  不要已死的瀏覽器
      "not op_mini all"  不要op_mini
     ]
   }
幫postcss找到package.josn中browserslist裡面的配置,通過配置載入指定的css相容性格式 
*/
// 使用loader的預設配置 ‘postcss-loader’
const commonCssLoader = [
  MiniCssExtractPlugin.loader,//取代style-loader,提取js中的css成單獨檔案
  'css-loader', //將css檔案變成commonjs模組載入到js中,裡面的內容是樣式字串
  {
    // 還需在package.json中定義browserslist
    loader: 'postcss-loader',
    options: {
      ident: 'postcss',
      plugins: () => {
        require('postcss-preset-env')();
      },
    },
  },
]

module.exports = {
  entry: './src/js/index.js',
  output: {
    filename: 'js/build.js',
    path: resolve(__dirname, 'build'),
  },
  module: {
    rules: [
      {
        test: /\.css$/,
        use: [
          // use陣列中執行順序:從右到左、從下到上依次執行
          // 'style-loader',建立style標籤,將js中的樣式資源插入進行,新增到head中生效
          ...commonCssLoader
        ],
      },
      {
        test: /\.less$/,
        use: [
          ...commonCssLoader,
          'less-loader',// 將less檔案編譯成css檔案
        ],
      },
    ]
  },
  plugins: [
    new MiniCssExtractPlugin({
      filename: 'css/build.css',
    }),
    new OptimizeCssAssetsWebpackPlugin(),// 壓縮css
    new HtmlWebpackPlugin({
      template: './src/index.html',
      minify: {
        collapseWhitespace: true,
        removeComments: true
      }
    })
  ],
  mode: 'production'
}

js配置

const { resolve } = require('path');
const HtmlWebpackPlugin = require('html-webpack-plugin');

module.exports = {
  entry: './src/js/index.js',
  output: {
    filename: 'js/build.js',
    path: resolve(__dirname, 'build'),
  },
  module: {
    rules: [
      /*
        語法檢查: eslint-loader eslint
        只檢查自己寫的原始碼,不檢查第三方庫
        設定檢查規則:
          package.josn中 eslintConfig中設定
          推薦使用airbnb規則
      */
      {
        test: /\.js$/,
        exclude: /node_modules/,
        loader: 'eslint-loader',
        options: {
          // 自動修復
          fix: true,
        },
      },
       /* 
      js相容性處理:babel-loader @babel/preset-env @babel/core
        1.基本js相容性處理 --> @babel/preset-env 
        問題:只能轉換基本語法,promise等高階語法無法轉換
        2.全部js相容性處理  --> @babel/polyfill 檔案中匯入即可
        問題:只需解決部分相容性問題。但是將所有相容性程式碼全部引入,體積過大
        3、需要做相容性處理就做:按需載入 --> core-js
     */
      {
        test: /\.js$/,
        exclude: /node_modules/,
        loader: 'babel-loader',
        options: {
        // 預設:指示babel做怎樣的相容性處理
          presets: [
            [
              '@babel/preset-env',
              {
                // 按需載入
                useBuiltIns: 'usage',
                // 指定core-js版本
                corejs: {
                  version: 3
                },
                // 指定相容性做到哪個版本瀏覽器
                targets:{
                  chrome: '60',
                  firefox: '60',
                  ie: '9',
                  edge: '17',
                  safari: '10'
                }
              }
            ]
          ],
        },
      },
    ],
  },
  plugins: [
    new HtmlWebpackPlugin({
      template: './src/index.html',
    }),
  ],
  mode: 'production', // 生產環境下會自動壓縮js程式碼
};

圖片處理

const HtmlWebpackPlugon = require('html-webpack-plugin')

module.exports = {
  entry:'./src/index.js',
  output:{
    filename:'build.js',
    path:`${__dirname}/build`
  },
  module:{
    rules:[
      {
        test:/\.less$/,
        //使用多個loader用use
        use:[
          'style-loader',
          'css-loader',
          'less-loader'
        ]
      },{
        //處理圖片資源
        // 問題:預設處理不了HTML中img圖片
        test: /\.(jpg|png|gif)$/,
        // 使用一個loader
        // 下載url-loader file-loader
        loader: 'url-loader',
        options: {
          // 圖片大小小於8kb,就會被base64處理
          // 優點:減少請求數量(減輕伺服器壓力)
          // 缺點:圖片體積會變大(檔案請求速度更慢)
          limit: 8 * 1024,
          // esModule:false 關閉es6模組化,使用commonjs解析

          // 給圖片進行重新命名
          // [hash:10]:取圖片的hash的前十位
          // [ext]取檔案原來的副檔名
          name:'[hash:10].[ext]'
        }
      },
      {
        test:/\.html$/,
        // 處理HTML檔案的img圖片(負責引入img,從而能被url-loader處理)
        loader:'html-loader'
      }
      
    ]
  },
  plugins:[
    new HtmlWebpackPlugon({
      template:'./src/index.html'
    })
  ],
  mode:'development'
}

其他資源

const { resolve } = require('path');
const HtmlWebpackPlugin = require ('html-webpack-plugin')

module.exports = {
  entry: './src/index.js',
  output: {
    filename: 'build.js',
    path:  resolve( __dirname, 'build' )
  },
  module: {
    rules: [
      {
        test: /\.css$/,
        use: ['style-loader', 'css-loader']
      },
      //打包其他資源(除了html/css/js以外的資源)
      {
        // 排除html/css/js資源
        exclude:/\.(css|js|html|jpg|png|gif)$/,//url-loader也可以用
        loader:'file-loader'
      }
    ]
  },
  plugins: [
    new HtmlWebpackPlugin({
      template: './src/index.html'
    })
  ],
  mode: 'development'
}

devServer自動打包編譯

const { resolve } = require('path');
const HtmlWebpackPlugin = require ('html-webpack-plugin')

module.exports = {
  entry: './src/index.js',
  output: {
    filename: 'build.js',
    path:  resolve( __dirname, 'build' )
  },
  module: {
    rules: [
      {
        test: /\.css$/,
        use: ['style-loader', 'css-loader']
      },
      //打包其他資源(除了html/css/js以外的資源)
      {
        // 排除html/css/js資源
        exclude:/\.(css|js|html)$/,//url-loader也可以用
        loader:'file-loader'
      }
    ]
  },
  plugins: [
    new HtmlWebpackPlugin({
      template: './src/index.html'
    })
  ],
  mode: 'development',

  //開發伺服器devsever:用來自動編譯,自動開啟瀏覽器,自動重新整理瀏覽器
  // 特點:只會在記憶體中編譯打包,不會有任何輸出(不會生成build資料夾)
  // 啟動devserver指令為:npx webpack-dev-server
  devServer: {
    // 構建後的專案路徑
    contentBase: resolve(__dirname,'build'),
    //啟動gzip壓縮
    compress: true,
    // 埠號
    port: 3000,
    // 自動開啟瀏覽器
    open: true
  }
}

開發環境配置

/* 
開發環境配置:程式碼可執行
執行指令:
  webpack  會將打包結果輸出
  npx webpack-dev-server 只會在記憶體中編譯打包,沒有輸出
*/
const {resolve} = require('path')
const HtmlWebpackPlugin = require('html-webpack-plugin')

module.exports = {
  entry:'./src/index.js',
  output: {
    filename: 'build.js',
    path: resolve(__dirname, 'build')
  },
  module:{
    rules: [
      {
        test: /\.less$/,
        use: ['style-loader', 'css-loader', 'less-loader']
      },
      {
        test:/\.css$/,
        use: ['style-loader', 'css-loader']
      },
      {
        test:/\.(jpg|png|gif)$/,
        loader: 'url-loader',
        options: {
          linit: 8*1024,
          name:'[hash:10].[ext]',
          exModule: false//關閉es6模組化
        },
         // outputPath:'img' 輸出到build/img資料夾
      },
      {
        //處理html中的img
        test: /\.html$/,
        loader: 'html-loader'
      },
      {
        exclude: /\.(html|css|js|jpg|png|gif|less)$/,
        loader:'file-loader',
        options: {
          name:'[hash:10].[ext]'
        }
      }
    ]
  },
  plugins: [
      new HtmlWebpackPlugin({
        template: './src/index.html'
      })
    ],
    mode: 'development',
    devServer: {
      contentBase: resolve(__dirname, 'build'),
      compress: true, //優化
      port: 3000,
      open: true
    },
}

生產環境配置

const { resolve } = require('path');
const MiniCssExtractPlugin = require('mini-css-extract-plugin');
const OptimizeCssAssetsWebpackPlugin = require('optimize-css-assets-webpack-plugin');
const HtmlWebpackPlugin = require('html-webpack-plugin');

// 定義node環境變數,決定使用browserslist的哪個環境
process.env.NODE_ENV = 'production';

// 複用loader
const commonCssLoader = [
  MiniCssExtractPlugin.loader,
  'css-loader', {
    // 還需在package.json中定義browserslist
    loader: 'postcss-loader',
    options: {
      ident: 'postcss',
      plugins: () => {
        require('postcss-preset-env')();
      },
    },
  },
]

/* 
正常來講,一個檔案只能被一個loader處理
當一個檔案要被多個loader處理,那麼一定要指定loader執行的先後順序:
  先執行eslint再執行babel
*/
module.exports = {
  entry: './src/js/index.js',
  output: {
    filename: 'js/build.js',
    path: resolve(__dirname, 'build'),
  },
  module: {
    rules: [
      {
        test: /\.css$/,
        use: [
          ...commonCssLoader
        ],
      },
      {
        test: /\.less$/,
        use: [
          ...commonCssLoader,
          'less-loader',
        ],
      },
      {
        // 在package.josn中eslintConfig --> airbnb
        test: /\.js$/,
        exclude: /node_module/,
        // 優先執行
        enforce: 'pre',
        loader: 'eslint-loader',
        options: {
          fix: true
        }
      },
      {
        // 在package.josn中eslintConfig --> airbnb
        test: /\.js$/,
        exclude: /node_module/,
        loader: 'babel-loader',
        options: {
          presets: [
            [
              '@babel/preset-env',
              {
                useBuiltIns: 'usage',
                corejs: { version: 3 },
                targets: {
                  chrome: '60',
                  firefox: '50',
                },
              },
            ],
          ],
        }
      },
      {
        test: /\.(jpg|png|gif)$/,
        loader: 'url-loader',
        options: {
          limit: 8 * 1024,
          name: '[hash:10].[ext]',
          outputPath: 'imgs',
          esModule: false
        }
      },
      {
        test: /\.html$/,
        loader: 'html-loader'
      },
      {
        exclude: /\.(js|css|less|html|jpg|png|gif)$/,
        loader: 'file-loader',
        options: {
          outputPath: 'media'
        }
      }
    ],
  },
  plugins: [
    new MiniCssExtractPlugin({
      filename: 'css/build.css',
    }),
    new OptimizeCssAssetsWebpackPlugin(),
    new HtmlWebpackPlugin({
      template: './src/index.html',
      minify: {
        collapseWhitespace: true,
        removeComments: true
      }
    })
  ],
  mode: 'production'
};

Webpack配置區分開發環境和生產環境

  在專案開發的時候,我們通常會將程式分為開發環境和生產環境(或者叫線上環境),開發環境通常指的是我們正在開發的這個階段所需要的一些環境配置,也就是方便我們開發人員除錯開發的一種環境;生產環境通常指的是我們將程式開發完成經過測試之後無明顯異常準備釋出上線的環境,也可以理解為使用者可以正常使用的就是生產環境;

  當然開發環境和生產環境在配置方面的需求是不一樣的,但是有共同點:

  開發環境的需求:

    模組熱更新 (本地開啟服務,實時更新)
    sourceMap (方便打包除錯)
    介面代理  (配置proxyTable解決開發環境中的跨域問題)

    程式碼規範檢查 (程式碼規範檢查工具)

  生產環境的需求:

    提取公共程式碼   
    壓縮混淆(壓縮混淆程式碼,清除程式碼空格,註釋等資訊使其變得難以閱讀)
    檔案壓縮/base64編碼(壓縮程式碼,減少線上環境檔案包的大小)
    去除無用的程式碼

  開發環境和生產環境的共同需求:

    同樣的入口
    同樣的程式碼處理(loader處理)
    同樣的解析配置

    在我們搭建好Vue-cli腳手架之後,我們的build資料夾會分別自動的生成webpack.base.conf.js、webpack.dev.conf.js、webpack.prod.conf.js三個webpack配置檔案;

    webpack.base.conf.js:webpack的開發環境和生產環境的共有配置(開發環境和生產環境都是需要執行的配置)

    webpack.dev.conf.js:webpack的開發環境的特有配置(只在開發環境中執行,生產環境中不執行)

    webpack.prod.conf.js:webpack的生產環境的特有配置(只在生產環境中執行,開發環境中不執行)

    我們為什麼要區分開發環境和生產環境呢?

      因為一個專案的開發過程中肯定不會是一個版本開發完之後就立馬上線,開發是必需,上線是目的;在開發的過程中會有各種各樣的問題,比如開發環境中跨域、開發環境和生產環境因環境不同而產生的未知奇葩錯誤等等都是會時常發生的,我們區分環境的目的就是為了讓開發人員在開發的過程中可以方便除錯,保持高效的開發;讓程式在生產環境中正常有效的執行;

    

    webpack.base.conf.js配置

    

const path = require('path');
//清除build/dist資料夾檔案
const CleanWebpackPlugin = require('clean-webpack-plugin');
//生成建立Html入口檔案
const HtmlWebpackPlugin = require('html-webpack-plugin');
//將css提取到單獨的檔案中
const MiniCssExtract = require('mini-css-extract-plugin');
//css壓縮
const OptimizeCss = require('optimize-css-assets-webpack-plugin');
//壓縮js檔案
const UglifyJsPlugin = require('uglifyjs-webpack-plugin');
//引入webpack
const webpack = require('webpack');

module.exports = {
//webpack 入口檔案
    entry: './src/index.js',
//webpack 輸出檔案配置
    output: {
    //輸出檔案路徑
        path: path.resolve(__dirname, 'dist'),
   //輸出檔名
        filename: 'k-editor.[hash:8].js',
    },
  //配置外掛
    plugins: [
    //使用外掛清除dist資料夾中的檔案
        new CleanWebpackPlugin({
            path: './dist'
        }),
    //使用外掛生成Html入口檔案
        new HtmlWebpackPlugin({
         //模板檔案路徑
            template: "./src/index.html",
        //模板檔名
            filename: "index.html",
            minify: {
                removeAttributeQuotes: true, //刪除雙引號,
                collapseWhitespace: true,    //壓縮成一行,
            },
            hash: true
        }),
      //提取css到style.css中
        new MiniCssExtract({
            filename: 'style.css'
        }),
    ],
    resolve: {
        // modules: [path.resolve('node_modules')],//只在當前目錄下查詢
        alias: { //別名
            'bootstrap': 'bootstrap/dist/css/bootstrap.css',
        },
        // mainFields: ['style', 'main'],//優先尋找style,
        // mainFiles: [],//入口檔案的名字,預設index.js
        // extensions: ['js', 'css', 'json', 'vue']//副檔名順序
    },
   //loader載入器模組配置
    module: {
        rules: [
            {
            //正則表示式匹配.css為字尾的檔案
                test: /\.css$/,
           //使用loader
                use: [
                    MiniCssExtract.loader,
                    'css-loader',
                    {
                        loader: "postcss-loader"
                    },
                ]
         //正則表示式匹配.less為字尾的檔案
         //使用lodaer來處理
            }, {
                test: /\.less$/,
                use: [
                    MiniCssExtract.loader,
                    'css-loader',
                    {
                        loader: "postcss-loader"
                    },
                    'less-loader'
                ]
            },
            /* {
                 test: /\.js$/,
               //不包括node_modules
                 exclude: /node_modules/,
                 use: [{
                     loader: "eslint-loader",
                     options: {
                         enforce: 'pre'    //強制更改順序,pre 前  post 後
                     }
                 }],
             },*/
            {
                test: /\.js$/,  //普通的loader
               //不包括node_modules
                exclude: /node_modules/,
                use: [{
                    loader: "babel-loader"
                }]
            },
            {
                test: /\.html$/,
                use: ['html-withimg-loader']
            },
            {
                test: /\.(gif|png|jpg)$/,
                use: [{
                    loader: "url-loader",
                    options: {
             //圖片小於10kb就是圖片地址,大於正常打包成base64格式編碼    
                        limit: 10000,
                       //輸出路徑
                        outputPath: 'img/'
                    }
                }]
            }
        ]
    },
};

  webpack.dev.conf.js:

  

//引入webpack-merge外掛進行合併
const {merge} = require('webpack-merge');
//引入webpack.base.conf.js檔案
const base = require('./webpack.base.conf');
//引入webpack
const webpack = require('webpack');
//進行合併,將webpack.base.conf.js中的配置合併到這
module.exports = merge(base, {
   //模組引數
    mode: 'development',
    devServer: {
        contentBase: './dist',
      //埠號
        port: '8383',
        inline: true,
        historyApiFallback: true,//在開發單頁應用時非常有用,它依賴於HTML5 history API,如果設定為true,所有的跳轉將指向index.html
        hot: true//允許熱載入
    },
//啟用source-map方便除錯
    devtool: 'source-map',
    plugins: [
       //定義全域性變數
        new webpack.DefinePlugin({
         //這裡必須要解析成字串進行判斷,不然將會被識別為一個變數
            DEV: JSON.stringify('dev')
        })
    ]
});

  webpack.prod.conf.js:

const {merge} = require('webpack-merge');
const base = require('./webpack.base');

const path = require('path');
const OptimizeCss = require('optimize-css-assets-webpack-plugin');
const UglifyJsPlugin = require('uglifyjs-webpack-plugin');
const webpack = require('webpack');

module.exports = merge(base, {
    mode: 'production',
    optimization: {
        minimizer: [
          //壓縮CSS程式碼
            new OptimizeCss(),
          //壓縮js程式碼
            new UglifyJsPlugin({
              //啟用檔案快取
                cache: true,
             //使用多執行緒並行執行提高構建速度
                parallel: true,
             //使用 SourceMaps 將錯誤資訊的位置對映到模組
                sourceMap: true
            })
        ]
    },
    plugins:[
     //使用外掛定義全域性變數DEV
        new webpack.DefinePlugin({
            DEV:JSON.stringify('production')
        })
    ]

});

  最後在配置一下package.json檔案就可以了:

  

"scripts": {
    "test": "npm  run test",
    "dev": "webpack-dev-server --config webpack.dev.js",
    "build": "webpack --config webpack.prod.js"
  },

  

    如何區分開發環境和生產環境呢?

      在node中,我們有一個物件process物件,它裡面包括的一些資訊,env和它的一些屬性,當然NODE_ENV是我們自己加上去的自定義屬性,用來區分環境變數,也就是通過這個變數來進行區別是開發環境還是生產環境;但是有個問題,不同電腦上設定的方式是不一樣的,所以cross-env就來了,它可以跨平臺設定環境和使用環境變數。

    

npm install cross-env

  我們在webpack.base.conf.js檔案中修改程式碼:

const NODE_ENV=process.env.NODE_ENV;
console.log(NODE_ENV);

   然後我們修改package.json檔案:

//--config是可以設定我們執行哪個webpack檔案,預設是執行webpack.config.js,但是我們現在修改檔名了,所以我們要設定一下
"build": "cross-env NODE_ENV=production webpack --config webpack.config.prod.js",
"dev": "cross-env NODE_ENV=development webpack-dev-server --config webpack.config.dev.js"

  就這樣,我們就實現了利用webpack來區分開發環境和生產環境,當我們用npm run dev執行的時候就是開發環境,當我們執行npm run build的時候就是構建生產環境打包;

webpack-merge 配置抽離

前置

如果你曾使用 webpack 構建應用,就會知道如果把所有配置都寫在webpack.config.js中那將是災難。正如你所見,我現在使用的這套部落格園面板是使用 gulp 構建的,如果你不熟悉 gulp, 那你肯定熟悉 webpack. 下面將介紹我使用這兩個工具是如何抽離配置的,先從 webpack 開始.

webpack

原因

webpack 預設的webpack.config.js長這樣:

module.exports = {
  entry: {},
  output: {},
  module: {}
  //...
}


copy這裡最讓人頭疼的就是module內的層層巢狀。例如,我稍稍處理 css 和 js 就會像下面這樣:

module: {
        rules: [
            {
                test: /\.css$/,
                use: [
                    'style-loader',
                    {
                        loader: 'css-loader',
                        options: {
                            importLoaders: 1,
                        },
                    },
                    'postcss-loader',
                ],
            },
            {
                test: /\.js$/,
                exclude: /node_modules/,
                use: [
                    {
                        loader: 'babel-loader',
                        options: {
                            presets: ['@babel/preset-env'],
                        },
                    },
                    {
                        loader: 'eslint-loader',
                        options: {
                            cache: true,
                        },
                    },
                ],
            },
        ],
    },

如果再去區分生產環境和開發環境可能看起來更加混亂。好一點的做法是,抽離不同環境的 module 或者其他配置,根據環境來決定最終匯出的項。但是無法改變的是這些東西都存在一個webpack.config.js檔案中。如果應用程式需要使用大量的 loader, 會顯得檔案又長又混亂,需要修改一些配置時容易出錯。尋找和出錯都會浪費時間.

webpack-merge

npm i -D webpack-merge

Webpack-merge 提供了一個函式,該函式將陣列串聯併合並建立新物件的物件。如果遇到函式,它將執行它們,通過演算法執行結果,然後再次將返回的值包裝在函式中。

語法

// 預設的方式
const output = merge (object1, object2, object3, ...);
 
// 陣列物件
// 這適用於所有可用的功能。
const output = merge ([object1, object2, object3]);
 
// 右邊優先
const output = merge (
  { fruit: "apple", color: "red" },
  { fruit: "strawberries" }
);


一般使用預設的方式就夠了,深入瞭解Webpack-merge. 接下來不在使用webpack.config.js, 在專案根目錄新建 config 資料夾。資料夾下新建:
  • webpack.base.js 公共的配置
  • webpack.dev.js 開發環境配置
  • webpack.prod.js 生產環境配置

webpack.base.js

const path = require ("path")
 
module.exports = {
  entry: {
    // 這裡是多入口
    index: "./src/main.js",
    reacg: "./src/themes/reacg/index.js"
    //...
  },
  output: {
    filename: "[name].min.js",
    path: path.join (__dirname, "..", "dist")
  },
  resolve: {
    alias: {
      "@": path.resolve ("src")
    }
  }
}

這裡需要注意的一點是output.path需要處理一下路徑.path.join (__dirname,"..","dist"), 這樣就能夠和原來一樣將打包得到的 dist 目錄輸出到根目錄.
這句程式碼的意思是獲取當前絕對路徑的上一層路徑,就是專案的根目錄了。另外的webpack.dev.jswebpack.prod.js根據專案需要分別配置就可以了。這樣如果再去新增或修改一些配置就能一目瞭然了。最後還需要更改一下package.json中的script:

"scripts": {
    "start": "webpack-dev-server --config config/webpack.dev.js",
    "build": "webpack --config config/webpack.prod.js"
},

文章內容轉載,參考連結:

https://www.cnblogs.com/xiaohuochai/p/7007391.html

https://www.cnblogs.com/xuewting/p/12992973.html

https://www.cnblogs.com/guangzan/p/12526542.html