webpack備忘手冊

什么是 webpack

?? webpack 是德國開發(fā)者 Tobias Koppers 開發(fā)的模塊加載器。

??在 webpack 中所有的文件都將被當做模塊使用。當 webpack 處理應用程序時,它會遞歸地構建一個依賴關系圖(dependency graph),其中包含應用程序需要的每個模塊,然后將所有的這些模塊打包成一個或多個 bundle。

與 Gulp/Grunt 對比

??webpack 與 Gulp/Grunt 是沒有對比性的,因為 Gulp/Grunt 是一種能夠優(yōu)化前端的開發(fā)流程的工具,而 webpack 是一種模塊化的解決方案。不過 Webpack 的優(yōu)點使得 Webpack 在很多場景下可以替代 Gulp/Grunt 類的工具。

??Grunt 和 Gulp 的工作方式是:在一個配置文件中,指明對某些文件進行類似編譯,組合,壓縮等任務的具體步驟,工具之后可以自動替你完成這些任務。

??webpack 的工作方式是:把你的項目當做一個整體,通過一個給定的主文件(如:index.js),Webpack 將從這個文件開始找到你的項目的所有依賴文件,使用 loaders 處理它們,最后打包為一個(或多個)瀏覽器可識別的 JavaScript 文件。

webpack 的安裝及使用

  1. 通過 npm 全局安裝 webapck
    $ npm install -g webpack
  1. 創(chuàng)建項目并初始化 package.json 文件
    $ mkdir demo1 && cd demo1
    $ npm init
  1. 在項目中安裝 webpack
    $ npm install webpack --save-dev

--save-dev 是開發(fā)時候依賴的東西,--save 是發(fā)布之后還依賴的東西

  1. 在項目中創(chuàng)建如下文件結構

    <pre>
    .
    ├── index.html // 顯示的網頁
    ├── main.js // webpack 入口
    └── bundle.js // 通過 webpack 命令生成的文件,無需創(chuàng)建
    </pre>

  2. 通過命令對項目中依賴的 js 文件進行打包

    # webpack 要打包的 js 文件名  打包后生成的js文件名
    $ webpack main.js bundle.js

??在 webpack 命令后面還可以加入以下參數(shù)

  • --watch 實時打包
  • --progress 顯示打包進度
  • --display-modules 顯示打包的模塊
  • --display-reasons 顯示模塊包含在輸出中的原因

??更多參數(shù)可以通過命令 webpack --help 查看

webpack 中的四個核心概念

  • Entry 入口
  • Output 輸出
  • Loaders
  • Plugins 插件

??webpack 中默認的配置文件名稱是 webpack.config.js,因此我們需要在項目中創(chuàng)建如下文件結構:

<pre>
.
├── index.html // 顯示的頁面
├── main.js // webpack 入口
├── webpack.config.js // webpack 中默認的配置文件
└── bundle.js // 通過 webpack 命令生成的文件,無需創(chuàng)建
</pre>

entry 入口

??入口起點(entry point)指示 webpack 應該使用哪個模塊,來作為構建其內部依賴圖的開始。進入入口起點后。 webpack 會找出有哪些模塊和庫是入口起點(直接和間接)依賴的。

??可以在 webpack.config.js 中 配置 entry 屬性,來指定一個入口或多個起點入口,代碼如下:

moudle.exports = {
  entry: './path/file.js',
};

output 輸出

?? output 屬性告訴 webpack 在哪里輸出它所創(chuàng)建的 bundles,以及如何命名這些文件。你可以通過在配置指定一個 output 字段,來配置這些過程:

const path = require('path');

moudle.exports = {
  entry: './path/file.js',
  output: {
    path: path.resolve(__dirname, 'dist'),
    filename: 'my-webpack.bundle.js',
  },
};

??其中 output.path 屬性用于指定生成文件的路徑,output.filename 用于指定生成文件的名稱。

Loaders

