一、DevServer和HMR

为什么要搭建本地服务器

目前我们开发的代码,为了运行需要有两个操作:

  • 操作一:npm run build,编译相关的代码
  • 操作二:通过live server或者直接通过浏览器,打开index.html代码,查看效果

这个过程经常操作会影响我们的开发效率

  • 我们希望可以做到,当文件发生变化时,可以自动的完成编译和展示
  • 为了完成自动编译,webpack提供了几种可选的方式:

    • webpack watch mode
    • webpack-dev-server
    • webpack-dev-middleware

接下来,一个个来学习一下它们

Webpack watch

webpack给我们提供了watch模式:

  • 在该模式下,webpack依赖图中的所有文件,只要有一个发生了更新,那么代码将被重新编译
  • 我们不需要手动去运行 npm run build指令了

如何开启watch呢?两种方式:

  • 方式一:在webpack.config.js配置文件中,添加 watch: true
  1. watch: true,
  2. entry: './src/index.js',
  3. output: {
  4. filename: 'bundle.js',
  5. path: path.resolve(__dirname, 'dist'),
  6. },
  • 方式二:在启动webpack的命令中,添加 —watch的标识脚本
  1. "scripts": {
  2. "test": "echo \"Error: no test specified\" && exit 1",
  3. "build": "webpack",
  4. "watch": "webpack --watch"
  5. },

npm run watch 即可

webpack-dev-server

watch模式可以监听到文件的变化,但是有以下缺点

  • 但是事实上它本身是没有自动刷新浏览器的功能的:

  • 目前我们可以在VSCode中使用live-server来完成这样的功能

  • 我们希望在不适用live-server的情况下,可以具备live reloading(实时重新加载)的功能

  • watch模式会对所有的源代码进行重新编译

  • 编译成功后,都会生成新的文件(文件操作,效率不高)

  • live-server每次都会刷新整个页面,效率不高

如何解决? webpack-dev-server

安装:

  • npm install —save-dev webpack-dev-server

配置:

  1. //webpack.config.js文件 可以使用其他值
  2. mode: 'development',
  3. //package.json文件
  4. //webpack5之前是 webpack-dev-server
  5. "serve": "webpack serve"

编译:

  • npm run serve

注意:

  • webpack-dev-server 在编译之后不会写入到任何输出文件。而是将 bundle 文件保留在内存中
  • 事实上webpack-dev-server使用了一个库叫memfs(之前是memory-fs webpack自己写的)

webpack-dev-middleware

默认情况下,webpack-dev-server已经帮助我们做好了一切:

  • 比如通过express启动一个服务
  • 比如HMR(热模块替换)

如果我们想要有更好的自由度,可以使用webpack-dev-middleware

什么是webpack-dev-middleware

  • webpack-dev-middleware 是一个封装器(wrapper),它可以把 webpack 处理过的文件发送到一个 server
  • webpack-dev-server 在内部使用了它,然而它也可以作为一个单独的 package 来使用,以便根据需求进行 更多自定义设置

webpack-dev-middleware的使用

安装,也可以用koa 主要是开启一个服务器

  • npm install —save-dev express webpack-dev-middleware

编写文件src同级目录下编写server.js

  1. const express = require('express');
  2. const webapck = require('webpack');
  3. const webpackDevMiddleware = require('webpack-dev-middleware');
  4. const app = express();
  5. //加载配置信息
  6. const config = require('./webpack.config');
  7. //将配置信息传递给webpack进行编译
  8. const compiler = webapck(config);
  9. //将编译后的结果传递给webpack-dev-middleware,返回的中间件供app使用
  10. const middleware = webpackDevMiddleware(compiler);
  11. app.use(middleware);
  12. app.listen(3000);

6、webpack-devserver - 图1

二、HMR

HMR的全称是Hot Module Replacement,翻译为模块热替换

模块热替换是指在应用程序运行过程中,替换、添加、删除模块,而无需重新刷新整个页面

HMR通过如下几种方式,来提高开发的速度:

  1. 不重新加载整个页面,这样可以保留某些应用程序的状态不丢失
  2. 只更新需要变化的内容,节省开发的时间
  3. 修改了css、js源代码,会立即在浏览器更新,相当于直接在浏览器的devtools中直接修改样式

使用HMR

  1. 默认情况下,webpack-dev-server已经支持HMR,我们只需要开启即可
  2. 在不开启HMR的情况下,当我们修改了源代码之后,整个页面会自动刷新,使用的是live reloading

webpack.config.js文件:

  1. devServer: {
  2. hot: true,
  3. },

入口文件index.js:

  1. import './math';
  2. console.log('Hello Webpack11');
  3. //监听math模块的更新
  4. if (module.hot) {
  5. module.hot.accept('./math.js', () => {
  6. console.log('模块热更新');
  7. });
  8. }

此时修改math模块内容

6、webpack-devserver - 图2

框架中的HMR

在开发其他项目时,我们是否需要经常手动去写入 module.hot.accpet相关的API呢?

比如开发Vue、React项目,我们修改了组件,希望进行热更新,这个时候应该如何去操作呢

事实上社区已经针对这些有很成熟的解决方案了:

  • 比如vue开发中,我们使用vue-loader,此loader支持vue组件的HMR,提供开箱即用的体验
  • 比如react开发中,有React Hot Loader,实时调整react组件(目前React官方已经弃用了,改成使用reactrefresh)

React中的HMR

在之前,React是借助于React Hot Loader来实现的HMR,目前已经改成使用react-refresh来实现了

安装实现HMR相关的依赖:

npm install -D @pmmmwh/react-refresh-webpack-plugin react-refresh

注意:这里安装@pmmmwh/react-refresh-webpack-plugin,最新的npm安装有bug(建议使用lts版本对应的npm版本)

修改webpack.config.js和babel.config.js文件:

  1. const ReactRefreshWebpackPlugin = require('@pmmmwh/react-refresh-webpack-plugin')
  2. plugins:[
  3. new ReactRefreshWebpackPlugin()
  4. ]
  1. module.exports = {
  2. presets:[
  3. ["@babel/preset-env"],
  4. ["@babel/preset-react"]
  5. ],
  6. plugins:[
  7. ['react-refresh/babel']
  8. ]
  9. }

Vue中的HMR

Vue的加载我们需要使用vue-loader,而vue-loader加载的组件默认会帮助我们进行HMR的处理

安装加载vue所需要的依赖:

npm install vue-loader vue-template-compiler -D

配置webpack.config.js:

  1. const VueLoaderPlugin = require('vue-loader/lib/plugin')
  2. {
  3. test:/\.vue$/
  4. use:'vue-loader'
  5. }
  6. plugins:[
  7. new VueLoaderPlugin()
  8. ]

HMR原理

webpack-dev-server会创建两个服务:

  • 提供静态资源的服务(express)
  • Socket服务(net.Socket)

express server负责直接提供静态资源的服务(打包后的资源直接被浏览器请求和解析)

  1. HMR Socket Server,是一个socket的长连接: 长连接有一个最好的好处是建立连接后双方可以通信(服务器可以直接发送文件到客户端)
  2. 当服务器监听到对应的模块发生变化时,会生成两个文件.json(manifest文件)和.js文件(update chunk)
  3. 通过长连接,可以直接将这两个文件主动发送给客户端(浏览器)
  4. 浏览器拿到两个新的文件后,通过HMR runtime机制,加载这两个文件,并且针对修改的模块进行更新

6、webpack-devserver - 图3

这说的有些简单,后续再深入吧。