webpack 4 使用簡析

前言

本質(zhì)上,webpack 是一個現(xiàn)代 JavaScript 應用程序的靜態(tài)模塊打包器(module bundler)。當 webpack 處理應用程序時,它會遞歸地構建一個依賴關系圖(dependency graph),其中包含應用程序需要的每個模塊,然后將所有這些模塊打包成一個或多個 bundle。

在開始使用 webpack 前,需要了解 webpack 的四個核心概念:

  • 入口(entry)入口起點(entry point)指示了 webpack 要解析的源碼模塊,webpack 會解析這些模塊并構建出其內(nèi)部 依賴圖,最后將完整的依賴輸出到 bundle 文件中。

?? 可以通過在 webpack 中配置 entry 屬性,來指定一個入口起點(或多個入口起點)。默認值為 ./src。

單入口配置

// webpack.config.js
module.exports = {
  entry: './path/to/my/entry/file.js'
};

多入口配置

// webpack.config.js
module.exports = {
  entry: {
    pageOne: './src/pageOne/index.js', // 入口1
    pageTwo: './src/pageTwo/index.js', // 入口2
    pageThree: './src/pageThree/index.js' // 入口3
  }
};

更多配置內(nèi)容,請參考:入口起點

  • 輸出(output):output 屬性指定 webpack 輸出 bundles 路徑,以及命名規(guī)則。默認值為 ./dist

?? output 屬性告訴 webpack 在哪里輸出它所創(chuàng)建的 bundles,以及如何命名這些文件,默認值為 ./dist

?? 可以通過配置 output 字段來配置輸出的 bundle 文件:

//webpack.config.js
const path = require('path'); // 使用 nodejs 的 path 庫

module.exports = {
  entry: './path/to/my/entry/file.js',
  output: {
    path: path.resolve(__dirname, 'dist'), // 工程根目錄的 dist 文件夾
    filename: 'my-first-webpack.bundle.js' // 輸出 bundle 名字
  }
};

更多配置選擇,請參考:輸出

  • loaderwebpack 本身只能處理 JavaScript 文件,對于那些非 JavaScript 的文件,則可以通過各種相應的 loader 來將其轉換成 webpack 能夠識別并進行處理的模塊。

?? 比如,在 webpack 打包前,loader 可以將文件從不同的語言(如 TypeScript)轉換為 JavaScript,或?qū)?nèi)聯(lián)圖像轉換為 data URL。loader 甚至允許你直接在 JavaScript 模塊中 import CSS文件!

?? 本質(zhì)上,loader 將所有類型的文件,轉換為應用程序的依賴圖(和最終的 bundle)可以直接引用的模塊。

webpack 中, 配置 loader 主要使用以下兩個選項:

  • test 屬性:標識 loader 要處理的文件類型;
  • use 屬性:表示進行轉換時,應該使用哪個 loader;
//webpack.config.js
const path = require('path');

const config = {
  output: {
    filename: 'my-first-webpack.bundle.js'
  },
  module: {
    rules: [
      { test: /\.txt$/, use: 'raw-loader' }
    ]
  }
};

module.exports = config;

上述配置中,指定了 webpack 編譯器在遇到 require()import 導入 txt 文件時,先使用 raw-loader 進行轉換,然后再打包。

:loader 支持鏈式傳遞。一組鏈式的 loader 將按照相反的順序執(zhí)行。loader 鏈中的第一個 loader 返回值給下一個 loader(簡而言之,loader 的執(zhí)行順序與配置相反,最后配置的 loader 最先執(zhí)行)。

更多 loader 的配置,請參考:loader

更多內(nèi)置 loader,請查看:loaders

更多第三發(fā) loader,請查看:awesome-webpack

  • 插件(plugins):loader 是在打包構建過程中用于處理某些類型的模塊轉換,而插件的能力更大,其在整個構建過程中起作用,可以執(zhí)行范圍更廣的任務,比如打包優(yōu)化和壓縮 bundle 文件,甚至于重新定義環(huán)境中的變量。

?? 這樣說吧,loader 就是在構建前對某些特定類型的文件進行預處理,而插件是在構建過程中能做任何事情(應該可以這樣認為,loader 其實就是一個小型的插件,其只能對某些特定文件進行預處理。而插件的目的就在于解決 loader 無法處理的事情)。

