传统webpack的hmr是使用webpack的HotModuleReplacementPlugin,而vite则是采用native ES Module的devServer。
- 初始化本地服务器
- 加载并运行对应的plugin
最重要的一件事就是运行plugin,目前vite支持的plugin大体如下图所示
1、建立ViteDevServer服务器(包含5大模块:httpServer fsWatcher pluginContainer webSocketServer moduleGraph),配置websocket便于实时更新、chokidar.watch来监听文件的变化、plugin处理特殊功能、moduleGraph记录文件与url的映射。
2、watcher.on()监听文件变化,获取文件地址,并使该文件所在的moduleGraph缓存失效
3、调用hmr.ts中的handleHMRUpdate,获取文件对应模块,检查是否有自定义HMR,如果有,执行自定义更新。
4、如果需要更新的模块为空,判断如果html文件修改,重载页面;否则返回not modules matched
5、更新模块:循环将需要更新的module文件信息放入updates数组,用于log输出。查找引用模块propagateUpdate,若找不到引用它的模块,设置needFullReload为true,需要重载页面。调用ws.send,发送更新信息,若重载页面,type设置为full-reload,否则设置为update。
6、到此,服务端的监听就完成了,代码片段如下,仅供参考:
// server.ts
export async function createServer(inlineConfig: InlineConfig = {}
): Promise<ViteDevServer>// 将服务升级为websocket,便于实时更新const ws = createWebSocketServer(httpServer, config, httpsOptions)// 使用chokidar监听变化,热更新的基础const watcher = chokidar.watch(path.resolve(root), {ignored: ['**/node_modules/**','**/.git/**',...(Array.isArray(ignored) ? ignored : [ignored])],ignoreInitial: true,ignorePermissionErrors: true,disableGlobbing: true,...watchOptions}) as FSWatcher// 处理所有的plugin,并保存到container中const container = await createPluginContainer(config, watcher)// 生成moduleGraph,这里记录了import的关系,url到file的映射const moduleGraph = new ModuleGraph(container)const server: ViteDevServer// watcher监听文件变化并进行对应的热更新处理watcher.on('change', async (file) => {file = normalizePath(file)// invalidate module graph cache on file change 文件改变时使原有模块图形失效moduleGraph.onFileChange(file)if (serverConfig.hmr !== false) {try {await handleHMRUpdate(file, server)} catch (err) {ws.send({type: 'error',err: prepareError(err)})}}})// hmr.ts
export async function handleHMRUpdate// 获取文件对应的模块const mods = moduleGraph.getModulesByFile(file)// 更新模块updateModules(shortFile, hmrContext.modules, timestamp, server)if (needFullReload) {config.logger.info(chalk.green(`page reload `) + chalk.dim(file), {clear: true,timestamp: true})ws.send({type: 'full-reload'})} else {config.logger.info(updates.map(({ path }) => chalk.green(`hmr update `) + chalk.dim(path)).join('\n'),{ clear: true, timestamp: true })ws.send({type: 'update',updates})}
7、在client.ts中监听websocket发送的文件更新消息,handleMessage()处理更新,这里我们常用的就是update和full-reload两种更新方式,代码片段如下:
case 'update': // 仅重新加载部分文件notifyListeners('vite:beforeUpdate', payload)// if this is the first update and there's already an error overlay, it// means the page opened with existing server compile error and the whole// module script failed to load (since one of the nested imports is 500).// in this case a normal update won't work and a full reload is needed.if (isFirstUpdate && hasErrorOverlay()) {window.location.reload() // 第一次加载或者页面出现错误时直接刷新页面return} else {clearErrorOverlay()isFirstUpdate = false}// 更新模块payload.updates.forEach((update) => {if (update.type === 'js-update') {// 更新js文件queueUpdate(fetchUpdate(update))} else {// css-update// this is only sent when a css file referenced with <link> is updatedlet { path, timestamp } = updatepath = path.replace(/\?.*/, '')// can't use querySelector with `[href*=]` here since the link may be// using relative paths so we need to use link.href to grab the full// URL for the include check.const el = ([].slice.call(document.querySelectorAll(`link`)) as HTMLLinkElement[]).find((e) => e.href.includes(path))if (el) {const newPath = `${base}${path.slice(1)}${path.includes('?') ? '&' : '?'}t=${timestamp}`el.href = new URL(newPath, el.href).href}console.log(`[vite] css hot updated: ${path}`)}})break
case 'full-reload': // reload整个页面notifyListeners('vite:beforeFullReload', payload)if (payload.path && payload.path.endsWith('.html')) {// if html file is edited, only reload the page if the browser is// currently on that page.const pagePath = location.pathnameconst payloadPath = base + payload.path.slice(1)if (pagePath === payloadPath ||(pagePath.endsWith('/') && pagePath + 'index.html' === payloadPath)) {location.reload()}return} else {location.reload()}break
以上就是我对vite热更新的简单分析,如有问题或建议,请多指教
参考文章
https://vitejs.cn/
https://blog.csdn.net/qianyu6200430/article/details/119122059