#yyds干货盘点#理解webpack自动刷新浏览器

Posted

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了#yyds干货盘点#理解webpack自动刷新浏览器相关的知识,希望对你有一定的参考价值。

在我们日常的前端开发过程中,在编辑器里只需要保存代码,浏览器就会自动刷新当前页面。这个过程被称为热更新。

其实实现这一功能需要两步:

  • 监听代码的变化
  • 自动刷新浏览器

下面看一下这两个步骤是如何实现的。

配置webpack热更新模式

  • 初识化项目并导入依赖
npm i webpack webpack-cli -D
npm i webpack-dev-server -D
npm i html-webpack-plugin -D

然后,我们需要弄明白,webpack 从版本 webpack@4 之后,需要通过 webpack CLI 来启动服务,提供了启动开发服务的命令。

# 启动开发服务器
webpack serve --mode development --config webpack.config.js
// pkg.json

"scripts":
"dev": "webpack serve --mode development --config webpack.config.js",
"build": "webpack build --mode production --config webpack.config.js"
,
"devDependencies":
"webpack": "^5.45.1",
"webpack-cli": "^4.7.2",
"webpack-dev-server": "^3.11.2",
"html-webpack-plugin": "^5.3.2",

在启动开发服务的时候,在 webpack 的配置文件中配置 ​devServe​ 属性,即可开启热更新模式。

// webpack.config.js
const path = require(path)
const HtmlWebpackPlugin = require(html-webpack-plugin)

module.exports =
entry: ./src/index.js,
output:
filename: main.js,
path: path.resolve(__dirname, dist),
,
devServer:
hot: true, // 开启热更新
port: 8080, // 指定服务器端口号
,
plugins: [
new HtmlWebpackPlugin(
template: ./index.html
)
]

源码解析


开启本地服务

首先通过webpack创建了一个compiler实例,然后通过创建自定义server实例,开启了一个本地服务。

// node_modules/webpack-dev-server/bin/webpack-dev-server.js

const webpack = require(webpack);

const config = require(../../webpack.config);

const Server = require(../lib/Server)



const compiler = webpack(config);

const server = new Server(compiler);

server.listen(8080, localhost, () => )

这个自定义Server 不仅是创建了一个http服务,它还基于http服务创建了一个websocket服务,同时监听浏览器的接入,当浏览器成功接入时向它发送hash值,从而实现服务端和浏览器间的双向通信。

// node_modules/webpack-dev-server/lib/Server.js

class Server

constructor()

this.setupApp();

this.createServer();



//创建http应用

setupApp()

this.app = express();



//创建http服务

createServer()

this.server = http.createServer(this.app);



//监听端口号

listen(port, host, callback)

this.server.listen(port, host, callback)

this.createSocketServer();



//基于http服务创建websocket服务,并注册监听事件connection

createSocketServer()

const io = socketIO(this.server);

io.on(connection, (socket) =>

this.clientSocketList.push(socket);

socket.emit(hash, this.currentHash);

socket.emit(ok);

socket.on(disconnect, () =>

let index = this.clientSocketList.indexOf(socket);

this.clientSocketList.splice(index, 1)

)

)







module.exports = Server;

监听编译完成

仅仅在建立websocket连接时,服务端向浏览器发送hash和拉取代码的通知还不够,我们还希望当代码改变时,浏览器也可以接到这样的通知。于是,在开启服务前,还需要对编译完成事件进行监听。

//监听编译完成,当编译完成后通过websocket向浏览器发送广播

setupHooks()

let compiler = this;

compiler.hooks.done.tap(webpack-dev-server, (stats) =>

this.currentHash = stats.hash;

this.clientSocketList.forEach((socket) =>

socket.emit(hash, this.currentHash);

socket.emit(ok);

)

)

监听文件修改

要想在代码修改的时候,触发重新编译,那么就需要对代码的变动进行监听。这一步,源码是通过​​webpackDevMiddleware​​​库实现的。库中使用了compiler.watch对文件的修改进行了监听,并且通过​​memory-fs​​实现了将编译的产物存放到内存中,这也是为什么我们在dist目录下看不到变化的内容,放到内存的好处就是为了更快的读写从而提高开发效率。

// node_modules/webpack-dev-middleware/index.js

const MemoryFs = require(memory-fs)

compiler.watch(, () => )

let fs = new MemoryFs();

this.fs = compiler.outputFileSystem = fs;

向浏览器中插入客户端代码

前面提到要想实现浏览器和本地服务的通信,那么就需要浏览器接入到本地开启的websocket服务,然而浏览器本身并不具备这样的能力,这就需要我们自己提供这样的客户端代码将它运行在浏览器。因此自定Server在开启http服务之前,就调用了​​updateCompiler()​​方法,它修改了webpack配置中的entry,使得插入的两个文件的代码可以一同被打包到 main.js 中,运行在浏览器。

//node_modules/webpack-dev-server/lib/utils/updateCompiler.js

const path = require(path);

function updateCompiler(compiler)

compiler.options.entry =

main: [

path.resolve(__dirname, ../../client/index.js),

path.resolve(__dirname, ../../../webpack/hot/dev-server.js),

config.entry,

]





module.exports = updateCompiler

node_modules /webpack-dev-server/client/index.js

这段代码会放在浏览器作为客户端代码,它用来建立 websocket 连接,当服务端发送hash广播时就保存hash,当服务端发送ok广播时就调用reloadApp()。

let currentHash;

let hotEmitter = new EventEmitter();

const socket = window.io(/);



socket.on(hash, (hash) =>

currentHash = hash;

)

socket.on(ok, () =>

reloadApp();

)



function reloadApp()

hotEmitter.emit(webpackHotUpdate, currentHash)

webpack/hot/dev-server.js

reloadApp()继续调用module.hot.check(),当然第一次加载页面时是不会被调用的。至于这里为啥会分成两个文件,个人理解是为了解藕,每个模块负责不同的分工。

let lastHash;

hotEmitter.on(webpackHotUpdate, (currentHash) =>

if (!lastHash)

lastHash = currentHash;

return;



module.hot.check();

)
复制代码

module.hot.check()是哪来的?答案是​​HotModuleReplacementPlugin​​。

以上是关于#yyds干货盘点#理解webpack自动刷新浏览器的主要内容,如果未能解决你的问题,请参考以下文章

#yyds干货盘点# 浏览器跨域请求 原理和个人理解

#yyds干货盘点#聊一聊webpack

#yyds干货盘点#会话管理

#yyds干货盘点#愚公系列2022年12月 微信小程序-下拉刷新功能实现

#yyds干货盘点# Selenium Web端自动化从入门到实践

#yyds干货盘点#vue.config.js 的完整配置