从零到一搭建 react 项目系列之(十二)

简介: 今天结合 React 搭建更友好的热更新效果。

前言


此前讲解 react-router、redux、react-redux、redux-saga 所涉及的内容较多,篇幅也较长。终于可以介绍 HMR 模块热更新。

其实此前已经介绍过了,但今天就结合 React 搭建更友好的热更新效果。


正文


一、HMR 实现


此前是怎么做的呢?


当然这种方式是没有针对使用 React 做优化处理的。

// webpack.config.js
modules.exports = {
  mode: 'develop',
  devServer: {
    // 需要注意的是,要完全启用 HMR,需要 webpack.HotModuleReplacementPlugin
    hot: true
  },
  optimization: {
    // 告知 webpack 使用可读取模块标识符,来帮助更好地调试。开发模式默认开启。简单来说,开启时你看到的是一个具体的模块名称,而不是一个数字 id。
    namedModules: true
  },
  plugins: [
    // 通过它启用 HMR,那么它的接口将被暴露在 module.hot 属性下面。
    new webpack.HotModuleReplacementPlugin()
  ],
  module: {
    rules: [
      {
        // 样式热更新,借助于 style-loader,其实幕后使用了 module.hot.accept
        test: /\.css/,
        use: ['style-loader', 'css-loader']
      }
    ]
  }
}


我们还需在入口文件添加 module.hot.accpet() 方法。

// index.js
import React from 'react'
import { render } from 'react-dom'
import '../styles/style.css'
import Root from './Root'
// 最简单的 React 示例
const rootElem = document.getElementById('app')
render(<Root />, rootElem)
// 通常,先检查 HotModuleReplacementPlugin 暴露的接口是否可访问,然后再开始使用它。
if (module.hot) {
  // accept 方法接受给定的依赖模块的更新,并触发一个回调函数来对这些更新做出响应。
  module.hot.accept('./Root', () => {
    import('./Root.js').then(module => {
      const NextRoot = module.default
      render(<NextRoot/>, rootElem)
    })
  })
}


需要注意的是:

原先的 new webpack.NameModulesPlugin() 在 webpack 4 中已废弃,取代它的是 optimization.nameModules。开发模式下默认开启,生产模式下,默认关闭。关于开启与禁用,最直观的区别如下图。


尝试随便修改一下 Home 组件,看到控制台的输出(如下图),两者区别不同。开启时,能看到具体涉及更新的模块有哪些,而关闭状态则是只能看到一个数字 id。


66.webp.jpg

关闭状态

77.webp.jpg

开启状态


二、HMR 搭配 React 的不足


上面的方式,如果搭配 React 使用的话,其实还不够友好。

用一个最简单的例子说明问题

// 一个非常简单的有状态组件
import React, { Component } from 'react'
class HMRDemo extends Component {
  constructor(props) {
    super(props)
    this.state = {
      count: 0
    }
  }
  render() {
    return (
      <div>
        <h3>HMR Demo Component!</h3>
        <h5>计数器:{this.state.count}</h5>
        <button onClick={() => { this.setState({ count: ++this.state.count }) }}>add</button>
      </div>
    )
  }
}
export default HMRDemo


这时候我们点击按钮,然后组件状态 count 自然变成了 1,这个没问题。接着,我们随意增加一个标签元素,然后页面自然会热更新,但是我们看到,count 又变成了 0,组件状态丢失了,如下图:

88.webp.jpg

我们享受着 HMR 给我们开发带来的便利,但同时我们又不想丢失 Component State 组件状态,怎么做呢?

为了解决这个问题,React Hot Loader 出现了。


三、React Hot Loader


先看一下官方指南的一段原话

React-Hot-Loader is expected to be replaced by React Fast Refresh. Please remove React-Hot-Loader if Fast Refresh is currently supported on your environment.


大概的意思是,react-hot-loader 将会被 React Fast Refresh 取代。如果您当前的环境支持的话,请移除 react-hot-loader。

*至于 React Fast Refresh 是什么?如何使用?这里不展开述说,可以看一下其他人写的一篇文章


继续介绍 react-hot-loader,还需要在上面的基础上,添加以下配置。


  1. 安装依赖包

$ yarn add --dev react-hot-loader@4.12.19
# 下面步骤用到
$ yarn add --dev @hot-loader@react-dom@16.12.0


