1. 程式人生 > >webpack學習(七):啟用 HMR(模組熱替換)

webpack學習(七):啟用 HMR(模組熱替換)

demo地址: https://github.com/Lkkkkkkg/webpack-demo
上次使用 webpack-dev-serve : https://blog.csdn.net/qq593249106/article/details/84922572

當前目錄結構 :

|- /dist //用於放打包後文件的資料夾
  |- app.bundle.js //出口檔案
  |- print.bundle.js //出口檔案
  |- index.html //模板檔案
|- /node_modules
|- /src //用於放原始檔的資料夾
  |- index.js //入口檔案
  |- print.js 
|
- package.json |- webpack.config.js //webpack配置檔案

webpack.config.js

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


module.exports = {
    entry: {
        app: './src/index.js'
        // print: './src/print.js'
}, devtool: 'inline-source-map', // 不同選項適用於不同環境 devServer: { contentBase: './dist', //將dist目錄下的檔案(index.html)作為可訪問檔案, 如果不寫這個引數則預設與webpack.cofig.js的同級目錄 port: 8080 //埠號設為8080, 預設也是8080 }, plugins: [ //webpack 通過 plugins 實現各種功能, 比如 html-webpack-plugin 使用模版生成 html 檔案 new CleanWebpackPlugin
(['dist']), //設定清除的目錄 new HtmlWebpackPlugin({ filename: 'index.html', //設定生成的HTML檔案的名稱, 支援指定子目錄,如:assets/admin.html title: 'Form HtmlWebpackPlugin', //設定生成的HTML的title }) ], output: { filename: '[name].bundle.js', //根據入口檔案輸出不同出口檔案 path: path.resolve(__dirname, 'dist') } };

這裡把配置的 入口檔案 print.js 給去掉了, 因為 print.js 被 入口檔案 index.js 應用了

HMR(模組熱替換)

模組熱替換(Hot Module Replacement 或 HMR)是 webpack 提供的最有用的功能之一, 它允許在執行時更新各種模組, 而無需進行完全重新整理
比如在這個現在修改 print.js 裡面的內容, 使用 HMR 就可以只更新 print.js 裡面的內容而不用重新加在整個頁面

啟用HMR

啟用這個功能很簡答, 只需要修改一下 webpack.config.js 的配置, 使用 webpack 內建的 HMR 外掛就可以了
webpack.config.js

const path = require('path');
const HtmlWebpackPlugin = require('html-webpack-plugin');
const CleanWebpackPlugin = require('clean-webpack-plugin');
const webpack = require('webpack'); //引入 webpack


module.exports = {
    entry: {
        app: './src/index.js'
        // print: './src/print.js'
    },
    devtool: 'inline-source-map', // 不同選項適用於不同環境
    devServer: {
        contentBase: './dist', //將dist目錄下的檔案(index.html)作為可訪問檔案, 如果不寫這個引數則預設與webpack.cofig.js的同級目錄
        port: 8080, //埠號設為8080, 預設也是8080,
        hot: true 
    },
    plugins: [ //webpack 通過 plugins 實現各種功能, 比如 html-webpack-plugin 使用模版生成 html 檔案
        new CleanWebpackPlugin(['dist']), //設定清除的目錄
        new HtmlWebpackPlugin({
            filename: 'index.html', //設定生成的HTML檔案的名稱, 支援指定子目錄,如:assets/admin.html
            title: 'Form HtmlWebpackPlugin', //設定生成的HTML的title
        }),
        new webpack.HotModuleReplacementPlugin() //啟用 webpack 內建的 HMR外掛
    ],
    output: {
        filename: '[name].bundle.js', //根據入口檔案輸出不同出口檔案
        path: path.resolve(__dirname, 'dist')
    }
};

啟用 webpack 內建的 HMR外掛後, module.hot 介面就會暴露在 index.js 中, 接下來需要在 index.js 中配置告訴 webpack 接受HMR的模組( print.js ):
index.js

import _ from 'lodash';
import printMe from './print.js';

function component() {
    var element = document.createElement('div');
    var btn = document.createElement('button'); //新建一個button物件

    element.innerHTML = _.join(['Hello', 'webpack'], ' '); //要用到lodash的語法

    btn.innerHTML = 'Click me and check the console!';
    btn.onclick = printMe; //button觸發的事件是引用的print.js暴露的事件

    element.appendChild(btn); //把button物件插入div中

    return element;
}

document.body.appendChild(component());

if (module.hot) {
    module.hot.accept('./print.js', function() { //告訴 webpack 接受熱替換的模組
        console.log('Accepting the updated printMe module!');
        printMe();
    })
}

啟動 webpack-dev-server

終端輸入 npm run dev 啟動, 開啟 index.html, 然後去修改 print.js 裡面的內容:
print.js

export default function printMe() {
    //console.log('I get called from print.js!');
    console.log('change');
}

回到網頁看控制檯:
在這裡插入圖片描述
可以看到 HMR 的字眼, 並且看到了 index.js下的輸出(‘Accepting the updated printMe module!’) 和 print.js 修改後的輸出(‘change’), 說明伺服器檢測到了 print.js 的程式碼變化並且執行了 module.hot.accept 的回撥函式,

但是現在只成功了50%, 因為點選 button 按鈕, 會發現輸出還是之前的(‘I get called from print.js!’), 說明 print.js 雖然被修改了, 但在 index.js 上還沒有被修改之後的替換, 所以 button 繫結的還是之前的事件, 這裡需要在檢測到程式碼修改後, 用修改之後的js重新渲染頁面:
index.js

import _ from 'lodash';
import printMe from './print.js';

function component() {
    var element = document.createElement('div');
    var btn = document.createElement('button'); //新建一個button物件

    element.innerHTML = _.join(['Hello', 'webpack'], ' '); //要用到lodash的語法

    btn.innerHTML = 'Click me and check the console!';
    btn.onclick = printMe; //button觸發的事件是引用的print.js暴露的事件

    element.appendChild(btn); //把button物件插入div中

    return element;
}

var element = component(); //改用一個element儲存一下
document.body.appendChild(element);

if (module.hot) { //告訴 webpack 接受熱替換的模組
    module.hot.accept('./print.js', function() {
        console.log('Accepting the updated printMe module!');
        document.body.removeChild(element); //刪掉舊的element
        element = component(); //獲得一個修改後的element
        document.body.appendChild(element); //重新插入到網頁中
    })
}

熱過載

因為之前配置過熱過載功能 , 所以修改了 index.js 之後會自動過載, 頁面重新整理說明過載完成, 這個時候再次修改 print.js 的輸出, 熱替換後再點選按鈕, 發現輸出的是修改後的程式碼:
在這裡插入圖片描述
實現了 HMR(模組熱替換功能), 這樣修改模組的程式碼就不用熱過載重新載入整個檔案了, 它只會更新修改的模組部分, 對於開發來說很方便

HMR 修改樣式表(CSS)

藉助於 style-loader 的幫助, CSS 的模組熱替換實際上是相當簡單的, 當更新 CSS 依賴模組時, 此 loader 在後臺使用 module.hot.accept 來修補(patch) < style> 標籤

安裝

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

配置 webpack.config.js

相關 style-loader 配置教程可以看 https://blog.csdn.net/qq593249106/article/details/84894989
webpack.config.js

const path = require('path');
const HtmlWebpackPlugin = require('html-webpack-plugin');
const CleanWebpackPlugin = require('clean-webpack-plugin');
const webpack = require('webpack'); //引入 webpack


module.exports = {
    entry: {
        app: './src/index.js'
        // print: './src/print.js'
    },
    devtool: 'inline-source-map', // 不同選項適用於不同環境
    devServer: {
        contentBase: './dist', //將dist目錄下的檔案(index.html)作為可訪問檔案, 如果不寫這個引數則預設與webpack.cofig.js的同級目錄
        port: 8080, //埠號設為8080, 預設也是8080,
        hot: true
    },
    module: {
        rules: [ //配置載入器, 用來處理原始檔, 可以把es6, jsx等轉換成js, sass, less等轉換成css
            {
                test: /\.css$/, //配置要處理的檔案格式,一般使用正則表示式匹配
                use: ['style-loader', 'css-loader'] //使用的載入器名稱
            }
        ]
    },
    plugins: [ //webpack 通過 plugins 實現各種功能, 比如 html-webpack-plugin 使用模版生成 html 檔案
        new CleanWebpackPlugin(['dist']), //設定清除的目錄
        new HtmlWebpackPlugin({
            filename: 'index.html', //設定生成的HTML檔案的名稱, 支援指定子目錄,如:assets/admin.html
            title: 'Form HtmlWebpackPlugin', //設定生成的HTML的title
        }),
        new webpack.HotModuleReplacementPlugin() //啟用 webpack 內建的 HMR外掛
    ],
    output: {
        filename: '[name].bundle.js', //根據入口檔案輸出不同出口檔案
        path: path.resolve(__dirname, 'dist')
    }
};

現在新建一個 style.css 在 src 資料夾下用來做本次測試:

|- /dist //用於放打包後文件的資料夾
  |- app.bundle.js //出口檔案
  |- print.bundle.js //出口檔案
  |- index.html //模板檔案
|- /node_modules
|- /src //用於放原始檔的資料夾
  |- index.js //入口檔案
  |- print.js 
  |- style.css //樣式檔案
|- package.json
|- webpack.config.js //webpack配置檔案

style.css

body {
    background-color: red;
}

別忘了在 index.js 引入 style.css:
index.js

import _ from 'lodash';
import printMe from './print.js';
import './style.css'

function component() {
    var element = document.createElement('div');
    var btn = document.createElement('button'); //新建一個button物件

    element.innerHTML = _.join(['Hello', 'webpack'], ' '); //要用到lodash的語法

    btn.innerHTML = 'Click me and check the console!';
    btn.onclick = printMe; //button觸發的事件是引用的print.js暴露的事件

    element.appendChild(btn); //把button物件插入div中

    return element;
}

var element = component(); //改用一個element儲存一下
document.body.appendChild(element);

if (module.hot) { //告訴 webpack 接受熱替換的模組
    module.hot.accept('./print.js', function() {
        console.log('Accepting the updated printMe module!');
        document.body.removeChild(element); //刪掉舊的element
        element = component(); //獲得一個修改後的element
        document.body.appendChild(element); //重新插入到網頁中
    })
}

啟動伺服器

終端輸入 npm run dev, 開啟網頁:
在這裡插入圖片描述
style.css 檔案生效了, 現在修改一下在 style.css 中修改一下, 將顏色改為綠色:
style.css

body {
    /*background-color: red;*/
    background-color: green;
}

這時, 網頁沒有過載直接變成了綠色, 實現了 css 的 HMR(熱替換)
在這裡插入圖片描述