《九》Webpack 中的 watch 模式、DevServer 和 HMR 模块热替换

目前,每次修改源代码之后,都需要手动运行 webpack 命令重新进行编译打包,并且手动刷新浏览器,才能看到最新的效果。Webpack 中有三种方案来解决这个问题。

watch 模式:

Webapck 提供了 watch 模式。在该模式下,Webapck 会监听依赖图中所有文件的源代码,一旦发现源代码发生变化,Webapck 就会自动重新进行编译打包。但是,仍需要手动刷新浏览器,才能看到最新的效果。

开启 watch 模式有两种方式:

  1. webpack.config.js 配置文件中,添加 watch:true
    module.exports = {
      watch: true
    }
    
  2. 在启动 Webpack 的脚本命令中,添加 --watch 参数。
    在这里插入图片描述

DevServer:

DevServer 会启动一个本地的服务器,并且会监听源代码的变化并自动重新进行编译打包,不再需要每次修改原代码之后都手动运行命名进行编译打包了;而且会自动刷新浏览器,不再需要手动刷新浏览器了。

  1. 安装 webpack-dev-servernpm install webpack-dev-server --save-dev

  2. 运行 webpack serve 命令将会开启 webpack-dev-server 服务。当监听到源代码发生变化,将会自动重新进行编译打包,并自动刷新浏览器。

    webpack-dev-server 是通过 express 开启一个本地服务的,默认使用 Live Reloading实时重载。

    Webpack5 之前的命令是 webpack-dev-server;但是 Webpack5 之后的命令是 webpack servewebpack-cli 检测到有 serve 这个参数,将会自动启动 webpack-dev-server 的服务。

    在这里插入图片描述

配置 DevServer:

可以在 Webpack 配置文件中配置 DevServer。

module.exports = {
  devServer: {
  	host: 0000, // 主机,默认是 localhost。
    port: 8888, // 端口号,默认是 8080
    open: true, // 是否自动打开浏览器,默认是 false
    compress: true, // 是否对打包的文件进行 gzip 压缩
  }
}

localhost 和 0.0.0.0 的区别:
localhost 其实是一个域名,会被解析为 127.0.0.1 的 IP 地址。127.0.0.1 是一个回环地址,也就是说自己主机发出去的包,直接被自己接收。在同一个局域网下的主机,通过 IP 地址是不能访问的。

正常的数据包是会经过 应用层 --> 传输层 --> 网络层 --> 数据链路层 --> 物理层;而回环地址在网络层就被获取到了,是不会经过数据链路层和物理层的。
0.0.0.0 是监听 IPV4 上的所有地址,再根据端口号找到不同的应用程序。在同一个局域网下的主机,通过 IP 地址是能访问的。

HMR:

HMR:Hot Module Replacement,模块热替换。是指在应用程序运行过程中,替换、添加、删除模块,而无需重新刷新整个页面。HMR 只更新需要变化的内容,而不会重新加载整个页面,这样可以保留应用程序的状态不丢失。

在普通项目中使用 HMR:

  1. 新建 src/index.jssrc/js/utils.js,并编写代码。

    // src/index.js
    import './js/utils'
    
    console.log('index.js')
    
    // src/js/utils.js
    console.log(1)
    
  2. 安装 webpack-dev-servernpm install webpack-dev-server --save-dev

  3. webpack.config.js 配置文件中进行配置。

    webpack-dev-server 内部已经集成了 HMR,如果想要开启 HMR,需要手动配置。

    module.exports = {
     devServer: {
     	// 配置 webpack-dev-server 开启 HMR
       hot: true,
     }
    }
    
  4. 必须指定哪些模块发生更新时,进行 HMR。

    如果不指定,当模块发生更新时,将会自动刷新整个页面。

    // 在 src/index.js 文件中指定当 utils 模块发生更新时,进行 HMR
    if(module.hot) {
      // 第一个参数是一个字符串或数组,用来指定哪些模块发生变化时要使用 HMR;第二个参数是一个函数,用来指定当模块更新时可以进行的操作
      module.hot.accept('./js/utils')
    }
    
  5. 运行 webpack server 开启 HMR。
    请添加图片描述

  6. 修改 src/js/utils.js 文件中的代码。

    // src/js/utils.js
    console.log(2)
    
  7. 会发现,浏览器保留了之前的状态,只更新了 utils 模块的内容。
    在这里插入图片描述