?? 想要使用一個插件,你只需要 require() 它,然后把它添加到 plugins 數(shù)組中。多數(shù)插件可以通過選項(option)自定義配置。你也可以在一個配置文件中因為不同目的而多次使用同一個插件,這時需要通過使用 new 操作符來創(chuàng)建它的一個實例。

// webpack.config.js
const HtmlWebpackPlugin = require('html-webpack-plugin'); // 通過 npm 安裝
const webpack = require('webpack'); // 用于訪問內(nèi)置插件

const config = {
  module: {
    rules: [
      { test: /\.txt$/, use: 'raw-loader' }
    ]
  },
  plugins: [
    new HtmlWebpackPlugin({template: './src/index.html'})
  ]
};

module.exports = config;

webpack 提供許多開箱可用的插件!查閱我們的 插件列表 獲取更多信息。

更多第三方插件,請查看 awesome-webpack 列表。

更多插件配置信息,請參考:插件(plugins)

安裝

  1. 首先安裝最新版本的 Node.js

  2. 本地安裝 webpack

npm install webpack  --save-dev 
npm install webpack-cli --save-dev 

:也可以全局安裝 webpacknpm install --global webpack。但是不推薦。

快速入門

例子:要求在 src/index.js 中為 document.body 添加一個 div 子節(jié)點,該 div 子節(jié)點內(nèi)容為:"Hello,Webpack"(要求使用第三方模塊 lodash 實現(xiàn)字符串拼接)。最后使用 webpacksrc/index.js 模塊內(nèi)容打包到 dist/index.js 中,dist/index.html 引用該生成的 bundle 實現(xiàn)頁面動態(tài)添加 div 子節(jié)點功能。

實踐

  1. 首先創(chuàng)建目錄結構,如下所示:
  webpackDemo
+ |- /dist
+   |- index.html
+ |- /src
+   |- index.js
  1. 創(chuàng)建一個 package.json 文件:
npm init -y
  1. 本地安裝 webpack
npm install webpack webpack-cli --save-dev
  1. 根目錄下創(chuàng)建 webpack 默認配置文件:webpack.config.js,并配置其入口起點為:src/index.js,打包生產(chǎn)的 bundle 文件為:dist/index.js
const path = require('path');

module.exports = {
  mode: 'development',
  entry: './src/index.js',
  output: {
    filename: 'index.js',
    path: path.resolve(__dirname, 'dist')
  }
};
  1. 安裝 lodash
npm install lodash --save-dev
  1. 編寫 src/index.js 代碼:
import _ from 'lodash'

function createDiv(){
    let div = document.createElement('div');
    div.innerHTML = _.join(['Hello',',','Webpack']);
    return div;
}

document.body.appendChild(createDiv());
  1. 進行打包,打包完成后即可看到 dist/index.js 生成:
npx webpack
  1. 手動將生成的 dist/index.js 引入到 dist/index.html 中,打開瀏覽器,即可看到效果:
<!DOCTYPE html>
<html>
    <head>
        <title>Webpcak Demo</title>
    </head>
    <body>
        <script src="index.js"></script>
    </body>
</html>

一些有用配置

  • 模式(mode):提供 mode 配置選項,區(qū)分生成環(huán)境,告知 webpack 使用相應模式的內(nèi)置優(yōu)化。其選擇有兩個值可選:development,production
module.exports = {
  mode: 'production'
};
  • 基礎配置:生產(chǎn)環(huán)境 + 入口 + 出口
// webpack.config.js
var path = require('path');

module.exports = {
  mode: 'development',
  entry: './foo.js',
  output: {
    path: path.resolve(__dirname, 'dist'),
    filename: 'foo.bundle.js'
  }
};
  • resolve.alias:創(chuàng)建 importrequire 的別名,來確保模塊引入變得更簡單。例如,一些位于 src/ 文件夾下的常用模塊:
// webpack.config.js
module.exports = {
  resolve: {
    alias: {
      Utilities: path.resolve(__dirname, 'src/utilities/'),
      Templates: path.resolve(__dirname, 'src/templates/')
    }
  }
}

像未配置前需要使用全路徑或相對路徑引入:

