什么是HMR?
HMR(Hot Module Replacement)是一种开发工具,也就是热更新。用于在应用程序运行时替换、添加或删除模块,而无需完全重新加载整个页面或重新启动应用程序。这可以极大地提高开发效率和调试体验。
HMR的优势
通过使用HMR,当你修改了一个模块的代码后,在保存文件的瞬间,只有该模块及其依赖关系会被重新编译,并且新的代码将立即生效。这使得你能够实时查看对应用程序所做更改的影响结果,无需手动刷新页面。
Vite的HMR功能是通过以下原理实现的:
-
ES模块和浏览器原生支持: Vite利用了现代浏览器对ES模块加载机制的支持。在开发环境中,Vite将每个文件作为一个独立的模块进行处理,并且使用浏览器自身提供的
import
和export
语法来实现模块之间的依赖关系。 -
WebSocket通信: 当源代码发生更改时,Vite会检测到变化并建立与客户端之间的WebSocket连接。这样,服务器可以向客户端发送更新消息。
-
按需编译和替换: 一旦服务器检测到代码更改,它会根据变化重新编译受影响的文件,并生成新版本。然后,服务器通过WebSocket将更新消息发送给客户端。
-
局部更新: 客户端收到更新消息后,它会根据需要下载新版本并对页面进行局部刷新,而无需完全重新加载整个页面。这种局部刷新使得应用程序保持在开发状态下无缝运行,并提供快速反馈。
具体而言,在Vite中实现HMR特性主要涉及以下三个步骤:
-
监听文件改动: 在开发模式下,Vite使用一个文件系统监视器(watcher)来监听源代码文件的变化。当文件被修改、添加或删除时,watcher会立即捕获到这些变化。
-
服务器端编译资源并推送新模块内容给浏览器: 一旦有文件更改被捕获,Vite启动一个服务器,并执行相应的编译过程。它会根据需要重新构建受影响的模块,并生成新版本。然后,服务器通过WebSocket与客户端保持连接,并将更新消息推送给浏览器。
-
浏览器接收并处理新模块内容: 浏览器收到更新消息后,在不刷新整个页面的情况下下载和加载新版本的模块。对于支持HMR功能的前端框架(如Vue.js和React),它们具备能力在接收到新模块内容后进行局部渲染或组件重载操作(例如Vue中即是rerender)。这使得应用程序可以实现热更新效果,即时地反映出源代码更改所带来的变化。
serverPluginClient 源码举例
serverPluginClient是Vite中的一个插件,用于在开发服务器上提供客户端代码。它负责将构建后的客户端代码发送给浏览器,并处理与客户端相关的请求。
服务端由 serverPluginClient 插件进行处理
代码举例:
export const clientPlugin: ServerPlugin = ({ app, config }) => {
// 读取客户端代码文件内容,并替换其中的占位符
const clientCode = fs
.readFileSync(clientFilePath, 'utf-8')
.replace(`__MODE__`, JSON.stringify(config.mode || 'development'))
.replace(
`__DEFINES__`,
JSON.stringify({
...defaultDefines,
...config.define
})
)
// 使用中间件处理请求
app.use(async (ctx, next) => {
if (ctx.path === clientPublicPath) {
ctx.type = 'js'
ctx.status = 200
// 返回具体内容,将端口号动态替换到客户端代码中的占位符处
ctx.body = clientCode.replace(`__PORT__`, ctx.port.toString())
} else {
// 兼容历史逻辑,并进行错误提示
if (ctx.path === legacyPublicPath) {
console.error(
chalk.red(
`[vite] client import path has changed from "/vite/hmr" to "/vite/client". ` +
`please update your code accordingly.`
)
)
}
return next()
}
})
}