在框架中使用 HMR:

在 React 中使用 HMR:

在之前,React 是借助于 react-hot-loader 来实现 HMR 的,但是目前官方已经弃用了这个 loader,改为使用 reatc-refresh

如果是通过 create-react-app 脚手架创建的 React 项目,默认就已经配置好了 react-refresh
如果是开发者手动搭建的 React 项目,则需要手动配置 react-refresh

  1. 安装 reactreact-dom 用来编写 React 代码:npm install react react-dom
  2. 安装 Babel、babel-loader@babel/preset-react 用来转换 React 代码:npm install @babel/core babel-loader @babel/preset-react --save-dev
  3. 安装 html-webpack-plugin 用来自动生成打包后的 index.html 文件:npm install html-webpack-plugin --save-dev
  4. 安装 React 中实现模块热替换的库和插件:npm install react-refresh @pmmmwh/react-refresh-webpack-plugin --save-dev

    react-refresh:实现模块热替换功能。
    @pmmmwh/react-refresh-webpack-plugin : 在 Webpack 中能够使用模块热替换功能需要的插件。

  5. 新建 src/js/App.jsx 并编写代码。
    // src/js/App.jsx
    import React from 'react'
    
    export default class App extends React.Component {
        state = {
            title: 'Hello World',
        }
        render() {
            return (
                <div>{this.state.title}</div>
            )
        }
    }
    
    
  6. 新建 src/index.jssrc/index.html 文件并编写代码。
    // src/index.js
    import React from 'react'
    import ReactDOM from 'react-dom'
    import App from './js/App'
    
    console.log('index.js')
    ReactDOM.render(<App/>, document.getElementById('app'))
    
    // src/index.html
    <!DOCTYPE html>
    <html lang="en">
    <head>
      <meta charset="UTF-8">
      <meta name="viewport" content="width=device-width, initial-scale=1.0">
      <title>Document</title>
    </head>
    <body>
      <div id="app"></div>
    </body>
    </html>
    
  7. webpack.config.js 配置文件进行配置。
    const HtmlWebpackPlugin = require('html-webpack-plugin')
    // 1. 导入插件
    const ReactRefreshWebpackPlugin = require('@pmmmwh/react-refresh-webpack-plugin')
    
    module.exports = {
      mode: 'development',
      module: {
        rules: [
          {
            test: /\.jsx?$/,
            use: {
              loader: 'babel-loader',
              options: {
                presets: ['@babel/preset-react'],
                // 3. react-refresh 专门为 babel 提供的插件
                "plugins": ["react-refresh/babel"]
              }
            }
          },
        ]
      },
      plugins: [
        new HtmlWebpackPlugin({
          template: './src/index.html'
        }),
        // 使用插件
        new ReactRefreshWebpackPlugin()
      ]
    }
    
  8. 安装 webpack-dev-server 用来启动一个本地服务:npm install webpack-dev-server --save-dev
  9. 运行 webpack serve 命令将会开启本地服务。
    请添加图片描述
  10. 修改 src/js/App.jsx 文件中的代码。
    import React from 'react'
    
    export default class App extends React.Component {
        state = {
            title: 'Hello React',
        }
        render() {
            return (
                <div>{this.state.title}</div>
            )
        }
    }
    
  11. 会发现,浏览器保留了之前的状态,只更新了 App.jsx 的内容。
    在这里插入图片描述
在 Vue 中使用 HMR:

