【webpack】五、Hot Module Replacement热模块更新

Hot Module Replacement 热模块更新


热模块替换,通常简写为hmr,能够在不刷新页面、不影响其他模块功能的情况下,热更新你本次修改的内容。

本文将通过两个个简单的案例介绍一下hmr

1. Hot Module Replacement的css案例

先根据以下提供的几个配置文件搭建一下项目

package.json

{
  "name": "practice1",
  "version": "1.0.0",
  "description": "cousinChen的demo",
  "dependencies": {},
  "devDependencies": {
    "autoprefixer": "^9.3.1",
    "clean-webpack-plugin": "^1.0.0",
    "css-loader": "^3.6.0",
    "file-loader": "^6.0.0",
    "html-webpack-plugin": "^4.3.0",
    "node-sass": "^4.10.0",
    "postcss-loader": "^3.0.0",
    "sass-loader": "^7.1.0",
    "style-loader": "^1.2.1",
    "url-loader": "^4.1.0",
    "webpack": "^4.25.1",
    "webpack-cli": "^3.1.2",
    "webpack-dev-server": "^3.11.1"
  },
  "scripts": {
    "start": "webpack-dev-server"
  },
  "keywords": [],
  "author": "",
  "license": "ISC"
}

webpack.config.js

//引入node的核心模块 path
const path = require('path');
const HtmlWebpackPlugin = require('html-webpack-plugin');
const CleanWebpackPlugin = require('clean-webpack-plugin');

/**
 * commonJs语法
 */
module.exports = {
  //指定项目的模式 production:生产环境 development:开发环境
  mode: 'development',
  devtool: 'source-map',
  // devtool: 'none',
  //指定入口文件,从哪个文件开始打包
  //entry对象可以简写为如下形式
  //entry: './src/index.js',
  entry: {
    main: './src/index.js',
  },
  //使用webpackDevServer为我们实现服务端的功能
  devServer: {
    //指定服务端的路径
    contentBase: './dist',
    //open为true,项目启动时会自动打开浏览器访问项目
    open: true,
    port: 8080,
    proxy: {
      '/api': 'http://localhost:8081'
    }
  },
  module: {
    rules: [{
      //正则匹配文件后缀
      test: /\.(jpg|png|gif)$/,
      use: {
        loader: 'url-loader',
        options: {
          //通过使用Placeholders(占位符)的方式,指定打包后的文件名使用原文件名,更多具体用法参考官网file-loader的placeholder用法
          name: '[name].[ext]',
          //指定打包输出路径
          outputPath: 'images/',
          //当文件小于20KB时,直接将文件以Base64编码的形式打包到js文件中,而不是生成到outputPath指定的文件夹中。
          limit: 1024 * 20
        }
      }
    }, {
      //正则匹配scss文件后缀
      test: /\.scss$/,
      //引入处理css、sass、自动补全浏览器兼容属性的loader
      use: [
        'style-loader',
        {
          loader: 'css-loader',
          options: {
            importLoaders: 2,
            // modules: true
          }
        },
        'sass-loader',
        'postcss-loader'
      ]
    }, {
      //正则匹配css文件后缀
      test: /\.css$/,
      //引入处理css、sass、自动补全浏览器兼容属性的loader
      use: [
        'style-loader',
        'css-loader',
        'postcss-loader'
      ]
    },{
      //正则匹配文件后缀
      test: /\.(eot|ttf|svg|woff)$/,
      use: {
        loader: 'file-loader'
      }
    }]
  },
  plugins: [
    new HtmlWebpackPlugin({
      template: 'src/index.html'
    }),
    new CleanWebpackPlugin(['dist'])
  ],
  //打包后的文件和其输出目录
  output: {
    filename: '[name].js',
    //调用path模块的resolve方法
    //node中_dirname这个变量,实际指的就是webpack.config.js这个文件所在目录的路径。
    //传入的'bundle',最终拼接出来的路径就是打包后输出的目录
    //如果不配置path, webpack会默认在我们根目录下创建一个名为dist的目录作为打包输出目录
    path: path.resolve(__dirname, 'dist')
  }
}

在src目录下的index.html中,只写一个id为root的div。

<!DOCTYPE html>
<html lang="en">
<head>
  <meta charset="UTF-8">
  <title>webpack学习</title>
</head>
<body>
  <div id="root"></div>
</body>
</html>

在src目录下,创建style.css文件,编写如下样式

div:nth-of-type(odd) {
  background: red;
}