?? Loaderswebpack 能夠去處理那些非 JavaScript 文件(webpack 自身只理解 JavaScript)。loader 可以將所有類型的文件轉換為 webpack 能夠處理的有效模塊,然后可以利用 webpack 的打包能力,對它們進行處理。

??本質上,webpack loader 將所有類型的文件,轉換為應用程序的依賴圖可以直接引用模塊。在更高層面上,在 webpack 的配置中 loader 有兩個目標:

  1. 識別應該被對應的 loader 進行轉換的那些文件(使用 test 屬性)
  2. 轉換這些文件,從而使其能夠被添加到依賴圖中(并且最終添加到 bundle 中)(use 屬性)

??在開始下面的代碼之前,我們需要安裝 style-loadercss-loader

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

并在項目中創(chuàng)建 style.css 樣式文件:

h1 {
  color: red;
}

??然后在 webpack.config.js 中輸入以下代碼:

const path = require('path');

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

Plugins 插件

?? Loaders 被用于轉換某些類型的模塊,而插件則可以用于執(zhí)行范圍更廣的任務。插件的范圍包括,從打包優(yōu)化和壓縮,一直到重新定義環(huán)境中的變量。

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

??在開始下面的代碼之前,我們需要安裝 html-webpack-plugin 插件:

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

它可以簡化 HTML 文件的創(chuàng)建,為您的 webpack 包提供服務。

??然后在 webpack.config.js 中輸入以下代碼:

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

const config = {
  entry: './main.js',
  output: {
    path: path.resolve(__dirname, 'dist'),
    filename: 'bundle.js',
  },
  module: {
    rules: [
      {
        test: /\.css$/,
        use: [{ loader: 'style-loader' }, { loader: 'css-loader' }],
      },
    ],
  },
  plugins: [new HtmlWebpackPlugin({ template: './index.html' })],
};

module.exports = config;

運行與配置

?? 最后我們可以直接通過 webpack 命令編譯打包,如果想要在其命令后加入?yún)?shù),可以通過配置 package.json 文件中的 scripts 屬性:

{
  "scripts": {
    "build": "webpack --config webpack.config.js --progress --display-modules"
  }
}

當然如果你想要更改默認的配置文件名稱,可以將 --config 后面的 webpack.config.js 配置文件名改為你自定義的名稱。

??通過以下命令執(zhí)行:

    $ npm run build

多入口設置與 html-webpack-pugin 插件詳解

??我們可以為 entry 指定多個入口。在開始代碼之前,我們需要創(chuàng)建如下目錄解構

<pre>
.
├── index.html // 顯示的頁面
├── main1.js // webpack 入口1
├── main1.js // webpack 入口2
├── style.css // 樣式文件
└── webpack.config.js // webpack 中默認的配置文件
</pre>

??我們在 index.html 文件中輸入以下內容:

<!DOCTYPE html>
<html lang="en">
  <head>
    <meta charset="UTF-8" />
    <meta name="viewport" content="width=device-width, initial-scale=1.0" />
    <meta http-equiv="X-UA-Compatible" content="ie=edge" />
    <title>demo3</title>
  </head>
  <body></body>
</html>

??我們在 main1.js 文件中輸入以下內容:

    improt './style.css'

    var h1 = document.createElement('h1');
    h1.innertHTML = '這是 main1.js 中的內容';
    document.body.appendChild(h1);

??我們在 main2.js 文件中輸入以下內容:

    improt './style.css'

    var h2 = document.createElement('h2');
    h2.innertHTML = '這是 main2.js 中的內容';
    document.body.appendChild(h2);

??我們在 style.css 文件中輸入以下內容:

h1 {
  color: red;
}
h2 {
  color: blue;
}

??我們在 webpack.config.js 文件中輸入以下內容:

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

const config = {
  entry: {
    bundle1: './main1.js',
    bundle2: './main2.js',
  },
  output: {
    path: path.resolve(__dirname, 'dist'),
    filename: '[name].js',
  },
  module: {
    rules: [{ test: /\.css$/, loader: 'style-loader!css-loader' }],
  },
  pugins: [new HtmlWebpackPlugin({ template: './index.html' })],
};

