使用服务端渲染的React多页面应用实现客户端与服务端热更新的最全解决方案

多页面应用(MPA, multi-page application)具有更快的首屏加载速度和更好的SEO(search engine optimization),已经超过单页面应用(SPA,Single-page application),成为更受欢迎的前端技术方案。为了更快的页面呈现速度,通常还会采用服务端渲染技术(SSR, Server Side Rendering)。为了提高开发效率,大家一般会在开发时使用热更新技术。本文接下来要探讨一下,对于使用服务端渲染的多页面应用,有哪些实现客户端与服务端热更新的解决方案。

首先区分一下几个概念。热重启(hot restart)、热编译(hot recompile)、热加载(hot reload)、热替换(hot replace)、热刷新(hot refresh)、热更新(hot update)。热重启一般是指服务端代码发生变化时自动重启服务。热编译一般是指相关代码发生变化时自动重新编译代码。热加载一般指相关代码发生变化时自动加载新的代码(通过网络请求或者文件系统读取新的代码)。热替换一般是指在不重新加载页面或者重启服务的情况下自动替换热加载的代码(由于都是模块化的代码,所以一般是指自动替换热加载的模块)。热刷新一般是指客户端代码热加载后自动刷新页面。本文的热更新主要是对上面技术的一种统称。

接下来分服务端代码打包、服务端代码不打包且使用express和服务端代码不打包且使用koa三种场景进行分析。

服务端代码打包时的热更新方案

启用服务端渲染时,由于node不能直接运行客户端代码,最简单的解决方法就是对服务端代码进行打包。

客户端热更新

虽然webpack支持模块热替换(HMR,hot module replacement)功能,但不是默认包含的。使用webpack.HotModuleReplacementPlugin插件后,才能完全开启HMR功能。HotModuleReplacementPlugin通过注册一些编译时的钩子(Hook)来注入HMR功能,比如重新编译时采用增量编译而不是全量编译。另外,还会把HMR的runtime注入到webpackBootstrap中,runtime定义了相关的api,比如module.hot.accept。根据官方教程,我们需要在代码中加入module.hot.accept函数来替换已经热加载的新模块,否则会收到告警“[HMR] The following modules couldn’t be hot updated: (Full reload needed)。This is usually because the modules which have changed (and their parents) do not know how to hot reload themselves.”。父模块引入子模块时,引用的是子模块的module.exports对象,热加载后,虽然webpack中的模块(installedModules)都已经更新了,但是,内存中父模块依赖的子模块的module.exports对象并没有被替换,所以要在accept函数中来处理一下。这里有个细节要注意一下,官方文档说,对于api

module.hot.accept(
  dependencies, // Either a string or an array of strings
  callback // Function to fire when the dependencies are updated
);

使用CommonJS时,需要在callback中手动require新模块来更新依赖,而使用ESM import时,引入的模块会被自动更新。个人测试的结果是,import的模块并没有被自动更新。但是又不能在callback中用import新模块的方式来更新依赖。怎么办呢?可以使用api:

module.hot.accept(
  errorHandler // Function to handle errors when evaluating the new version
);

该模块会更新自己,那依赖的子模块自然也被更新了。虽然这个api会阻止更新事件冒泡,但是一般会把这个函数放在最上层的模块中,所以就没影响了。

热更新会重置页面的状态,如果想保留页面的状态,可以使用react-hot-loader

上面说的都是js代码的热替换,那css代码的热替换呢?可以用css-hot-loader来实现。

客户端热更新主要依靠webpack-dev-server来实现。webpack-dev-server主要包括热编译和热加载两个功能。webpack-dev-server集成了webpack-dev-middleware中间件。webpack-dev-middleware把编译的输出文件存在memory-fs提供的内存文件系统中,以加快存取速度。webpack-dev-middleware调用compiler.watch函数来启动webpack的watch模式;当源文件发生变化时,webpack就会重新编译;当使用了HotModuleReplacementPlugin时,这里的重新编译其实是增量编译,不会像第一次编译那样编译整个工程代码,而是只编译发生变化的源文件并只输出发生变化的chunks和json文件,编译生成的json文件和chunks都是用上一个hash值命名的,比如‘62547a0be0169d5f328f.hot-update.json’和‘0.62547a0be0169d5f328f.hot-update.js’。一个json文件的示例如下,前面是新的hash值,后面是新的chunks信息。

{"h":"57080b114a576212e8f8","c":{"0":true}}

webpack-dev-server启动了一个express服务,把请求映射到相关文件。webpack-dev-server的热加载采用WebSocket通信协议实现,分为服务端和客户端两部分。服务端用sockjs-node启动一个Socket Server,同时注册一个compiler.hooks.done钩子函数,当编译完成时,向客户端发送一个代表编译完成的信息,同

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值