打开src目录下的index.js,编写如下代码,实现功能:点击新增按钮,在网页上生成内容为item的div节点

import './style.css';

var btn = document.createElement('button');
btn.innerHTML = '新增';
document.body.appendChild(btn);
btn.onclick = function(){
  var div = document.createElement('div');
  div.innerHTML = 'item';
  document.body.appendChild(div);
}

点击新增按钮,在页面上生成了颜色为红色的div

当我们将style.css文件中背景色改为蓝色,再查看浏览器,发现webpackDevServer自动编译后,页面重新加载,导致之前渲染的dom节点、样式都失效了。

div:nth-of-type(odd) {
  background: blue;
}

image-20210112002507641

那么,能不能在我们修改样式后,页面不但不自动加载,还能应用修改后的样式呢?当然可以,使用hmr就行!

2. Hot Module Replacement的配置

如下修改webpack.confg.js文件

  • 在devServer中添加hot和hotOnly两项配置
  • 在webpack.confg.js文件头部引入webpack模块
  • 在plugins中引入webpack热模块更新插件,webpack.HotModuleReplacementPlugin()
//引入node的核心模块 path
const webpack = require('webpack');

module.exports = {
  mode: '',
  devtool: '',,
  entry: {},
  devServer: {
    contentBase: './dist',
    open: true,
    port: 8080,
    proxy: {
      '/api': 'http://localhost:8081'
    },
    //让webpackDevServer开启hot module replacement功能
    hot: true,
    //当hot module replacement功能没生效时,也不允许浏览器重新加载
    hotOnly: true
  },
  module: {},
  plugins: [
      new HtmlWebpackPlugin({
      template: 'src/index.html'
    }),
    new CleanWebpackPlugin(['dist']),
    //引入webpack热模块更新插件
    new webpack.HotModuleReplacementPlugin()
  ],
  output: {}
}

在控制台输入npm run start重启项目,在浏览器页面点击新增按钮,生成蓝色的div

image-20210112005128548

然后去style.css中修改颜色为红色,自动编译完后发现页面并没有重新加载,且蓝色的div变成了红色,说明hmr生效了。

div:nth-of-type(odd) {
  background: blue;
}

image-20210112005101231

3. Hot Module Replacement的js案例

将上面css案例中的热模块功能关闭,我们演示一下开关热模块功能对js模块代码的影响。

在src目录下,创建counter.js文件,作为一个模块供外部调用。实现功能:在页面上生成一个数字,每次点击这个数字,数字累计加一。

function counter(){
  var div = document.createElement('div');
  div.setAttribute('id', 'counter');
  div.innerHTML = 1;
  div.onclick = function(){
    div.innerHtml = parseInt(div.innerHTML, 10) + 1;
  }
  document.body.appendChild(div);
}

export default counter;

再创建一个number.js文件,同样作为一个模块供外部调用。实现功能:在页面显示数字1000。

function number(){
  var div = document.createElement('div');
  div.setAttribute('id', 'number');
  div.innerHTML = 1000;
  document.body.appendChild(div);
}

export default number;

在index.js中引入counter、number模块,并调用counter()、number()方法。

import counter from './counter';
import number from './number';

counter();
number();

此时没有开启Hot Module Replacement之前,即没有配置devServer下的hot,也没有引入引入webpack热模块更新插件。

点击第一个数字,让它累加(counter模块的功能)

image-20210114000123004

然后修改number.js文件中的内容,将div.innerHTML值改成2000。(即这边修改的是number模块的代码)

image-20210114000241389

此时页面因为重新加载,导致counter模块累加的数字被重置为1了

image-20210114000459563

而在开启Hot Module Replacement之后,点击第一个数字进行累加为9,修改number.js文件中的数值为3000,却发现热更新成功后,页面确实没有重新加载,第一个数字也没被重置为1,但是,2000也没有变为3000呀!这似乎和预想的不太一样。别急,这是因为js的热模块更新和css的有些区别。

image-20210114003054355

在index.js中加入以下代码,因为js模块热更新后,需要我们自己决定具体哪些模块需要热更新。

//判断模块是否热更新
if(module.hot){
  //当检测到number模块热更新后,执行以下逻辑
  module.hot.accept('./number', ()=>{
    //移除页面上的number节点
    document.body.removeChild(document.getElementById('number'));
    //重新调用number()方法
    number();
  });
}

而上面css文件的修改能及时生效,是因为css的热更新逻辑已经被css-loader实现了,现在知道为什么了吧~

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值