module.exports = config;

??完成上面的代碼工作后,運行 webapck 命令,我們打開 dist 文件中的 index.html

運行的結果并不是我們預期的那樣展示 h1 的內容在前,h2 內容在后,打開生成后的 index.html 源碼:

<!DOCTYPE html>
<html lang="en">
  <head>
    <meta charset="UTF-8" />
    <meta name="viewport" content="width=device-width, initial-scale=1.0" />
    <meta http-equiv="X-UA-Compatible" content="ie=edge" />
    <title>demo3</title>
  </head>
  <body>
    <script type="text/javascript" src="bundle2.js"></script>
    <script type="text/javascript" src="bundle1.js"></script>
  </body>
</html>

從源碼中便可得知,先引入的 bundle2.js 文件,也就是 main2.js 的內容,后引入的 bundle1.js 文件,也就是 main1.js 的內容。

??我們并沒有在 index.html 中輸入任何引入 JavaScript 文件的代碼,那么使用 webpack 打包后生成的文件,是怎么引入 JavaScript 文件的呢。事實上就是通過 html-webpack-plugin 為我們生成的 index.html 。

html-webpack-plugin 中的參數(shù)詳解

??通過 npm 中的介紹,html-webpack-plugin 是一個 webpack 插件,可以簡化 HTML 文件的創(chuàng)建,為我們的 webpack 包提供服務,它包含了一個改變每個編譯的文件名參數(shù)。使用 lodash 模板提供我們自己的模板或者使用自己的 loader

??我們可以配置以下參數(shù)傳遞給 HtmlWebpackPlugin

  • title: 用于生成的 HTML 文檔的標題。
  • filename: 要寫入 HTML 的文件。默認為 index.html 。你也可以在這里指定一個子目錄(例如:assets / admin.html)。
  • template: 引入的模板文件,具體內容可以查看文檔。
  • inject: true | 'head' | 'body' | false,指定引入 JavaScript 腳本文件,在生成的HTML 中的位置。默認為 true,指JavaScript 腳本文件在 <body> 元素中引入;head ,指JavaScript 腳本文件在 <head> 元素中引入,bodytrue 值相同;false 指只生成 HTML 文件,不引入任何JavaScript 腳本文件。
  • favicon: 生成的 HTML 文件中的圖標路徑。
  • minify: {...} | false 是否對生成的 HTML 文件壓縮,默認為 false,具體配置可查看 html-minifier
  • hash: true | false ,如果為 true ,給生成的 js 文件一個獨特的 hash 值,該 hash 值是該次 webpack 編譯的 hash 值,這對緩存清除非常有用。默認值為 false
  • cache: true | false, 如果為 true 則只編譯生成更改的內容將文件,默認值為 true。
  • showErrors:true | false,如果為 true,則將錯誤內容添加到 HTML 中,默認值為 true。
  • chunks: 指定引入的 JavaScript 腳本文件(例如:[ 'bundle1', 'bundle2' ])。
  • chunksSortMode: 'none' | 'auto' | 'dependency' |'manual' | {function} - default: 'auto',對引入的 chunks 進行排序,具體可以查看該文檔。
  • excludeChunks: 排除掉指定的 JavaScript 腳本文件(例如:[ 'bundle1', 'bundle2' ])。
  • xhtml: true | false,默認值是 false ,如果為 true ,則以兼容 xhtml 的模式引用文件。

??現(xiàn)在我們知道了 html-webpack-plugin 中的參數(shù),下面我們就來修改 webpack.config.js 中的內容:

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

