前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
工具
TVP
发布
社区首页 >专栏 >[探索]Webpack DevServer和HMR原理

[探索]Webpack DevServer和HMR原理

作者头像
前端LeBron
发布2021-12-08 16:36:07
1.8K0
发布2021-12-08 16:36:07
举报
文章被收录于专栏:前端LeBron前端LeBron

原创不易,未经作者允许禁止转载!!

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
代码语言:javascript
复制
module.exports = {
    entry: "./src/index.js",
    output: {
        filename: "js/bundle.js",
        path: path.resolve(__dirname, "build"),
    },
    watch:true,
}
  • 方式二:在启动webapck的命令中,添加--watch标识
代码语言:javascript
复制
npm script:{
 "watch": "webpack --watch"
}
# npm run watch

Webpack Dev Server

  • 上面的方式可以监听到文件的变化,但是没有自动刷新浏览器的功能
    • webpack-dev-server可以实现
  • 安装
    • npm install --save webpack-dev-server
  • 修改npm script,同时可在配置文件中devServer属性下配置devServer
代码语言:javascript
复制
script:{
    "serve":"webpack serve"
}
  • 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
代码语言:javascript
复制
const express = require("express")
const webpack = require("webpack")
const webpackDevMiddleware = require("webpack-dev-middleware")

const  app = express()
const config = require("./webpack.config")
const compiler = webpack(config)

app.use(webpackDevMiddleware(compiler,{
    publicPath:config.output.publicPath
}),()=>{
    console.log("这里是回调函数")
})

app.listen(3000,()=>{
    console.log("Server running")
})
  • 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对于我们直接访问打包后的资源其实并没有太大的作用,它的主要作用是如果我们打包后的资源,又依赖于其他的一些资源,那么就需要指定从哪里来查找这个内容:
    • 比如代码是这样的:;
    • 这样打包后浏览器无法通过相对路径去找到这个文件夹;
    • 所以代码是这样:;
    • 如何让它去查找到这个文件的存在? 设置contentBase即可;
    • 比如在index.html中,我们需要依赖一个 abc.js 文件,这个文件我们存放在 public文件中;
    • 在index.html中,我们应该如何去引入这个文件?
  • 当然在devServer中还有一个可以监听contentBase发生变化后重新编译的一个属性:watchContentBase。

Proxy代理

proxy是我们开发中常用的一个配置选项,它的目的设置代理来解决跨域访问的问题

  • 设置
    • boolean值:默认是false,如果设置为true,刷新的时候,返回404错误时,会自动返回index.html的内容
    • object值:可以配置rewrites属性
    • 可以配置from来匹配路径,决定要跳到哪个页面,详情查阅官方文档。
    • target:标识的是代理到的目标地址,比如/api/moment会被代理到http://localhost:8888/api/moment
    • pathRewrite:默认情况下,我们的/api也会被写入到URL中,如果希望删除,可以使用
    • secure:默认情况下不接受转发到https的服务器,如果希望支持,设置为false
    • changeOrigin:表示是否更新代理后请求headers中的host地址
    • historyApiFallback:解决SPA页面在路由跳转后,进行页面刷新返回404的错误

Other Config

  • hotOnly
    • 默认情况下当代码编译失败修复后会刷新页面,不希望刷新设置hotOnly:true
  • host主机地址
    • 默认值是localhost
    • 如果其他PC也可以访问可设置0.0.0.0
  • localhost和0.0.0.0的区别
    • 监听0.0.0.0时,在同一个网段下的主机中,通过IP地址是可以访问的。
    • 正常的数据库包经常 应用层 -> 传输层 -> 网络层 -> 数据链路层 -> 物理层
    • 而回环地址,在网联络层直接就被获取
    • 监听127.0.0.1时,同个网段下的主机中,通过ip地址是不能访问的。
    • localhost本质上是一个域名会被解析为127.0.0.1
    • 127.0.0.1是一个会换地址,表达的意思是主机自己发出去的包,直接被自己接受
    • 0.0.0.0:监听IPV4上所有的地址,再根据端口找到不同的应用程序。
  • Port
    • 设置监听的端口,默认为8080
  • open是否打开浏览器
    • 默认为false,true会打开浏览器
    • 也可以设置类似于Google Chrome等值
  • compress是否为静态文件开启gzip compression
    • 默认是false,可以设置为true

