值得了解的webpack高階技能

yiliwei發表於2018-05-07

只要這幾步,webpack速成不是事兒一文中, 筆者簡單介紹了webpack的常見用法。能滿足最基本的開發需求。在這篇文章中,再來談談一些較高階的應用。

1. 配置不同環境的構建指令碼

之前構建都是通過 npx webpack ... 這樣的方式執行構建命令。可能你會覺得這樣的方式不夠高效。甚至在某些特定的情況下還需要設定 Node 的環境變數。根據環境變數值的不同,設定構建 --mode 的不同。

scripts 配置的入口在 package.json 中,另外再介紹下。通過執行 npm i 預設安裝package.json中全部依賴。

 "scripts": {
    "build": "webpack",
    "dev": "webpack-dev-server"
  },
複製程式碼

cross-env 設定node環境變數的外掛 npm i cross-env -D

  • 開發環境構建: npm run dev

值得了解的webpack高階技能

通過圖中可以看到,npm run dev 相當於 npx webpack-dev-server

  • 生產環境構建: npm run build

1.1 設定node環境變數

  "scripts": {
    "dev": "cross-env NODE_ENV=development webpack-dev-server",
    "build": "cross-env NODE_ENV=production webpack"
  },
複製程式碼

先安裝外掛cross-env

npm i cross-env -D

執行 npm run dev, 先設定環境變數 NODE_ENV=devlelopment, 然後執行 webpack 命令。

為什麼要設定環境變數呢? 設定環境變數後,可以在打包構建時執行相應環境變數下的指令碼。

值得了解的webpack高階技能
在webpack.config.js中 process.env.NODE_ENV 拿到環境變數值。做你想做的事情。哈哈哈~

值得了解的webpack高階技能

  • 通過 webpack 自帶的外掛定義全域性變數 _DEV_ ,類似於全域性變數 window
//應用: 在專案 src/index.js 檔案中程式碼裡區分開發或者是生產環境
 if(__DEV__){
     alert('開發環境')
 }else{
     alert('生產環境')
 }
複製程式碼
  • 根據環境不同,來設定某些外掛是否可用

在webpack.config.js檔案中,設定變數 let isDev = process.env.NODE_ENV === 'development',然後定義外掛的disable屬性的值

  plugins: [
        new ExtractTextWebpackPlugin({
            filename: 'css/index.css',
            disable: isDev
        }),
    ],
複製程式碼

1.2 構建 DLL 動態連結庫

.dll 為字尾的檔案稱為動態連結庫,在一個動態連結庫中可以包含給其他模組呼叫的函式和資料

  • 基礎模組獨立出來打包到單獨的動態連線庫裡
  • 當需要匯入的模組在動態連線庫裡的時候,模組不能再次被打包,而是去動態連線庫裡獲取 dll-plugin
  • 提高構建效率

react, react-dom 為例

  • 建立 webpack.config.react.js
let path = require('path');
let webpack = require('webpack');

module.exports  = {
    entry: {
        vendor: ['react', 'react-dom']
    },
    output: {
        filename: '[name].js',
        path: path.join(__dirname, 'dist'),
        libraryTarget: "var",  //構建後輸出js檔案所屬規範, 如果 設定為 commonjs, 則輸出檔案符合 commonjs規範
        library: '_dll_[name]', //構建後輸出js庫的名字
      
    },
    mode: 'development',
    plugins: [
        new webpack.DllPlugin({
            name: '_dll_[name]',
            path: path.join(__dirname, 'dist', '[name].manifest.json')
        })
    ],
};
複製程式碼
  • 設定構建命令
 "scripts": {
    "build": "webpack",
    "dev": "webpack-dev-server",
    "react": "wepack --config webpack.config.react.js"
  },
複製程式碼
  • 執行 npm run react

值得了解的webpack高階技能

  • 輸出的 vendor.js

值得了解的webpack高階技能

1.3 使用動態連結庫

在webpack.config.js中配置外掛

plugins: [
    new webpack.DllReferencePlugin({
        manifest: path.resolve(__dirname, 'dist', 'vendor.manifest.json')
    })
]
複製程式碼
  • react編譯需要安裝如下loader

npm i babel-core babel-loader babel-preset-env babel-preset-react babel-preset-stage-0 -D

  • 配置相關rules
   module: {
        rules: [
            {
                test: /\.jsx?/,
                use: 'babel-loader',
                exclude:/node_modules/,
                include:/src/
            },
        ]
    },