const config = {
  entry: {
    bundle1: path.resolve(__dirname, 'main1.js'),
    bundle2: path.resolve(__dirname, 'main2.js'),
  },
  output: {
    path: path.resolve(__dirname, 'dist'),
    filename: '[name].js',
  },
  module: {
    rules: [{ test: /\.css$/, loader: 'style-loader!css-loader' }],
  },
  plugins: [
    new HtmlWebpackPlugin({
      title: '多文件引入', // 生成 html 的標題
      filename: 'index.html', // 生成 html 文件的名稱
      template: path.resolve(__dirname, 'index.html'), // 根據(jù)自己的指定的模板文件來生成特定的 html 文件
      // inject: true, // 注入選項 有四個值 ture: 默認值,script標簽位于html文件的 body 底部, body: 同 true, head: script標簽位于html文件的 head 底部,false:不注入script標簽
      favicon: path.resolve(__dirname, 'favicon.ico'), // 生成的 html 文件設置 favicon
      minify: {
        caseSensitive: false, //是否大小寫敏感
        collapseBooleanAttributes: true, //是否簡寫boolean格式的屬性如:disabled="disabled" 簡寫為disabled
        collapseWhitespace: true, //是否去除空格
      },
      hash: true, // hash選項的作用是 給生成的 js 文件一個獨特的 hash 值,該 hash 值是該次 webpack 編譯的 hash 值。默認值為 false
      cache: true, // 默認值是 true。表示只有在內容變化時才生成一個新的文件
      showErrors: true, // showErrors 的作用是,如果 webpack 編譯出現(xiàn)錯誤,webpack會將錯誤信息包裹在一個 pre 標簽內,屬性的默認值為 true
      chunks: ['bundle1', 'bundle2'], // 指定引入的 js 文件
      //excludeChunks:[ 'bundle1' ], // 排除掉某些 js 文件
      /**
       * script 標簽的引用順序
       * 'dependency' 按照不同文件的依賴關系來排序
       * 'auto' 默認值,插件的內置的排序方式
       * 'none'
       * 'manual'
       * funciton 自定義排序,與JS中自定義數(shù)組的sort回調一個含義, 具體可以看 https://github.com/jantimon/html-webpack-plugin/issues/481
       */
      chunksSortMode: function(chunk1, chunk2) {
        var orders = ['bundle1', 'bundle2'];
        var order1 = orders.indexOf(chunk1.names[0]);
        var order2 = orders.indexOf(chunk2.names[0]);
        return order1 - order2;
      },
      xhtml: false, // 一個布爾值,默認值是 false ,如果為 true ,則以兼容 xhtml 的模式引用文件
    }),
  ],
};

module.exports = config;

??完成上面的代碼工作后,運行 webapck 命令,我們打開 dist 文件中的 index.html。

??Nice!與我們的預期效果顯示一致。在對 html-webpack-plugin 的介紹中,提到了 lodash 模板, 那么該怎么用呢?我們再次修改 webpack.config.js 中的內容,為 HtmlWebpackPlugin 傳入 Date 參數(shù):

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