// index.js
import Utility from '../../utilities/utility';

在配置后,就可以使用別名進行導入:

// index.js
import Utility from 'Utilities/utility'; // 注意使用的是別名:Utilities
  • watch:webpack 可以監(jiān)聽文件變化,當它們修改后會重新編譯。watch 默認為關閉。
watch: true

還可以通過 watchOptions 來進一步控制 watch 模式的選項:

watchOptions: {
  aggregateTimeout: 300, // 文件更改后,300毫秒后進行重新構建
  poll: 1000, // 輪詢時間,每 1s 進行一次檢測
  ignored: /node_modules/  // 不監(jiān)聽 node_modules 目錄
}
  • 外部擴展(externals):防止將某些 import 的包(package)打包到 bundle 中,而是在運行時(runtime)再去從外部獲取這些擴展依賴(external dependencies)。

?? 例如,從 CDN 引入 jQuery,而不是把它打包:

<!-- index.html -->
<script
  src="https://code.jquery.com/jquery-3.1.0.js"
  integrity="sha256-slogkvB1K3VOkzAI8QITxV3VzpOnkeNVsKvtkYLMjfk="
  crossorigin="anonymous">
</script>
// webpack.config.js
externals: {
  jquery: 'jQuery'
}

這樣就剝離了那些不需要改動的依賴模塊,換句話,下面展示的代碼還可以正常運行:

import $ from 'jquery';
$('.my-element').animate(...);

像上述配置了 jquery 為外部依賴后,webpack 編譯器在遇到:import $ from 'jquery'; 時,就知道了應該排除掉 jquery 模塊,不將其打包進 bundle 中。

// webpack.config.js
module.exports = {
  target: 'node' // 生成服務器端代碼
};

如果想同時生成多個 Target,可以如下配置:

// webpack.config.js
var path = require('path');
var serverConfig = {
  target: 'node',    // 生成服務器端代碼
  output: {
    path: path.resolve(__dirname, 'dist'),
    filename: 'lib.node.js'
  }
  //…
};

var clientConfig = {
  target: 'web', // <=== 默認是 'web',可省略
  output: {
    path: path.resolve(__dirname, 'dist'),
    filename: 'lib.js'
  }
  //…
};

module.exports = [ serverConfig, clientConfig ]; // 導出兩個 bundle

管理資源

安裝

npm install style-loader css-loader --save-dev 

配置

 const path = require('path');

  module.exports = {
    entry: './src/index.js',
    output: {
      filename: 'bundle.js',
      path: path.resolve(__dirname, 'dist')
    },
+   module: {
+     rules: [
+       {
+         test: /\.css$/,
+         use: [
+           'style-loader',
+           'css-loader'
+         ]
+       }
+     ]
+   }
  };

使用

  1. 現(xiàn)在 src/ 目錄下新建一個 style.css 文件:
// style.css
.container {
    color: red;
}
  1. 修改入口文件:src/index.js
import _ from 'lodash';
+ import './style.css'

function createDiv(){
    let div = document.createElement('div');
    div.innerHTML = _.join(['Hello','Webpack'],',');
+    div.classList.add('container')
    return div;
}

document.body.appendChild(createDiv());
  1. 最后重新打包即可看到效果:npx webpack
  • 抽離 CSS 文件為單獨文件:前面使用 style-loader 最終會將 css 樣式注入到頁面的 <head> 內(nèi)的 style 節(jié)點中,即為內(nèi)部樣式表。我們更希望使用的是外部樣式表,即通過 <link> 標簽進行引入,那么通過使用 mini-css-extract-plugin 插件即可實現(xiàn)。

安裝

npm install mini-css-extract-plugin --save-dev 

配置


const path = require('path');
+ const MiniCssExtractPlugin = require('mini-css-extract-plugin');
const devMode = process.env.NODE_ENV !== 'production'; // 判斷當前環(huán)境是開發(fā)環(huán)境還是 部署環(huán)境,主要是 mode屬性的設置值。