配置示例

代码语言:javascript
复制
devServer: {
    hot: true,
    hostOnly:true,
    host:"0.0.0.0",
    port:8080,
    open:true,
    compress:true,
    proxy:{
        "/api":{
            target:"http://localhost:8888",
            pathRewrite:{
                "^/api":""
            },
            secure:false,
            changeOrigin:true
        }

    }
}

Hot Module Replacement

  • 什么是HMR?
    • HMR全称Hot Module Replacement,翻译为模块热替换
    • 模块热替换是指在应用程序运行过程中,替换、添加、删除模块,而无需重新刷新整个页面。
  • HMR通过如下几种方式,来提高开发的速度。
    • 不重新加载整个页面,这样可以保留某些应用程序的状态不丢失;
    • 只需更新需要变化的内容,节省开发时间
    • 修改了css、js源代码,会立即在浏览器更新,相当于直接在浏览器的devtools中直接修改样式。
  • 如何使用HMR?
    • 默认情况下,webpack-dev-server已经支持HMR,只需要开启即可。
    • 在不开启HMR的情况下,修改了源代码后,整个页面会自动刷新,使用的是live reloading。
  • 如何开启
    • 修改webpack.config.js
代码语言:javascript
复制
module.exports = {
    entry: "./src/index.js",
    output: {
        filename: "js/bundle.js",
        path: path.resolve(__dirname, "build"),
    },
    watch:true,
    mode: "development",
    devServer:{
      hot:true
    },
 }
  • 更新后还是刷新整个浏览器,因为需要定义使用HMR的模块。
代码语言:javascript
复制
if(module.hot){
    module.hot.accept("./App.vue",()=>{
        console.log("vue更新了")
    })
}

框架的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
代码语言:javascript
复制
const VueLoaderPlugin = require("vue-loader/lib/plugin")

module: {
    rules: [
        {
            test: /\.vue$/,
            use: ["vue-loader"]
        },
   ]
},
plugins:[new VueLoaderPlugin()]

React的HMR

  • 在之前,React是借助React Hot Loader来实现HMR,目前已经改成使用react-refesh来实现了
  • 安装相关依赖
    • npm install @pmmmwh/react-refresh-webpack-plugin react-refresh
  • webpack.config.js
代码语言:javascript
复制
const ReactRefreshWebpackPlugin = require("@pmmmwh/react-refresh-webpack-plugin")

plugins: [
    new ReactRefreshWebpackPlugin(),
],
  • babel.config.js
代码语言:javascript
复制
module.exports = {
    presets: [
        ["@babel/preset-env", {
            useBuiltIns: "usage",
            corejs: 3.8
        }],
        ["@babel/preset-react"],
        ["@babel/preset-typescript"]
    ],
    plugins: [
        ['react-refresh/babel']
    ]
}

HMR的原理

image-20210502184548214

  • 那么HMR的原理是什么呢?如何可以做到只更新一个模块中的内容?
    • webpack-dev-server会创建两个服务:提供静态资源的服务(express)和Socket(net.Socket)
    • Express Server负责直接提供静态资源服务(打包后的资源直接被浏览器请求和解析)
  • HMR Socket Server是一个socket长连接
    • 长连接有一个最好的好处是建立连接后双方可以通信(服务器可以直接发送文件到客户端)
    • 当服务期间听到对应模块发上变化时,会生成两个文件.json(manifest文件)和.js文件(update chunk)
    • 通过长连接,可以直接将这两个文件主动发送给客户端。
    • 浏览器拿到两个新的文件后,通过HMR runtime机制,加载这两个文件,并且针对修改的模块进行更新。
本文参与 腾讯云自媒体分享计划,分享自微信公众号。
原始发表:2021-05-26,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 前端LeBron 微信公众号,前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体分享计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
目录
  • Webpack-Dev-Server
    • Webpack Watch Mode
      • Webpack Dev Server
        • Webpack Dev Middleware
          • PublicPath
            • ContentBase
              • Proxy代理
                • Other Config
                  • 配置示例
                  • Hot Module Replacement
                    • 框架的HMR
                      • Vue的HMR
                        • React的HMR
                          • HMR的原理
                          领券
                          问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档