webpack HMR原理探究

流程图镇楼~

image

这里将图中的EventSource换成websocket。
HMR(# hot module replacement)简单的来说就是监听文件变换替换相应的模块而不用刷新整个页面,区别于live reload(监听文件刷新整个页面),是提高开发体验和效率的一种方式。
大致原理:
2部分组成,一端是sever端,一端是client端。

  1. server端通过websocket将文件变化告知client端,并告知对应模块的hash值。
  2. client端收到消息后,对比模块的hash做出是否需要变更模块的决定,并再次请求完整的模块变化部分并替换client相应的模块(webpack-dev-middleware所做的事)。
  3. webpack暴露HMR接口,中间做多大规模的更新通过各个loader自己决定,否则冒泡替换整个模块。
    下面自己基于wepack写个简单的HMR的例子
    4个文件
  • webpack-base-conf.js
  • index.html
  • main.js
  • print.js(一个模块)

目录结构:


image
const path = require('path');
const HtmlWebpackPlugin = require('html-webpack-plugin'); //生成html文件
const CleanWebpackPlugin = require('clean-webpack-plugin'); //每次build的时候清空之前的目录
const webpack = require('webpack')

//path.resolve(...[args])
//从右往左添加路径,最低路径其实拼接取决右边的参数,即使左边写了,也会被忽略,..会忽略左边第一个/后面的路径值,../../会忽略2个,以此类推
//把所有路径定位到项目工程根目录下
function resolve(dir) {
    return path.resolve(__dirname, '..', dir);
}
module.exports = {
    devtool: 'source-map',
    mode: 'none',
    entry: {
        main: resolve('./src/main.js')
    },
    output: {
        path: resolve('dist'),
        filename: '[hash].[name].js'
    },
    devServer: {
        contentBase: resolve('dist'), //本地服务器所加载的页面所在的目录
        historyApiFallback: true, //不跳转
        // inline: true, //实时刷新
        hot: true
    },
    plugins: [
        new CleanWebpackPlugin(),
        new webpack.NamedModulesPlugin(),
        new webpack.HotModuleReplacementPlugin(),
        new HtmlWebpackPlugin({
            title: 'Development',
            template: resolve('src/index.html')
        })
    ]
}
//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>Document</title>
</head>
<body>
    <div id="root">根元素</div>
</body>
</html>
//main.js
import printNode from './print.js'
//这里能捕获监听到文件变化
if(module.hot) {
    module.hot.accept('./print.js', ()=> {
        document.querySelector("#root").appendChild(printNode);
    })
}
//print.js
function printMe() {
    console.log('updatessssssss')
}

var printNode = document.createElement('div');
printNode.innerHTML = '12';
// document.querySelector("#root").appendChild(printNode);

// module.exports = printMe;
module.exports = printNode;
//package.json

{
  "name": "test",
  "version": "1.0.0",
  "main": "index.js",
  "scripts": {
    "test": "echo \"Error: no test specified\" && exit 1",
    "webpack": "webpack --config ./build/webpack.base.conf.js",
    "server": "webpack-dev-server --config ./build/webpack.base.conf.js"
  },
  "license": "MIT",
  "dependencies": {
    "clean-webpack-plugin": "^2.0.1",
    "html-webpack-plugin": "^3.2.0",
    "webpack": "^4.29.6",
    "webpack-cli": "^3.3.0",
    "webpack-dev-server": "^3.3.1"
  }
}

运行

npm run server

改变printNode.innerHTML = '12'里的内容,可以看到页面会发生HMR!.

PS:
这里对比下Webpack 的HMR和 Vue这些MVVM框架的响应式变化的区别,前者是通过监听文件的变化和hash进行模块替换,后者是通过数据劫持来改变页面内容,原理不同,效果也有差异。关于Vue的响应式原理可以看我的另一篇文章

【完】

参考文献

https://www.jianshu.com/p/6fd41da59782

©著作权归作者所有,转载或内容合作请联系作者
【社区内容提示】社区部分内容疑似由AI辅助生成,浏览时请结合常识与多方信息审慎甄别。
平台声明:文章内容(如有图片或视频亦包括在内)由作者上传并发布,文章内容仅代表作者本人观点,简书系信息发布平台,仅提供信息存储服务。

相关阅读更多精彩内容

友情链接更多精彩内容