const config = {
  entry: {
    bundle1: path.resolve(__dirname, 'main1.js'),
    bundle2: path.resolve(__dirname, 'main2.js'),
  },
  output: {
    path: path.resolve(__dirname, 'dist'),
    filename: '[name].js',
  },
  module: {
    rules: [{ test: /\.css$/, loader: 'style-loader!css-loader' }],
  },
  plugins: [
    new HtmlWebpackPlugin({
      date: new Date(),
      title: '多文件引入', // 生成 html 的標題
      filename: 'index.html', // 生成 html 文件的名稱
      template: path.resolve(__dirname, 'index.html'), // 根據(jù)自己的指定的模板文件來生成特定的 html 文件
      // inject: true, // 注入選項 有四個值 ture: 默認值,script標簽位于html文件的 body 底部, body: 同 true, head: script標簽位于html文件的 head 底部,false:不注入script標簽
      favicon: path.resolve(__dirname, 'favicon.ico'), // 生成的 html 文件設置 favicon
      minify: {
        caseSensitive: false, //是否大小寫敏感
        collapseBooleanAttributes: true, //是否簡寫boolean格式的屬性如:disabled="disabled" 簡寫為disabled
        collapseWhitespace: true, //是否去除空格
      },
      hash: true, // hash選項的作用是 給生成的 js 文件一個獨特的 hash 值,該 hash 值是該次 webpack 編譯的 hash 值。默認值為 false
      cache: true, // 默認值是 true。表示只有在內容變化時才生成一個新的文件
      showErrors: true, // showErrors 的作用是,如果 webpack 編譯出現(xiàn)錯誤,webpack會將錯誤信息包裹在一個 pre 標簽內,屬性的默認值為 true
      chunks: ['bundle1', 'bundle2'], // 指定引入的 js 文件
      //excludeChunks:[ 'bundle1' ], // 排除掉某些 js 文件
      /**
       * script 標簽的引用順序
       * 'dependency' 按照不同文件的依賴關系來排序
       * 'auto' 默認值,插件的內置的排序方式
       * 'none'
       * 'manual'
       * funciton 自定義排序,與JS中自定義數(shù)組的sort回調一個含義, 具體可以看 https://github.com/jantimon/html-webpack-plugin/issues/481
       */
      chunksSortMode: function(chunk1, chunk2) {
        var orders = ['bundle1', 'bundle2'];
        var order1 = orders.indexOf(chunk1.names[0]);
        var order2 = orders.indexOf(chunk2.names[0]);
        return order1 - order2;
      },
      xhtml: false, // 一個布爾值,默認值是 false ,如果為 true ,則以兼容 xhtml 的模式引用文件
    }),
  ],
};

module.exports = config;

更改 index.html 中的內容,lodash 模板默認支持的是 ejs 模板的語法:

<!DOCTYPE html>
<html lang="en">
  <head>
    <meta charset="UTF-8" />
    <meta name="viewport" content="width=device-width, initial-scale=1.0" />
    <meta http-equiv="X-UA-Compatible" content="ie=edge" />
    <title>demo3</title>
  </head>
  <body>
    <%= htmlWebpackPlugin.options.date %>
  </body>
</html>

??完成上面的代碼工作后,運行 webapck 命令,我們打開 dist 文件中的 index.html。

??通過運行結果,我們可以發(fā)現(xiàn)在頂部輸出了當前時間,也就是 HtmlWebpackPlugin 傳入的參數(shù),實際上 HtmlWebpackPlugin 中的參數(shù)都可以通過 htmlWebpackPlugin.options.參數(shù)名稱 輸出,我就不一一列舉。

Babel

??Babel 是一個工具鏈,主要用于在舊的瀏覽器或環(huán)境中將 ECMAScript 2015+ 代碼轉換為向后兼容版本的 JavaScript 代碼。

安裝依賴包

npm i -D babel-loader @babel/core @babel/preset-env
  • babel-loader:用于 babel 在 webapck 中的加載模塊。
  • @babel/core:babel 編譯工具;
  • @babel/preset-env:babel 生成指定支持瀏覽器版本的編譯工具。

babel 支持兩種配置,一種是在項目根目錄下創(chuàng)建 .babelrc 配置文件,另一種是通過 webpack loader options 的方式配置

  • .babelrc
{
  "presets": [
    [
      "@babel/preset-env",
      {
        // 用于指定瀏覽器版本號
        "targets": {
          "browsers": "last 2 version"
        }
      }
    ]
  ]
}
  • webpack loader options 配置
{
  "module": {
    "rules": [
      {
        "test": /\.js$/,
        "exclude": /(node_modules)/,
        "loader": "babel-loader",
        "options": {
          "presets": [
            [
              "@babel/preset-env",
              {
                // 用于指定瀏覽器版本號
                "targets": {
                  "browsers": "last 2 version"
                }
              }
            ]
          ]
        }
      }
    ]
  }
}

Babel 默認只轉換語法,而不轉換新的 API,如 Set、Promise、Map 等或是 ES6 對 Array、String 等擴展,如果需要使用新的 API 還需要使用對相應的 轉換插件 或 polyfill

  • Babel Polyfill
