webpack 4 搭建 React 架构:实现热更新(二)

上一篇文章 webpack 4 搭建React antd 中后台项目架构 实现了基本的架构工作,让项目可以跑起来,虽说依照 webpack 中文官网,在 webpack.dev.js 中配置了 hot,只有修改样式的时候才能热更新,可以像在浏览器中修改样式一样的快速,修改 js 文件依然没有效果,主要是原因是 style-loader 实现了HMR接口,而 react 的 js 文件并没有实现这个功能:

//webpack.dev.js
...
devServer: {
        contentBase: path.resolve(__dirname, 'dist'),
+        hot: true,
        hotOnly: true,
        open: false,  //自动打开浏览器
        port: 9000,
        overlay: {
            warnings: false,
            errors: true
        }
    },
    plugins: [
+        new webpack.NamedModulesPlugin(),
+        new webpack.HotModuleReplacementPlugin()
    ],
...
//main.js
import React from 'react'
import ReactDOM from 'react-dom'
import App from './app'
import Layout from './components/Layout/Layout'
import './css/main'
import './css/base'

ReactDOM.render(
    //,
    ,
    document.getElementById('app')
)

+ if(module.hot){
+     module.hot.accept();
+ }

猜想可能是引入 react 的缘故,于是重新加一个入口文件 index.js 来使验证一下

//webpack.common.js
...
entry: {
  app: './src/index.js'
}
...
//入口文件 index.js
import { square } from './math.js'
console.log('打印2的平方',square(2))

let arr = [1,2,3];
let arr2 = arr.map(item => item + 2)
arr.includes(8)
console.log('new Set(arr2):', new Set(arr2))

async function l() {
    return await 1;
}
l().then((value) => {
    console.log(value);
    console.log(111111)
})

const app = document.getElementById('app')
app.innerHTML = 'hello world';

+ if(module.hot){
+     module.hot.accept();
+ }

这样在启动项目,刷新页面,可以看到直接修改 index.js 中的 html 内容,页面实时更新了。由此可见 react 框架并没有做热更新,以前用 vue-cli 创建项目的时候,是可以进行热更新的,原因是 vue-loader 实现了这个功能,而 react 没有做到这样,所有我们需要借助插件来实现,插件就是 react-hot-loader

现在通过 react-hot-loader 来实现 react 的热更新:

  • 安装最新版的 react-hot-loader;
cnpm install @hot-loader/react-dom --save-dev
  • 将入口文件换成 main.js
//webpack.common.js
...
entry: {
  app: './src/main.js'
}
...
  • 修改 root component (src/components/Layout/Layout.js),在 react react-dom 之前引入 react-hot-loader/root,然后将组件用 hot 包起来;
+ import { hot } from 'react-hot-loader/root'
import React from 'react'
import { Layout, Menu, Breadcrumb, Icon } from 'antd'
import Header from '../Header/Header'
import Footer from '../Footer/Footer'
import './layout.less'

const { Content, Sider } = Layout
const { SubMenu } = Menu

class LayoutContainer extends React.Component {
    state = {
        collapsed: false,
    }

    onCollapse = collapsed => {
        console.log(collapsed);
        this.setState({ collapsed });
    };

    render() {
        return (
        
            
Option 1 Option 2 User } > Tom Bill Alex Team } > Team 1 Team 2 File User Bill
Bill is a cat.
); } } + export default hot(LayoutContainer)
  • 继续运行项目,如图,已经可以实时更新了,并将修改的文件路径也给打印出来了;


可以看到控制台出现了警告,说是 react-dom pacth 没有检测到,可能会影响我们使用 react 16.6 以上版本的特性,比如 hook,查看 react-hot-loader 配置,安装 @hot-loader/react-dom 这个插件,将其指到 react-dom 即可;在 webpack.common.js 中作如下配置:

//webpack.common.js
...
resolve: {
        extensions: ['.js', '.css', '.less'],
        alias: {
            'react-dom': '@hot-loader/react-dom'
        }
    },
...

最后运行项目,这个警告就没有了;


最后我们来验证一下用 hook 特性来写 Header.js,看一下 hot 是不是起作用;修改 Header.js 为一个函数组件,然后使用 hook,来使用 react class 的 state 特性;

//src/components/Header/Header.js
import React, { useState } from 'react'
import { Layout, Button } from 'antd'
const { Header } = Layout

import './header.less'

export default () => {
    const [ username, setUsername ] = useState('john');
    return (
        
我的名字叫 {username}
) }

我们在 Header.js 中引入 useState hook,新建一个变量和设置变量的函数,初始值是 john,点击按钮修改为 lyli,可以看到页面直接更新了,并没有重新刷新;

最终我们实现了 react 项目的热更新,项目地址在这里,大家可以下载尝试一下:https://github.com/mxcz213/webpack4-demo

参考:
https://webpack.js.org/guides/hot-module-replacement/
https://www.npmjs.com/package/react-hot-loader

你可能感兴趣的:(webpack 4 搭建 React 架构:实现热更新(二))