在 Vue 中,vue-loader 在加载组件时,默认就会对其进行 HMR 的处理。

  1. 安装 Vue 用来编写 Vue 代码:npm install vue
  2. 安装 vue-loader 用来处理 Vue 文件,安装 vue-template-compiler 用来对 Vue 文件中的 template 模板进行编译:npm install vue-loader vue-template-compiler --save-dev
  3. 安装 html-webpack-plugin 用来自动生成打包后的 index.html 文件:npm install html-webpack-plugin --save-dev
  4. 新建 src/js/App.vue 文件并编写代码。
    // src/js/App.vue
    <template>
    	<div>{{title}}</div>
    </template>
    
    <script>
      export default {
        data() {
          return {
            title: 'Hello Vue1111'
          }
        }
      }
    </script>
    
    <style scoped></style>
    
  5. 新建 src/index.jssrc/index.html文件并编写代码。
    // src/index.js
    import Vue from 'vue'
    import App from './js/App.vue'
    
    console.log('index.js')
    new Vue({
      render: h => h(App)
    }).$mount('#app')
    
    // src/index.html
    <!DOCTYPE html>
    <html lang="en">
    <head>
      <meta charset="UTF-8">
      <meta name="viewport" content="width=device-width, initial-scale=1.0">
      <title>Document</title>
    </head>
    <body>
      <div id="app"></div>
    </body>
    </html>
    
  6. webpack.config.js 配置文件中进行配置。
    // webpack.config.js
    // VueLoaderPlugin 插件在安装 vue-loader 时会被默认安装
    const VueLoaderPlugin = require('vue-loader/lib/plugin')
    
    module.exports = {
      module: {
        rules: [
          {
            test: /\.vue$/,
            use: 'vue-loader',
          }
        ]
      },
      plugins: [
      	new HtmlWebpackPlugin({
          template: './src/index.html'
        }),
      	// 使用 vue-loader 必须同时使用 VueLoaderPlugin 插件,才能对 Vue 文件正确处理
        new VueLoaderPlugin()
      ]
    }
    
  7. 安装 webpack-dev-server 用来启动一个本地服务:npm install webpack-dev-server --save-dev
  8. 运行 webpack serve 命令将会开启本地服务。
    请添加图片描述
  9. 修改 src/App.vue 文件中的代码代码。
    <template>
      <div>Hello Vue</div>
    </template>
    
  10. 会发现,浏览器保留了之前的状态,只更新了 App.vue 的内容。
    在这里插入图片描述

HMR 的原理:

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

  1. express server:提供静态资源的服务,通过 Node 中 express 来实现。当浏览器第一次访问网址时,就是通过 express 返回 Webpack 打包后的静态资源,浏览器对下载下来的 HTML、CSS、JS 等资源进行解析和执行。
  2. HMR server:Socket 服务,通过 Web Socket 来实现。当服务器监听到有文件发生变化时,会生成.json(manifest)和 .js (update chunk)两个文件;通过 Socket 长连接,将这两个文件主动发送给浏览器;浏览器接收到两个新的文件后,通过 HMR runtime 机制,加载这两个文件,并且只针对修改的模块进行更新。

    Socket 长连接在建立连接后,双方可以互相通信。也就是浏览器可以主动发送消息给服务器,服务器也可以主动发送消息给浏览器。

在这里插入图片描述

watch 模式、DevServer 和 HMR 的对比:

  1. 编译打包:
    • watch 模式可以监听源代码的变化并自动重新进行编译打包,不再需要每次修改原代码之后都手动运行命新进行编译打包了。但是即使只更改了一个文件的一个地方,也会对所有的源代码都重新进行编译;并且每次编译成功后,都会生成新的本地文件,文件操作是很耗费性能的。

      使用 watch 可以看到项目中有打包输出的文件。

    • DevServer 可以监听源代码的变化并自动重新进行编译打包,不再需要每次修改原代码之后都手动运行命名进行编译打包了。但是即使只更改了一个文件的一个地方,也会对所有的源代码都重新进行编译;不过每次编译成功后,不会生成新的本地文件,而只是保留在内存中,相比 watch 模式来说效率更高。

      使用 DevServer 可以看到项目中并没有有打包输出的文件。

    • HMR 可以监听源代码的变化并自动重新进行编译打包,不再需要每次修改原代码之后都手动运行命名进行编译打包了;并且它只会重新编译发生了变化的模块,而不会重新编译整个应用程序;而且每次编译成功后,不会生成新的本地文件,而只是保留在内存中。

      使用 HMR 可以看到项目中并没有有打包输出的文件。

  2. 在浏览器中渲染:
    • watch 模式没有自动刷新浏览器的功能,需要手动刷新浏览器才能看到编译打包后最新的效果。
    • DevServer 虽然会自动刷新浏览器,但是会刷新整个页面。
    • HMR 会自动刷新浏览器,但是不会重新加载整个页面,只会更新发生了变化的模块,没有发生变化的模块的状态就可以被保留下来。
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值