npm i --save @babel/polyfill

只需在入口頭部引入即可

import '@babel/polyfill';
// 或
require('@babel/polyfill');

上面的使用方法,會導致打包后的文件過大,由于是在入口文件直接引入 polyfill,從而將會導入 polyfill 整個包,增加了無用代碼。

@babel/preset-envuseBuiltIns: 'usage' 按需引入,就是用于解決上面的問題。

{
  "presets": [
    [
      "@babel/preset-env",
      {
        // 用于指定瀏覽器版本號
        "targets": {
          "browsers": "last 2 version"
        },
        "useBuiltIns": "usage"
      }
    ]
  ]
}

@babel/polyfill 是通過改寫全局 prototype 的方式對新的 API 支持,比較適合單獨運行的項目。

  • Babel RunTime Transform
npm i @babel/runtime --save
npm i @babel/plugin-transform-runtime --save-dev
{
  "presets": [
    [
      "@babel/preset-env",
      {
        // 用于指定瀏覽器版本號
        "targets": {
          "browsers": "last 2 version"
        }
      }
    ]
  ],
  "plugins": ["@babel/plugin-transform-runtime"]
}

@babel/runtime 的 polyfill 對象是臨時構造并 import/require 的,因此并不是真正的全局引用,由于不是全局引用,對于實例化對象的方法,并不能生效。比較適合編寫 第三方類庫。

文件操作

file-loader 可以解析項目中的 url 引入(不禁限于 CSS),根據(jù)配置,將圖片拷貝到相應路徑,并修改打包后文件的引用路徑。

{
  "test": /\.(png|jpe?g|gif)$/,
  "loader": "file-loader",
  "options": {
    // 輸出文件名稱,默認為:'[hash].[ext]'
    "name": "[name].[hash:8].[ext]",
    // 指定輸出文件存放路徑,默認為當前目錄
    "outputPath": "assets/images",
    // 指定輸出文件 公共路徑
    // publicPath: '../',
    // 如果是 true,生成一個文件(向文件系統(tǒng)寫入一個文件)。 如果是 false,loader 會返回 public UR不會生成文件
    "emitFile": true
  }
}

url-loaderfile-loader 的作用是一樣的,但是 url-loader 可以通過 limit 配置限制輸出大小,如果小于 limit 字節(jié)的文件會被轉成 Base64 編碼,大于則會拷貝文件。

{
  "test": /\.(png|jpe?g|gif)$/,
  "loader": "url-loader",
  "options": {
    // 限制輸出文件大小,如果輸出文件小于該限制,則被轉成 base64 編碼,大于則會拷貝文件
    "limit": 1024,
    // 輸出文件名稱,默認為:'[hash].[ext]'
    "name": "[name].[hash:8].[ext]",
    // 指定輸出文件存放路徑,默認為當前目錄
    "outputPath": "assets/images"
    // 指定輸出文件 公共路徑
    // publicPath: '../',
  }
}

img-loader 可以壓縮圖片,也可以使用 image-webpack-loader 代替。

npm i -D img-loader imagemin imagemin-gifsicle imagemin-mozjpeg imagemin-pngquant imagemin-svgo

插件具體參數(shù)可以查看 github

{
  "loader": "img-loader",
  "options": {
    "plugins": [
      require("imagemin-gifsicle")({}),
      require("imagemin-mozjpeg")({}),
      require("imagemin-pngquant")({}),
      require("imagemin-svgo")({})
    ]
  }
}

html-loader 可以設置 html 中的某些片段是要交給 webpack 處理的。

{
  "test": /\.html$/,
  "loader": "html-loader",
  "options": {
    // 設置交由 webpack 處理的屬性
    "attrs": ["img:src", "img:data-src"]
  }
}

