// Vue3.0写法
import {createApp} from 'vue';
import App from './App.vue';
createApp(App).mount('#root')
模块热替换HMR的原理
HMR是如何做到只更新模块内容的变化而不刷新整个浏览器的呢?
这是因为webpack-dev-serve在工作的时候会创建两个服务:
基于express创建的提供静态资源的服务
基于web Socket创建的实时通信服务(net.Socket)
当我们将源代码首次打包编译之后,会将打包之后的bundle文件夹放在express创建的服务上,然后浏览器访问localhost:8080端口就会发起HTTP请求,express创建的静态资源服务器作出响应,将浏览器请求的资源返回,浏览器拿到这些资源之后进行解析和加载,这就是我们首次启动页面的时候的webpack dev server为我们做的事情。
当模块中的某个地方发生变化的时候,由于开启了HMR,HMR是一个Socket server,它是一个socket的长连接服务,意味着建立连接之后客户端和服务器之间可以互相通信,服务器可以直接发送资源到浏览器。当HMR服务监听到对应的模块发生变化的时候,会生成两个文件:
manifest.json:用于描述模块的哪些地方发生了变化的信息
update chunk:发生更新的模块代码
然后基于长连接将这两个文件主动的推送给客户端浏览器,浏览器在接受到这两个新的文件之后,通过HMR runtime机制,加载这两个文件,并且针对修改的模块进行更新,所以之前加载过的静态资源不会重新请求一遍并加载,浏览器也就不会刷新整个页面,而是只对变化的模块进行加载,这就是HMR实现的原理。
devServer的配置项
注意:所有devServer的配置项都是针对于开发过程中的一些配置。
hot选项用来指定是否开启HMR以及如何开启。
值为true,代表开启HMR。
值得注意的是在webpack-dev-server的4.0+版本开始,HMR是默认开启的,所以无需我们手动开启了。
值为only
默认情况下源代码编译出错,修复错误后重新编译成功之后会刷新整个浏览器页面,代表重新加载所有代码
当值为only的时候,修复错误重新编译之后只刷新出错的那个模块代码,不会重新刷新整个浏览器页面
host选项用于设置WDS启动的本地服务器的主机地址
默认值为localhost
localhost本质代表一个域名,通常情况下会被解析为127.0.0.1,也就是回环地址(Loop Back Address)。意思就是我们自己主机发出去的包直接被自己接收。
一般情况下正常的数据包传输是应用层-传输层-网络层-数据层-物理层,而回环地址发出去的包会在网络层就被捕获到,是不会经过数据层和物理层的。所以当我们监听127.0.0.1的时候,在同一个网段下的主机中通过ip地址是不能访问的。
如果设置值为0.0.0.0
代表监听IPV4上所有的地址,再根据端口号找到不同的应用程序。因此监听0.0.0.0的时候,在同一个网段下的主机通过ip地址是可以访问的。
port选项用于设置本地服务的端口号,默认值是8080。
open选项用于设置是否每次编译成功之后自动打开浏览器
默认值是false也就是不打开
可以设置布尔值true默认打开浏览器,还可以设置浏览器应用程序名称google-chrome等打开对应的浏览器
compress
compress选项用于设置是否为静态文件开启gzip压缩
默认值是false也就是不压缩
设置为true代表在浏览器请求本地服务器上静态文件的时候开启gzip压缩,可以提高开发时的效率。
proxy
proxy选项主要用来解决在项目的开发阶段通过设置代理来解决前后端跨域请求的问题。
比如当前前端项目是run serve之后,浏览器在获取项目打包之后静态资源的时候其实是向域名为:http://localhost:8080端口的本地静态资源服务器发起的请求,但是如果我们想在项目中请求后端的API接口的时候,比如后端的服务器地址为:http://127.168.15.12:5500,此时当浏览器发起http网络请求的时候就会出现跨域请求而无法访问的错误,这是因为浏览器的同源策略限制的,因为浏览器只允许对相同域名、端口号和协议的服务器发起请求,然而项目中请求的服务器一个是localhost:8080,另外一个是http://127.168.15.12:5500,因此浏览器会报跨域。
为了解决跨域问题,首先要明确一个解决的思路,因为同源策略仅仅是限制浏览器端不能在同一页面向不同源的服务器发起请求,如果我们设置一个代理服务器,将所有浏览器发出的请求都先发到这个代理服务器上,然后由这个代理服务器发起请求去不同的源获取数据,然后将获取的数据返回给浏览器,这样就等于绕开了浏览器的同源策略的限制,因为服务器端是没有同源限制的。
在浏览器中直接发起对一个非同源服务器API data接口的请求:
axios.get("http://127.168.15.12:5500/data").then(res=>{
console.log(res);
}).catch(err)=>{
console.log(err);
此时由于跨域,浏览器会报跨域错误。
target属性
为了解决这个问题我们配置一个proxy代理:
devServer:{
proxy:{
"/kai":{
target:"http://127.168.15.12:5500",
以上配置代表所有浏览器发起的请求URL中包含"/kai"的话,webpack dev server就会通过一系列操作将这个请求转发到目标服务器"http://127.168.15.12:5500"上。
因此,配置了proxy之后,我们就需要修改下我们的请求路径为"/kai/data",这样子请求路径中就包含了"/kai"才会被转发到目标服务器:
axios.get("/kai/data").then(res=>{
console.log(res);
}).catch(err)=>{
console.log(err);
再次发起请求之后发现不会报跨域错误的,但是会报一个404资源找不到的错误。这是因为当浏览器在发起请求的时候,由于当前的请求路径是"/kai/data",浏览器就会自己在前面加上协议和域名,所以此时实际的完整请求URL是:
http://localhost:8080/kai/data
由于我们配置了代理proxy中的target:"http://127.168.15.12:5500",所以代理服务器会将"http://localhost:8080/kai/data"请求映射为:
http://127.168.15.12:5500/kai/data
然而实际请求的资源其实是存放在"http://127.168.15.12:5500/data"上的,由于请求路径中多了一个/kai,所以服务器找不到资源返回了404。现在的问题是在请求发出前将"/kai"去掉,这样子就可以了,这个需求可以通过pathRewrite选项来配置
pathRewrite属性
devServer:{
proxy:{
"/kai":{
target:"http://127.168.15.12:5500",
pathRewrite:{
"^/kai":""
以上配置代表所有以"/kai"开头的请求在转发到目标服务器之前,将/kai替换为空字符串,这样以来最后发起的请求路径就符合要求,返回正常结果。
http://127.168.15.12:5500/data
secure属性
默认情况下,WDS不接受转发到HTTPS的服务器上,如果希望支持,需要设置secure为false也就是关闭HTTPS检查。
devServer:{
proxy:{
"/kai":{
target:"http://127.168.15.12:5500",
pathRewrite:{
"^/kai":""
secure:false
changeOrigin属性
除此之外,常见的配置proxy的时候还会有一个changeOrigin属性,它表示是否更新代理请求中的headers中的host地址。
如果不配置changeOrigin属性:那么目标服务器接收到的请求的来源就是"http://localhost:8080";如果目标服务器127.168.15.12:5500不对所有请求的源做校验的话,那么可以不配置changeOrigin属性,但是如果目标服务器开启了对于请求源校验的话,也就是如果请求路径中的域名不是127.168.15.12的话,那么就拒绝这次请求。
所以为了避免目标服务器做来源校验而请求失败的问题,我们需要将changeOrigin设置为true,代表最后真实的发送给目标服务器的请求中其请求地址是目标服务器的域名,而不是localhost:8080。
devServer:{
proxy:{
"/kai":{
target:"http://127.168.15.12:5500",
pathRewrite:{
"^/kai":""
secure:false,
changeOrigin:true
注意:WDS在实现Proxy的代理功能的时候,其实是用了现成的http-proxy-middleware和http-proxy两个库来实现的。
historyApiFallback属性
historyApiFallback选项主要用于解决在SPA单页面应用中,当路由发生跳转之后如果刷新页面,报404错误的问题。
不管是Vue还是React项目,先在一般都是SPA单页面应用,所以使用的都是前端路由。前端路由的核心原理就是监听浏览器地址栏的路由变化,然后执行相应的js代码最后渲染不同的组件。但是如果在History路由模式下我们切换了路由后又刷新了页面,此时浏览器会认为是去服务器获取对应的资源,所以就会报404的错误。
此时,我们只需要配置devServer中的historyApiFallback属性为true即可解决这个问题,因为默认情况下开启之后如果服务器找不到资源就会将打包之后的index.html文件返回给浏览器,此时浏览器获取到html文件就不会报错了:
devServer:{
historyApiFallback:true
当然historyApiFallback的值可以是一个布尔值,还可以是一个对象,如果是一个对象的话需要设置rewrites来设置一到多个不同路径要返回不同页面的配置:
注意:设置这些不同的html的前提是打包之后的文件夹中必须包含这些html页面
devServer:{
historyApiFallback: {
rewrites: [
{ from: /^\/$/, to: 'index.html' }, // 当刷新的页面路径为/的时候,默认返回index.html
{ from: /^\/subpage/, to: 'subpage.html' },
{ from: /./, to: '404.html' }, // 页面路由为任意组合值的时候,返回404.html