module.exports = {
  module: {
    rules: [
+      {
+        test: /\.(sa|sc|c)ss$/,
+        use: [MiniCssExtractPlugin.loader, 'css-loader']
+      },
    ]
  },
+  plugins: [
+    new MiniCssExtractPlugin({
+      filename: devMode ? '[name].css' : '[name].[hash].css', // 設置最終輸出的文件名
+      chunkFilename: devMode ? '[id].css' : '[id].[hash].css'
+    })
+  ]
};

使用:重新打包后,可在瀏覽器查看是否以外部鏈接引入樣式表。

:由于抽取了樣式,因此不再使用 style-loader 注入到 html 中了。

安裝

npm install sass-loader node-sass webpack --save-dev

配置

// webpack.config.js
module.exports = {
  module: {
    rules: [
      {
        test: /\.(sa|sc|c)ss$/,
+        use: [MiniCssExtractPlugin.loader, 'css-loader',sass-loader]
      },
    ]
  },
};

安裝

npm install file-loader  --save-dev 

配置

const path = require('path');

  module.exports = {
    entry: './src/index.js',
    output: {
      filename: 'bundle.js',
      path: path.resolve(__dirname, 'dist')
    },
    module: {
      rules: [
        {
          test: /\.css$/,
          use: [
            'style-loader',
            'css-loader'
          ]
        },
+       {
+          test: /\.(png|svg|jpg|gif|jpeg|ico)$/,
+         use: [
+           'file-loader'
+         ]
+       }
      ]
    }
  };

使用:只需向項目中增加一張圖片。比如,在 style.css 中,引入一張圖片:

.container {
    width: 400px;
    height: 400px;
    color: red;
    background: url('../static/img/horse.png');
}

最后,重新打包一下即可看到效果:npx webpack

安裝

npm install image-webpack-loader --save-dev

配置

 const path = require('path');

  module.exports = {
    entry: './src/index.js',
    output: {
      filename: 'bundle.js',
      path: path.resolve(__dirname, 'dist')
    },
    module: {
      rules: [
        {
          test: /\.css$/,
          use: [
            'style-loader',
            'css-loader'
          ]
        },
        {
          test: /\.(png|svg|jpg|gif|jpeg|ico)$/,
          use: [
            'file-loader',
+           {
+             loader: 'image-webpack-loader',
+             options: {
+               mozjpeg: {
+                 progressive: true,
+                 quality: 65
+               },
+               optipng: {
+                 enabled: false,
+               },
+               pngquant: {
+                 quality: '65-90',
+                 speed: 4
+               },
+               gifsicle: {
+                 interlaced: false,
+               },
+               webp: {
+                 quality: 75
+               }
+             }
+           },
          ]
        }
      ]
    }
  };

使用:重新打包一下,可以看到生成一張已被壓縮的圖片。

安裝

npm install url-loader --save-dev

配置

module.exports = {
  module: {
    rules: [
      {
        test: /\.(png|svg|jpg|gif|jpeg|ico|woff|woff2|eot|ttf|otf)$/,
        use: [
+          {
+            loader: 'url-loader', // 根據(jù)圖片大小,把圖片優(yōu)化成base64
+            options: {
+              limit: 10000  // 圖片小于 10000 字節(jié)時,進行 base64 編碼
+            }
+          },
          {
            loader: 'image-webpack-loader', // 先進行圖片優(yōu)化
            options: {
              mozjpeg: {
                progressive: true,
                quality: 65
              },
              optipng: {
                enabled: false
              },
              pngquant: {
                quality: '65-90',
                speed: 4
              },
              gifsicle: {
                interlaced: false
              },
              webp: {
                quality: 75
              }
            }
          }
        ]
      }
    ]
  }
};

使用:重新打包后,只要圖片小于 10000 字節(jié)的,就會被轉換成 base64 編碼的 DataURL。

file-loaderurl-loader 可以接收并加載任何文件,然后將其輸出到構建目錄。這就是說,我們可以將它們用于任何類型的文件,包括字體。

管理輸出

前面的操作我們都是手動地對 index.html 文件進行管理,但隨著項目的增大,手動管理方式將繁瑣且容易出錯,因此急需一個自動注入資源的方法,這種操作可以通過插件進行完成。

  • 自動注入資源:可以使用 html-webpack-plugin 將打包生成的 bundle 自動注入到 index.html 中。

安裝

npm install html-webpack-plugin --save-dev 

配置

