[探索]Webpack DevServer和HMR原理
写作不易,未经作者允许禁止以任何形式转载!
如果觉得文章不错,欢迎关注、点赞和分享!
Webpack-Dev-Server
为什么要搭建本地服务器
-
目前开发的代码,为了运行需要有两个操作
npm run build
编译- 通过live-server或者直接通过浏览器打开html文件,查看效果
-
为了完成自动编译,webpack提供了几种可选的方式
- Webpack watch mode
- Webpack-dev-server
- Webpack-dev-middleware
Webpack Watch Mode
-
webpack提供了watch模式
- 在该模式下,webpack依赖图中所有文件,只要有一个发生了更新,那么代码将被重新编译。
- 不需要手动
npm run build
-
如何开启?
- 方式一:在导出的配置中,添加watch:true
module.exports = { |
- 方式二:在启动webapck的命令中,添加–watch标识
npm script:{ |
Webpack Dev Server
-
上面的方式可以监听到文件的变化,但是没有自动刷新浏览器的功能
- webpack-dev-server可以实现
-
安装
npm install --save webpack-dev-server
-
修改npm script,同时可在配置文件中devServer属性下配置devServer
script:{ |
- webpack-dev-server在编译之后不会写入到任何输出文件。而是将bundle文件保留在内存中
- 事实上webpck-dev-server使用了一个叫memfs的库。
Webpack Dev Middleware
-
webpack-dev-middleware是一个封装器,它可以把webpack处理过的文件发送到一个server
- webpack-dev-server在内部使用了它,然而它也可以作为一个单独的package来使用,以便根据需求进行更多自定义配置
- 搭配一个服务器来使用它,比如express.
npm install --save express webpack-dev-middleware
-
编写Server.js
const express = require("express") |
- Node Server.js即可运行起一个服务,并监听文件更改和刷新浏览器。
PublicPath
-
Output中有两个很重要的属性:path和publicPath
- path:用于指定文件的输出路径,是一个聚堆路径
- publicPath:默认是一个空字符串,它为我们项目中的资源制定一个公共的路径publicPath
-
这个publicPath很不容易理解,其实就是给我们打包的资源,给它一个路径
- 资源的路径 = output.publicePath + 打包资源的路径(比如"js/[name].bundle.js")
-
常用的值
- ./ :本地环境下可以使用这个相对路径
- / :服务器部署时使用,服务器地址 + /js/[name].bundle.js
-
devServer的publicPath、output的publicPath和[webpackDevMiddleware的publicPath]需一致
ContentBase
-
devServer中contentBase对于我们直接访问打包后的资源其实并没有太大的作用,它的主要作用是如果我们打包后的资源,又依赖于其他的一些资源,那么就需要指定从哪里来查找这个内容:
- 比如在index.html中,我们需要依赖一个 abc.js 文件,这个文件我们存放在 public文件中;
- 在index.html中,我们应该如何去引入这个文件?
- 比如代码是这样的:;
- 这样打包后浏览器无法通过相对路径去找到这个文件夹;
- 所以代码是这样:;
- 如何让它去查找到这个文件的存在? 设置contentBase即可;
-
当然在devServer中还有一个可以监听contentBase发生变化后重新编译的一个属性:watchContentBase。
Proxy代理
proxy是我们开发中常用的一个配置选项,它的目的设置代理来解决跨域访问的问题
- 设置
- target:标识的是代理到的目标地址,比如/api/moment会被代理到http://localhost:8888/api/moment
- pathRewrite:默认情况下,我们的/api也会被写入到URL中,如果希望删除,可以使用
- secure:默认情况下不接受转发到https的服务器,如果希望支持,设置为false
- changeOrigin:表示是否更新代理后请求headers中的host地址
- historyApiFallback:解决SPA页面在路由跳转后,进行页面刷新返回404的错误
- boolean值:默认是false,如果设置为true,刷新的时候,返回404错误时,会自动返回index.html的内容
- object值:可以配置rewrites属性
- 可以配置from来匹配路径,决定要跳到哪个页面,详情查阅官方文档。
Other Config
-
hotOnly
- 默认情况下当代码编译失败修复后会刷新页面,不希望刷新设置hotOnly:true
-
host主机地址
- 默认值是localhost
- 如果其他PC也可以访问可设置0.0.0.0
-
localhost和0.0.0.0的区别
- localhost本质上是一个域名会被解析为127.0.0.1
- 127.0.0.1是一个会换地址,表达得意思是主机自己发出去的包,直接被自己接受
- 正常的数据库包经常 应用层 -> 传输层 -> 网络层 -> 数据链路层 -> 物理层
- 而回环地址,在网联络层直接就被获取
- 监听127.0.0.1时,同个网段下的主机中,通过ip地址是不能访问的。
- 0.0.0.0:监听IPV4上所有的地址,再根据端口找到不同的应用程序。
- 监听0.0.0.0时,在同一个网段下的主机中,通过IP地址是可以访问的。
-
Port
- 设置监听的端口,默认为8080
-
open是否打开浏览器
- 默认为false,true会打开浏览器
- 也可以设置类似于Google Chrome等值
-
compress是否为静态文件开启gzip compression
- 默认是是false,可以设置为true
配置示例
devServer: { |
Hot Module Replacement
-
什么是HMR?
- HMR全称Hot Module Replacement,翻译为模块热替换
- 模块热替换是指在应用程序运行过程中,替换、添加、删除模块,而无需重新刷新整个页面。
-
HMR通过如下几种方式,来提高开发的速度。
- 不重新加载整个页面,这样可以保留某些应用程序的状态不丢失;
- 只需更新需要变化的内容,节省开发时间
- 修改了css、js源代码,会立即在浏览器更新,相当于直接在浏览器的devtools中直接修改样式。
-
如何使用HMR?
- 默认情况下,webpack-dev-server已经支持HMR,只需要开启即可。
- 在不开启HMR的情况下,修改了源代码后,整个页面会自动刷新,使用的是live reloading。
-
如何开启
- 修改webpack.config.js
module.exports = { |
- 更新后还是刷新整个浏览器,因为需要定义使用HMR的模块。
if(module.hot){ |
框架的HMR
有一个问题:在开发其他项目时,我们是否需要经常手动去写入 module.hot.accpet相关的API呢?
-
比如开发Vue、React项目,我们修改了组件,希望进行热更新,这个时候应该如何去操作?
-
社区已经针对这些有很成熟的解决方案了:
- 比如vue开发中,我们使用vue-loader,此loader支持vue组件的HMR,提供开箱即用的体验;
- 比如react开发中,有React Hot Loader,实时调整react组件(目前React官方已经弃用了,改成使用react- refresh);
Vue的HMR
-
Vue的加载需要vue-loader,而vue-loader加载的默认会进行HMR处理
-
安装加载Vue所需依赖
npm install vue-loader vue-template-compiler
-
配置Webpack.config.js
const VueLoaderPlugin = require("vue-loader/lib/plugin") |
React的HMR
-
在之前,React是借助React Hot Loader来实现HMR,目前已经改成使用react-refesh来实现了
-
安装相关依赖
npm install @pmmmwh/react-refresh-webpack-plugin react-refresh
-
webpack.config.js
const ReactRefreshWebpackPlugin = require("@pmmmwh/react-refresh-webpack-plugin") |
- babel.config.js
module.exports = { |
HMR的原理
-
那么HMR的原理是什么呢?如何可以做到只更新一个模块中的内容?
- webpack-dev-server会创建两个服务:提供静态资源的服务(express)和Socket(net.Socket)
- Express Server负责直接提供静态资源服务(打包后的资源直接被浏览器请求和解析)
-
HMR Socket Server是一个socket长连接
- 长连接有一个最好的好处是建立连接后双方可以通信(服务器可以直接发送文件到客户端)
- 当服务期间听到对应模块发上变化时,会生成两个文件.json(manifest文件)和.js文件(update chunk)
- 通过长连接,可以直接将这两个文件主动发送给客户端。
- 浏览器拿到两个新的文件后,通过HMR runtime机制,加载这两个文件,并且针对修改的模块进行更新。
[探索]Webpack DevServer和HMR原理 (juejin.cn)
掘金:前端LeBron
知乎:前端LeBron
持续分享技术博文,关注微信公众号👇🏻