當我們在使用第三方庫時,如果每個模塊都用到了它,但我們并不想在每個模塊中重復去寫 importrequire
,我們可以使用下面兩種方式:

  • webpack.ProvidePlugin
{
  "resolve": {
    "alias": {
      // 定義別名 指定本地文件路徑,$ 表示將 jquery 關鍵字解析到某個目錄的文件下,而不是解析某個目錄
      "jquery$": path.join(__dirname, "./lib/jquery.min.js")
    }
  },
  "plugins": [
    new webpack.ProvidePlugin({
      // "$": "jquery", // 導入 npm 依賴包
      // "$": ["./lib/jquery.min.js"] // 指定本地文件路徑
      "$": "jquery"
    })
  ]
}
  • imports-loader
npm i imports-loader
{
  "test": /\.js$/,
  "loader": "imports-loader",
  "options": {
    "$": "jquery"
  }
}

CSS

mini-css-extract-plugin 能夠將 CSS 提取到單獨的文件中,它為每個包含 CSS 的 JS 文件創(chuàng)建了一個 CSS 文件。

npm install --save-dev mini-css-extract-plugin
const MiniCssExtractPlugin = require('mini-css-extract-plugin');
{
  "module": {
    "rules": {
      "test": /\.css$/,
      "use": [
        // mini-css-extract-plugin 只能用于生產環(huán)境,因此在 loader 中需要加入一個判斷
        process.env.NODE_ENV === "development"
          ? "style-loader"
          : {
              "loader": MiniCssExtractPlugin.loader,
              "options": {
                // CSS 樣式用的背景圖片 會與抽取后的 CSS 文件路徑有沖突,在此設置公共相對路徑
                "publicPath": "../../"
              }
            }
      ]
    }
  },
  "plugins": [
    new MiniCssExtractPlugin({
      // 用于設置提取的 CSS 文件名稱與存儲路徑
      "filename": "assets/styles/[name].[hash:8].css",
      // 用于設置異步加載 CSS 文件名稱與存儲路徑,例如: import('./assets/styles/test.js')
      "chunkFilename": "assets/styles/[id].[chunkhash:8].css"
    })
  ]
}

optimize-css-assets-webpack-plugin 能夠壓縮 CSS 文件大小。

npm install --save-dev optimize-css-assets-webpack-plugin
const OptimizeCssAssetsPlugin = require('optimize-css-assets-webpack-plugin');
{
  "plugins": [
    new OptimizeCssAssetsPlugin({
      // 匹配需要壓縮的 CSS 文件正則,默認為 /\.css$/g
      // assetNameRegExp: /\.optimize\.css$/g,
      // 設置 CSS 壓縮工具 默認使用的是  cssnano
      // cssProcessor: require('cssnano'),
      // 設置 CSS 壓縮工具的 options ,默認 {}
      // cssProcessorOptions: {},
      // 設置 CSS 壓縮工具插件的 options, 默認 {}
      // cssProcessorPluginOptions: {},
      // 插件輸出信息是否輸出在控制臺,默認 true
      // canPrint: true,
    })
  ]
}

PostCSS 是一個允許使用 JS 插件轉換樣式的工具。

npm install postcss postcss-loader --save-dev
  • autoprefixer 添加了 vendor 瀏覽器前綴,它使用 Can I Use 上面的數(shù)據(jù)。
npm install autoprefixer --save-dev
{
  "module": {
    "rules": [
      {
        "test": /\.css$/,
        "use": [
          "style-loader",
          "css-loader",
          {
            "loader": "postcss-loader",
            "options": {
              "ident": "postcss",
              plugins:[
                require('autoprefixer')({
                  // 設置兼容瀏覽器版本
                  "browsers": ['last 2 versions'],
                })
              ]
              "sourceMap": true
            }
          }
        ]
      }
    ]
  }
}

不難發(fā)現(xiàn) autoprefixer 中需要配置 browsers,Babel 中也需要配置,我們可以通過下面兩種方法配置:

  • package.json
  "browserslist": [
    "last 2 version"
  ]
  • .browserslistrc
last 2 version
  • postcss-preset-env 能夠轉換還未兼容 CSS 例如 --color 變量。