// webpack.config.js
const path = require('path');
+ const HtmlWebpackPlugin = require('html-webpack-plugin');

module.exports = {
  mode: 'development',
  entry: {
+    app: './src/index.js' // 為入口起點增加命名
  },

  output: {
    filename: '[name]-[hash].js',
    path: path.resolve(__dirname, 'dist')
  },
+  plugins: [
+    new HtmlWebpackPlugin({
+      title: 'Output Management', // 默認值:Webpack App
+      filename: 'index.html', // 默認值: 'index.html'
+      template: path.resolve(__dirname, 'src/index.html'), // 以 src/index.htm 為模板
+      minify: {
+        collapseWhitespace: true,
+        removeComments: true,
+        removeAttributeQuotes: true // 移除屬性的引號
+      }
+    })
+  ]
};

使用:安裝上述配置,打包完成后,html-webpack-plugin 插件就會自動把生成的 js 文件插入到 dist/index.html 文件中。

  • 清理 /dist 文件夾:通常,在每次構建前清理 /dist 文件夾,是比較推薦的做法,因此只會生成用到的文件。需要用到插件 clean-webpack-plugin。

安裝

npm install clean-webpack-plugin --save-dev

配置


const path = require('path');
const HtmlWebpackPlugin = require('html-webpack-plugin');
+ const CleanWebpackPlugin = require('clean-webpack-plugin');

module.exports = {
  mode: 'development',
  entry: {
    app: './src/index.js'
  },

  output: {
    filename: '[name]-[hash].js',
    path: path.resolve(__dirname, 'dist')
  },
  plugins: [
+    new CleanWebpackPlugin(),
    new HtmlWebpackPlugin({
    ...
    })
  ]
};

使用:上述配置完成后,以后每次打包,/dist 目錄都會先進行清理,打包后就只生成需要使用到的文件了。

  • JS 啟用 babel 轉碼:雖然現(xiàn)代的瀏覽器已經(jīng)兼容了 96% 以上的 ES6 的語法了,但是為了兼容老式的瀏覽器(IE8、9)我們需要把最新的 ES6 的語法轉成 ES5 的。使能該功能需要使用 babel。

安裝

npm install -D babel-loader @babel/core @babel/preset-env webpack

配置

module: {
  rules: [
    {
      test: /\.m?js$/,
      exclude: /(node_modules|bower_components)/,
      use: {
        loader: 'babel-loader',
        options: {
          presets: ['@babel/preset-env']
        }
      }
    }
  ]
}

使用:經(jīng)過上述配置后,就可以在入口文件中使用 ES6 語法,打包后,該 ES6 語法內(nèi)容就會被轉換成 ES5 語法內(nèi)容。

  • ESLint校驗代碼格式規(guī)范:需要 eslint 支持。

安裝

npm install eslint --save-dev
npm install eslint-loader --save-dev

# 以下是用到的額外的需要安裝的eslint的解釋器、校驗規(guī)則等
npm i -D babel-eslint standard

配置

  1. webpack 配置文件進行配置:

module.exports = {
  module: {
    rules: [
      {
        test: /\.m?js$/,
        exclude: /(node_modules|bower_components)/,
        use: [
          {
            loader: 'babel-loader',
            options: {
              presets: ['@babel/preset-env']
            }
          },
+          {
+            loader: 'eslint-loader',
+            options: {
+              // eslint options (if necessary)
+              fix: true
+            }
+          }
        ]
      },
};
  1. 項目根目錄下創(chuàng)建一個文件:.eslintrc.js。往里面寫入以下配置:
// .eslintrc.js
// https://eslint.org/docs/user-guide/configuring
module.exports = {
  root: true,
  parserOptions: {
    parser: 'babel-eslint'
  },
  env: {
    browser: true
  },
  extends: [
    // https://github.com/standard/standard/blob/master/docs/RULES-en.md
    'standard'
  ],
  globals: {
    NODE_ENV: false
  },
  rules: {
    // allow async-await
    'generator-star-spacing': 'off',
    // allow debugger during development
    'no-debugger': process.env.NODE_ENV === 'production' ? 'error' : 'off',
    // 添加,分號必須
    semi: ['error', 'always'],
    'no-unexpected-multiline': 'off',
    'space-before-function-paren': ['error', 'never'],
    // 'quotes': ["error", "double", { "avoidEscape": true }]
    quotes: [
      'error',
      'single',
      {
        avoidEscape: true
      }
    ]
  }
};
  1. 項目根目錄下創(chuàng)建一個文件:.eslintignore。該文件配置要忽略 eslint 檢測的目錄:
/dist/
/node-modules/
# 忽略根目錄的 js 文件(即配置文件)
/*.js

開發(fā)環(huán)境搭建

在開發(fā)過程中,我們編寫的源代碼一般有多個,最后交由 webpack 打包成單獨一個 bundle。因此,如果出現(xiàn)錯誤,錯誤顯示在 bundle 中,就無法直接定位到我們自己的錯誤代碼中。所以,在開發(fā)環(huán)境(development)中,需要對 webpack 進行一些配置,方便我們進行開發(fā)。

  • 使用 source map:為了更容易地追蹤錯誤和警告,JavaScript 提供了 source map 功能,將編譯后的代碼映射回原始源代碼。如果一個錯誤來自于 b.js,source map 就會明確的告訴你。
    配置:開啟 source map
module.exports = {
    devtool: 'inline-source-map',
};
  • 使用觀察模式:每次我們修改源碼后,都需要使用 npx webpack 進行手動打包,不免繁瑣了點??梢酝ㄟ^配置 webpack "watch" 自動監(jiān)視文件更改,自動進行編譯打包。
    配置:啟動 "watch" 的方法為控制臺輸入:npx webpack --watch,也可以將該命令配置到 package.jsonscripts 中,啟動一個腳本命令:
// package.json
{
  ···
  "scripts": {
    "test": "echo \"Error: no test specified\" && exit 1"
+    "watch": "npx webpack --watch"
  },
  ···
}

使用:控制臺輸入:npm run watch

  • 使用 webpack-dev-server:webpack "watch" 可以自動進行打包,但是瀏覽器端我們還是需要手動進行刷新才能看到修改效果。如果我們也想讓瀏覽器實時監(jiān)測文件修改,自動刷新,則可以使用 webpack-dev-server。

安裝

npm install  webpack-dev-server --save-dev

配置

module.exports = {
    devServer: {
        contentBase: './dist'  // 監(jiān)聽目錄變化,實時重載
    },
};

使用:通過在 package.json 中配置一個腳本命令進行啟動:npm run hotload

// package.json
{
  ···
  "scripts": {
    "test": "echo \"Error: no test specified\" && exit 1"
    "watch": "npx webpack --watch",
+    "hotload": "webpack-dev-server --open"
  },
  ···
}

生產(chǎn)環(huán)境搭建

在生產(chǎn)環(huán)境(production)中,我們的目標是傾向于生成更小的 bundle,更輕量的 source map,以及更優(yōu)化的資源,以改善加載時間。
因此,可通過以下配置來輸出更好匹配生產(chǎn)環(huán)境的 bundle。

安裝

npm i -D optimize-css-assets-webpack-plugin

配置

const path = require('path');
const MiniCssExtractPlugin = require('mini-css-extract-plugin');
+ const OptimizeCSSAssetsPlugin = require('optimize-css-assets-webpack-plugin');

module.exports = {
+  mode: 'production',
+  optimization: {
+    minimizer: [new OptimizeCSSAssetsPlugin({})]
+  },
...
};
  • 壓縮 JavaScript:需要的插件:uglifyjs-webpack-plugin,此插件只在 mode: ’production’ 下起作用。

安裝

npm i -D uglifyjs-webpack-plugin

配置

+ const UglifyJsPlugin = require('uglifyjs-webpack-plugin');

module.exports = {
  mode: 'production',
  optimization: {
    minimizer: [
+      new UglifyJsPlugin({
+        cache: true,
+        parallel: true,
+        sourceMap: true // set to true if you want JS source maps
+      }),
      new OptimizeCSSAssetsPlugin({})
    ]
  }
};

區(qū)分開發(fā)環(huán)境和生產(chǎn)環(huán)境

開發(fā)環(huán)境(development)和生產(chǎn)環(huán)境(production)的構建目標差異很大。兩者的構建目標既存在相同部分,也存在不同之處。因此,通常會為每個環(huán)境編寫 彼此獨立的 webpack 配置,對于兩者皆有的配置,則抽取到一個公共的配置文件中即可。這種分文件配置方法可通過插件 webpack-merge 完成。

安裝

npm install webpack-merge --save-dev

配置:具體配置詳情如下:

  • 新建一個文件:webpack.common.js。作為開發(fā)環(huán)境和生產(chǎn)環(huán)境公共配置文件:
// webpack.common.js
const path = require('path');
// 清理 /dist
const CleanWebpackPlugin = require('clean-webpack-plugin');

module.exports = {
  entry: {
    app: './src/index.js'
  },
  output: {
    filename: '[name].bundle.js',
    path: path.resolve(__dirname, 'dist')
  },
  module: {
    rules: [
      {
        test: /\.m?js$/,
        exclude: /(node_modules|bower_components)/,
        use: [
          {
            loader: 'babel-loader',
            options: {
              presets: ['@babel/preset-env']
            }
          },
          {
            loader: 'eslint-loader',
            options: {
              fix: true
            }
          }
        ]
      },
      {
        test: /\.(woff|woff2|eot|ttf|otf)$/,
        use: [
          {
            loader: 'url-loader',
            options: {
              limit: 10000
            }
          }
        ]
      },
      {
        test: /\.(png|svg|jpg|gif|jpeg|ico)$/,
        use: [
          {
            loader: 'url-loader', // 根據(jù)圖片大小,把圖片優(yōu)化成base64
            options: {
              limit: 10000
            }
          },
          {
            loader: 'image-webpack-loader', // 先進行圖片優(yōu)化
            options: {
              mozjpeg: {
                progressive: true,
                quality: 65
              },
              optipng: {
                enabled: false
              },
              pngquant: {
                quality: '65-90',
                speed: 4
              },
              gifsicle: {
                interlaced: false
              },
              webp: {
                quality: 75
              }
            }
          }
        ]
      }
    ]
  },
  plugins: [new CleanWebpackPlugin()]
};
  • 新建一個文件:webpack.dev.js。作為開發(fā)環(huán)境的配置文件:
const path = require('path');
const merge = require('webpack-merge');
const common = require('./webpack.common.js');
// 注入資源到 html
const HtmlWebpackPlugin = require('html-webpack-plugin');
// 抽離 CSS 文件為單獨文件
const MiniCssExtractPlugin = require('mini-css-extract-plugin');
// merge 第二參數(shù)的配置會覆蓋第一個參數(shù)相同的配置
module.exports = merge(common, {
  mode: 'development',
  // 使能 source map
  devtool: 'inline-source-map',
  // 使能 webpack-dev-server,瀏覽器自動重載
  devServer: {
    contentBase: './dist' // 監(jiān)聽目錄內(nèi)容改變
  },
  watchOptions: {
    // 監(jiān)視文件相關的控制選項
    poll: true, // webpack 使用文件系統(tǒng)(file system)獲取文件改動的通知。在某些情況下,不會正常工作。例如,當使用 Network File System (NFS) 時。Vagrant 也有很多問題。在這些情況下,請使用輪詢. poll: true。當然 poll也可以設置成毫秒數(shù),比如:  poll: 1000
    ignored: /node_modules/, // 忽略監(jiān)控的文件夾,正則
    aggregateTimeout: 300 // 默認值,當?shù)谝粋€文件更改,會在重新構建前增加延遲
  },
  module: {
    rules: [
      {
        test: /\.(sa|sc|c)ss$/,
        use: [
          MiniCssExtractPlugin.loader,
          {
            loader: 'css-loader',
            options: {
              sourceMap: true
            }
          },
          {
            loader: 'sass-loader',
            options: {
              sourceMap: true
            }
          }
        ]
      }
    ]
  },
  plugins: [
    new HtmlWebpackPlugin({
      title: 'Development',
      filename: 'index.html', // 默認值: 'index.html',
      template: path.resolve(__dirname, 'src/index.html'),
      minify: {
        collapseWhitespace: false,
        removeComments: false,
        removeAttributeQuotes: true // 移除屬性的引號
      }
    }),
    new MiniCssExtractPlugin({
      filename: '[name].css', // 設置最終輸出的文件名
      chunkFilename: '[id].css'
    })
  ]
});
  • 新建一個文件:webpack.prod.js。作為生產(chǎn)環(huán)境的配置文件:
const path = require('path);
const merge = require('webpack-merge');
// 壓縮 js 文件
const UglifyJSPlugin = require('uglifyjs-webpack-plugin');
const common = require('./webpack.common.js');
// 注入資源到 html
const HtmlWebpackPlugin = require('html-webpack-plugin');
// 壓縮 CSS
const OptimizeCSSAssetsPlugin = require('optimize-css-assets-webpack-plugin');
// 壓縮 JS
const UglifyJsPlugin = require('uglifyjs-webpack-plugin');
// 抽離 CSS 文件為單獨文件
const MiniCssExtractPlugin = require('mini-css-extract-plugin');

// merge 第二參數(shù)的配置會覆蓋第一個參數(shù)相同的配置
module.exports = merge(common, {
  mode: 'production',
  optimization: {
    minimizer: [
      new UglifyJsPlugin({
        cache: true,
        parallel: true,
        sourceMap: true // set to true if you want JS source maps
      }),
      new OptimizeCSSAssetsPlugin({})
    ]
  },
  module: {
    rules: [
      {
        test: /\.(sa|sc|c)ss$/,
        use: [
          MiniCssExtractPlugin.loader,
          {
            loader: 'css-loader',
            options: {
              sourceMap: true
            }
          },
          {
            loader: 'sass-loader',
            options: {
              sourceMap: true
            }
          }
        ]
      }
    ]
  },
  plugins: [
    new UglifyJSPlugin(),
    new HtmlWebpackPlugin({
      title: 'Production', // 默認值:Webpack App
      filename: 'index.html', // 默認值: 'index.html'
      template: path.resolve(__dirname, 'src/index.html'),
      minify: {
        collapseWhitespace: true,
        removeComments: true,
        removeAttributeQuotes: true // 移除屬性的引號
      }
    }),
    new MiniCssExtractPlugin({
      filename: '[name].[hash].css', // 設置最終輸出的文件名
      chunkFilename: '[id].[hash].css'
    })
  ]
});
  • 最后,在 package.json 中配置如下腳本,可分別啟動開發(fā)環(huán)境和生產(chǎn)環(huán)境:
// package.json
{
  ···
  "scripts": {
    "test": "echo \"Error: no test specified\" && exit 1",
    "watch": "npx webpack --watch",
    "hotload": "webpack-dev-server --open",
+    "start": "webpack-dev-server --open --config webpack.dev.js",
+    "build": "webpack --config webpack.prod.js"
  },
  ···
}

通過 npm run start 啟動開發(fā)環(huán)境,
通過 npm run build 啟動生產(chǎn)環(huán)境。

參考

最后編輯于
?著作權歸作者所有,轉載或內(nèi)容合作請聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時請結合常識與多方信息審慎甄別。
平臺聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點,簡書系信息發(fā)布平臺,僅提供信息存儲服務。

相關閱讀更多精彩內(nèi)容

  • 版權聲明:本文為博主原創(chuàng)文章,未經(jīng)博主允許不得轉載。 webpack介紹和使用 一、webpack介紹 1、由來 ...
    it筱竹閱讀 11,475評論 0 21
  • GitChat技術雜談 前言 本文較長,為了節(jié)省你的閱讀時間,在文前列寫作思路如下: 什么是 webpack,它要...
    蕭玄辭閱讀 12,907評論 7 110
  • webpack使用學習 本分享學習借鑒webpack中文官網(wǎng),官網(wǎng)鏈接(中文文檔):https://www.web...
    腿毛怪丶叔叔閱讀 976評論 0 5
  • webpack 介紹 webpack 是什么 為什么引入新的打包工具 webpack 核心思想 webpack 安...
    yxsGert閱讀 6,675評論 2 71
  • 披薩!是一種由特殊的醬汁和餡料做成的具有意大利風味的食品,但其實這種食品已經(jīng)超越語言與文化的障礙,成為全球通行的小...
    范振民閱讀 584評論 1 23

友情鏈接更多精彩內(nèi)容