複製程式碼
  • 在專案根目錄下建立 .babelrc檔案
{
    "presets": [
        "env",
        "stage-0",
        "react"
    ]
}
複製程式碼

值得了解的webpack高階技能

  • 執行構建命令 npm run build, 控制檯列印:
[./node_modules/react-dom/index.js] delegated ./node_modules/react-dom/index.js from dll-reference _dll_vendor 42 bytes {index} [built]
[./node_modules/react/index.js] delegated ./node_modules/react/index.js from dll-reference _dll_vendor 42 bytes {index} [built]
複製程式碼

由此可以看出專案構建時是使用的預先構建好的react庫檔案,從控制檯列印的構建耗時也明顯不同。

2. 抽離公共程式碼(庫檔案或元件)

  • 建立src/a.js, src/x.js 檔案
  • 在src/index.js 和 src/x.js檔案中都import a.js檔案
  • 在src/index.js 和 src/x.js檔案中都引入 react庫檔案

index.js

import './index.css';
// if(module.hot){
//    console.log('熱更新');
// }
import React, { Component } from 'react'
import { render } from 'react-dom'
import a from './a.js'

render(<h1>hello zfpx</h1>, window.app)
複製程式碼

x.js

import React, { Component } from 'react'
import { render } from 'react-dom'

import a from './a.js'
複製程式碼

a.js

module.exports = {
    a: 'a.js'
}
複製程式碼

在webpack.config.js中配置:

    optimization: {
        splitChunks: {
            cacheGroups: {
                commons: { //提供公共元件, 只要超出0位元組就生產新的包
                    chunks: 'initial',
                    //miniChunks: 2,
                    //maxInitalRequest: 5,
                    name: 'commons',
                    minSize: 0 
                },
                vendor: {// 抽離第三外掛
                    test: /node_modules/,
                    chunks: 'initial',
                    name: 'vender',
                    priority: 10,
                    enforce: true
                }
            }
        }
    },
複製程式碼

執行構建指令碼 npm run build, 控制檯列印:

值得了解的webpack高階技能

如果註釋相關構建資訊:

值得了解的webpack高階技能
從二者控制檯資訊中可以看出,構建後的index.js 和 x.js檔案體積大小和 dist目錄中輸出的檔案數量是不一樣的。

3. 設定全域性變數

在index.js中引入 jquery.js後,在a.js檔案中想不引入jquery.js直接使用jquery物件

webpack.config.js中配置

module: {
    rules: [
        {
        test: /jquery/,
        use:[{
            loader:'expose-loader',
            options:'$'
        }]
        },
    ]
}
複製程式碼

或者使用webpack提供的內建外掛

 // 提供全域性變數外掛
        new webpack.ProvidePlugin({
            $:'jquery'
        }),
複製程式碼

二者的區別是神馬呢

  • 使用外掛時:
1. $不會定義到window上
2. 只要是用到jquery(或者 $)的地方,當前bundle.js都會把jquery打包進去
複製程式碼

webpack.config.js 相關配置為:

entry: {
        index: './src/index.js',
        x: './src/x.js'
    },
 output: {
    filename: '[name].[hash].bundle.js',
    path: path.resolve(__dirname, 'dist'),
    library: '_dll_[name]'
},
...
pugins: [
 new HtmlWebpackPlugin({
            template: 'src/index.html',
            hash: true
        }),
]
複製程式碼

index.js

console.log($)
複製程式碼

x.js

console.log($);
複製程式碼

值得了解的webpack高階技能

從構建的日誌中可以看出,index.js構建後的檔案大小和x.js構建的檔案大小一致

  • 使用 expose-loader
1.匯入一次,就會暴露出來
2.$會定義到在window上
複製程式碼

index.js

import $ from 'jquery'
console.log('index.js');
console.log($);
複製程式碼

x.js

console.log('x.js');
console.log($);
console.log(window.$);
複製程式碼

構建日誌:

值得了解的webpack高階技能

瀏覽器列印日誌:

值得了解的webpack高階技能

從構建的日誌中可以看出,index.js構建後的檔案大小和x.js構建的檔案大小不一致 從瀏覽器列印日誌可以看出,x.js在沒有直接import jquery時,也可以拿到jquery物件

4. 小結

webpack還有很多其他適用的功能,比如在使用vue時,常常會根據路由載入相應的元件js也就是我們所說的 按需載入。可以用到webpack程式碼分離中的 動態匯入和懶載入(dynamic imports),優化程式碼載入,提升效能。

相關文章