@hot-loader/react-dom 是在 react-dom 相同版本的基础上,添加了一些支持热更新的补丁。所以需要安装与 react-dom 一致的版本。

  1. 添加 react-hot-loader/babel.babelrc 配置中。

// .babelrc
{
  "plugins": [
    "react-hot-loader/babel"
  ]
}


  1. 将根组件标记为热导出(hot-exported

import React from 'react'
import { Provider } from 'react-redux'
import { hot } from 'react-hot-loader/root'
import App from './pages/App'
import store from './store'
const Root = () => {
  return (
    <Provider store={store}>
      <App />
    </Provider>
  )
}
export default hot(Root)
// 温馨提示
// 关于新 API 👉 hot 是位于 '/root' 下面的,但并不是所有的打包工具都支持该新 API。比如 parcel 就不支持。
// 此时 react-hot-loader 就会抛出错误,并要求你使用旧的 API,方法如下:
// 
// import { hot } from 'react-hot-loader'
// export default hot(module)(App)


  1. 确保在  reactreact-dom 之前导入 react-hot-loader,两种方式可选择:
  • 在入口文件导入 import react-hot-loader(要在 React 之前)
  • 在 webpack 配置文件的 entry 配置 react-hot-loader/patch

// webpack.config.js
{
  entry: [
    'react-hot-loader/patch',
    './src/js/index.js'
  ]
}


需要注意的是:react-hot-loader/patch 一定要写在 entry 的最前面。如果有 babel-polyfill 就写在 babel-polyfill 的后面。

  1. 使用 React Hooks 需要用到 @hot-loader/react-dom。同时,就以上配置重新启动,此时控制台会打印一个 WARNING,如下:


React-Hot-Loader: react-🔥-dom patch is not detected. React 16.6+ features may not work. (Issue #1227


解决方案之一,另一种可以看下 ISSUE #1227

// webpack.config.js
{
  resolve: {
    alias: {
      'react-dom': '@hot-loader/react-dom'
    }
  }
}


测试一下,分别两次点击 add 后,再添加一个节点元素,发现 Component State 是在我们预期之内的,并没有像之前一样因为热更新而丢失状态。

9.webp.jpg


四、至此


关于 Hot Module Replacement 模块热替换(热更新)基本就介绍完了。

前面我们提到一个 React Fast Refresh 概念,它由官方维护,稳定性与性能有保障,对 React Hooks 有更完善的支持。官方实现是 react-refresh

后面有时间的话,应该会写一篇关于它的文章。但是,它最低支持版本是 react-dom@16.9+


The end.

目录
相关文章
|
8月前
|
前端开发 JavaScript 测试技术
从零开始搭建react+typescript+antd+redux+less+vw自适应项目
从零开始搭建react+typescript+antd+redux+less+vw自适应项目
205 0
|
8月前
|
前端开发 API 数据安全/隐私保护
【第45期】一文解决React项目的权限管理
【第45期】一文解决React项目的权限管理
370 0
|
8月前
|
Web App开发 资源调度 JavaScript
竟然可以在一个项目中混用 Vue 和 React?
竟然可以在一个项目中混用 Vue 和 React?
767 0
|
8月前
|
运维 JavaScript 前端开发
发现了一款宝藏学习项目,包含了Web全栈的知识体系,JS、Vue、React知识就靠它了!
发现了一款宝藏学习项目,包含了Web全栈的知识体系,JS、Vue、React知识就靠它了!
119 2
|
8月前
|
存储 JSON 前端开发
react保姆级搭建新项目
此文主要以ts+vite+router6+antd 快速搭建一个react项目,适用于初学者,用于学习交流
122 2
|
8月前
|
移动开发 JavaScript 前端开发
vue/react项目刷新页面出现404的原因以及解决办法
vue/react项目刷新页面出现404的原因以及解决办法
747 0
|
8月前
|
存储 资源调度 前端开发
【React | 完整项目创建流程】能看到这么详细的React配置流程,就偷着乐吧!
【React | 完整项目创建流程】能看到这么详细的React配置流程,就偷着乐吧!
232 1
|
8月前
|
前端开发 JavaScript API
Github 上 8 个很棒的 React 项目
Github 上 8 个很棒的 React 项目
2122 0
|
8月前
|
前端开发 开发工具 git
React项目包结构的作用
React项目包结构的作用
123 0
|
8月前
|
前端开发
React 中 react-i18next 切换语言( 项目国际化 )
React 中 react-i18next 切换语言( 项目国际化 )
697 0