将webpack的热替换(HMR)整合到既有server(Express)
注意:本篇不是一个讲解HMR原理的文章,仅仅阐述如何实现接入HMR。
背景
随着部门业务的发展,业务逻辑的不断发展,为了更好的应对业务的多变性,提高项目可维护性,我们提出了基于React的组件化工作流。
在项目的前期,我们将原先的jquery组件逐步的迁移至React组件,一个个的业务模块被拆分为可维护的组件及可复用组件,对于React,由于独有的JSX
语法,页面的html
结构和js逻辑
混搭在一起,通常是下面这样:
1 | const Avatar = (props)=>{ |
但是为了组件的统一维护性,需要将组件的样式也单独拆分到组件粒度并将组件的.jsx
和.scss
文件放在一个目录里面,以上面讲到的Avatar
组件为例:
1 | //...avatar/index.jsx |
组件的目录结构为:
1 | --component |
这个概念其实很早就有大神提出来了==>张云龙-前端工程-基础篇
当然,使用几乎无所不能的webpack可以达到我们的诉求。
痛处
在迁移中我发现,由于前端开发中通过webpack来加载.scss
文件资源,每次修改组件样式文件时,webpack的watch
机制就会触发资源的重新打包,进而将整个page页面刷新(通过使用livereload来监听js文件改动)。而在这之前,也就是样式没有拆分到组件粒度之前,样式文件按照page为单位维护,通过compass
+livereload.js
来完成实时编译.scss
文件并热替换样式文件的“一条龙”服务。
现在写组件样式就很不爽了,就改了一个背景色,整个页面需要重新reload。所以有没有解决方案,让样式文件能够在不刷新浏览器的情况下被应用?
答案是有的,可以通过webpack的HMR(Hot-Module-Replacement)可以实现模块的热替换,这其中就包含css模块。
了解HMR
首先什么是HMR?原引官方文档的解释:
Hot Module Replacement (HMR) exchanges, adds, or removes modules while an application is running, without a full reload. This can significantly speed up development in a few ways:
- Retain application state which is lost during a full reload.
- Save valuable development time by only updating what’s changed.
- Tweak styling faster – almost comparable to changing styles in the browser’s debugger.
原引中文文档翻译:
模块热替换(HMR - Hot Module Replacement)功能会在应用程序运行过程中替换、添加或删除模块,而无需重新加载整个页面。主要是通过以下几种方式,来显著加快开发速度:
- 保留在完全重新加载页面时丢失的应用程序状态。
- 只更新变更内容,以节省宝贵的开发时间。
- 调整样式更加快速 - 几乎相当于在浏览器调试器中更改样式。
在前端开发过程中,我们需要不断的调整样式,通过webpack的HMR我们可以很方便的在保存应用状态前提下快速修改代码,极大的节省了开发时间和提升开发效率。
面临的选择
通过查阅官方文档和相关资料,使用webpack的hmr有以下3种方式,可以根据情况作出选择:
- webpack-dev-server CLI
- webpack-dev-server API
- webpack-hot-middleware
下面简要概述这3种方式:
method-1 :webpack-dev-server CLI
第一种方法是通过命令行的方式来启用webpack的HMR,移动到相关project目录下,配置相关webpack明令,对于webpack-dev-server
CLI来说,可以接受通过--config
配置传入的webpack.config.js
配置文件。仅仅需要做以下事情即可:
1 | cd your-project-path |
这种方式可以方便的用来快速开始一个webpack项目,在真实的开发项目中,建议不要试用这种方式。
更多的webpack CLI配置命令点击这里
method-2:webpack-dev-server API
第二种方法是通过webpack-dev-server来启用HMR,这种方式需要修改webpack配置项,简单来说就是webpack本地开启一个server,浏览器加载的boundle
中包含一个javaScript runtime
可以通过socket和webpack-dev-server通信,具体查看这篇文章来了解具体步骤。
method-3:webpack-hot-middleware
第三种方法是通过设置webpack-dev-middleware
+webpack-hot-middleware
来完成代码热替换的,与其他2种方法都必须要使用webpack-dev-server
不同的地方在于,方法三适合哪些即有项目。对即有的项目来说,有自己的一套本地server(通常是Express),包含了其他一些针对本地server的处理,这个时候在强行再次起一个服务(webpack-dev-server
)无疑是得不偿失的。通过webpack-dev-middleware
和webpack-hot-middleware
中间件来整合webpack热替换到即有本地服务中,从而享受webpack HMR带来的各种开发优势。
将webpack HMR整合到Express
在选择实现方案的时候,考虑到由于已经存在前端本地服务(Express),所以选择通过webpack-hot-middleware的方式来接入webpack的HMR。
需要执行以下几个步骤:
步骤1:Express接入webpack-dev-middleware
首先,webpack-dev-mddleware是什么?
它是一个简单的webpack包装中间件,通过连接服务来提供从webpack打包好的文件。注意到它相比于将boundle打包成文件有以下优势:
- 不会有硬盘文件的写入,它将打包好的文件存放在内存中。
- 当在watch模式下有文件改动,webpack-dev-middle将会延迟
boundle
的响应直到新的boundle
打包完成(也就是说,你不需要在刷新页面前等待文件的重新打包)
1 | var app = express(); |
步骤2:Express接入webpack-hot-middleware
什么是webpack-hot-middleware中间件?
通过webpack-hot-middleware中间件让你在既有server上添加热替换功能。
1 | var app = express(); |
步骤3:修改webpack配置文件
首先,需要启用webpack的热加载功能,通过webpack自带的插件即可。
1 | if(key == 'serve'){// 保证只在开发阶段试用webpack热替换 |
其次,修改每个入口文件配置,链接到webpack热加载服务器。1
2
3
4
5
6
7
8
9
10
11var hotMiddlewareScript = 'webpack-hot-middleware/client?path=/__webpack_hmr&timeout=20000&reload=true';
// 详细的配置访问==>
//https://github.com/glenjamin/webpack-hot-middleware#config
//... 省略
if(key =='serve'){
for(var entry in entrys){
if(entrys.hasOwnProperty(entry)){
entrys[entry] = [entrys[entry], hotMiddlewareScript];
}
}
}
步骤4:修改js代码
为了完成webpack的热替换,还需要在你的应用代码中添加一些额外的代码
,这些额外的代码做的事情是移除一些可能对应用产生副作用的影响。
由于通过stlye-loader来处理.css文件,而该loader自带模块热替换的功能,无需在应用代码中额外处理。
可以通过使用webpack提供的相关API来显示的处理,具体可以查看webpack官网
至此,已经完成了所有的配置修改工作,现在启动express服务器,修改某一个引入的.scss
文件,你会发现页面在不刷新的情况下重新应用了相关的样式文件。
遇到的问题
在将webpack热替换接入到项目中时,还遇到了其他的一些问题:
- 路径匹配问题(由于window和linux系统分盘符导致的打包文件路径问题)
- 如何在不影响线上打包的情况下在开发环境下接入HMR
- 如何模版文件中的js文件路径和webpack打包到内存中的路径保持一致
小结及反思
通过此次在项目中成功接入webpack HMR,更加明确及熟悉了前端架构优化的流程。总结了以下几点:
- 首先提出诉求,我们想要达成的目标是什么?==> 热替换样式,不刷新页面
- 调研实现方法有哪几种? ==> 3种,webpack-dev-server CLI / webpack-dev-server API / webpack-hot-middleware
- 是否有合适方案/根据项目现有状况挑选一种最合适的实现方案==> webpack-dev-middle+webpack-hot-middleware
- 实现注意事项有哪些?==>路径匹配/环境区分/etc
碍于篇幅,本篇仅仅解释了基于webpack的样式热替换,当然还可以实现让js文件热替换,可以查看webpack官网了解更多的loader和[HMR的API](https://doc.webpack-china.org/api/hot-module-replacement)。
参考文章
后续将会有另外的文章来讲解webpack热替换的原理,敬请期待:)