postcss-preset-env 中已經引入了 autoprefixer,因此使用它的時候可以不用引入 autoprefixer。

npm install postcss-preset-env --save-dev
{
  "module": {
    "rules": [
      {
        "test": /\.css$/,
        "use": [
          "style-loader",
          "css-loader",
          {
            "loader": "postcss-loader",
            "options": {
              "ident": "postcss",
              plugins:[
                require('postcss-preset-env')
              ]
              "sourceMap": true
            }
          }
        ]
      }
    ]
  }
}

Webpack 打包優(yōu)化

  • 提取公共代碼:減少代碼冗余,提高用戶下載代碼帶寬。

Webpack 4 提供了內置插件 webpack.SplitChunksPlugin

{
  "optimization": {
    "splitChunks": {
      /**
       * 拆分塊的名稱:boolean: true | function (module, chunks, cacheGroupKey) | string
       * 如果為 true 將自動生成基于塊和緩存組密鑰的名稱,生產環(huán)境下建議 false 避免更改名稱
       * 如果是一個函數(shù)允許自定義名稱,不推薦,容易造成代碼下載冗余
       */
      "name": false,
      "automaticNameDelimiter": "~", // 用于指定生成名稱的分隔符
      "chunks": "initial", // 用于指定那些模塊需要分割, 可選項 "initial"(初始化) | "all"(默認) | "async"(動態(tài)加載)
      "maxAsyncRequests": 1, // 按需加載時的最大并行請求數(shù)。
      "maxInitialRequests": 1, // 入口點處的最大并行請求數(shù)。
      "minChunks": 1, // 分割前必須共享模塊的最小塊數(shù)。
      // 緩存組會繼承splitChunks的配置,但是test、priorty和reuseExistingChunk只能用于配置緩存組。
      "cacheGroups": {
        // 自定義緩存組屬性
        "vendors": {
          "test": /[\\/]node_modules[\\/]/, // 控制此緩存組選擇的模塊,使用正則表達式匹配
          "filename": "venders", // 拆分出塊的名稱
          "reuseExistingChunk": true, // 可設置是否重用已用chunk 不再創(chuàng)建新的chunk
          "priority": "0" // 優(yōu)先級高的chunk為被優(yōu)先選擇,優(yōu)先級一樣的話,size大的優(yōu)先被選擇
          // enforce: true, // 忽略外部 maxAsyncRequests、maxInitialRequests、minSize、minChunks 始終以 緩存組配置執(zhí)行
        }
      }
    }
  }
}
  • 代碼分割與懶加載:能夠在最短的時間內,展示頁面。通過動態(tài)加載模塊的方式實現(xiàn)。

    • webpack 內置 methods

      /**
       * 動態(tài)引入 模塊
       * dependencies: [] 需要加載的模塊,并不會執(zhí)行,需要在回調函數(shù)中 require
       * callback: 回調函數(shù),執(zhí)行動態(tài)加載模塊的方法
       * errorCallback: 錯誤回調,可以省略
       * chunkName: 拆分塊名稱
       */
      require.ensure(dependencies, callback, errorCallback, chunkName)
      
      // 只接收引入的模塊 而不執(zhí)行,當兩個子模塊同時依賴一個模塊,可以通過這種引用將公共模塊提前引入到父模塊中
      require.include(dependencies)
      
    • ES2015 Loader 規(guī)范

      import(/* webpackChunkName: 生成的 chunk 名稱 */).then() // 此時引入的 模塊已經執(zhí)行
      
?著作權歸作者所有,轉載或內容合作請聯(lián)系作者
【社區(qū)內容提示】社區(qū)部分內容疑似由AI輔助生成,瀏覽時請結合常識與多方信息審慎甄別。
平臺聲明:文章內容(如有圖片或視頻亦包括在內)由作者上傳并發(fā)布,文章內容僅代表作者本人觀點,簡書系信息發(fā)布平臺,僅提供信息存儲服務。

相關閱讀更多精彩內容

友情鏈接更多精彩內容