react HRM 热更替, 看这篇就够了...

2,730 阅读1分钟

step1: 开启webpack4.0+ 热更替

  1. 安装npm webpack@4.39.1 webpack-cli@3.3.6 babel-loader @babel/core@7.5.5 @babel/preset-env@7.5.5 webpack-dev-server@3.8.0 html-webpack-plugin -D

说明: @babel/core@7.5.5 @babel/preset-env@7.5.5, 解析js文件, babel/preset-env高级语法转化成低级语法;html-webpack-plugin 解析模板html文件;

  1. 安装react react-dom @babel/preset-react -D

说明: 解析react 语法

  1. 配置webpack.config.js

说明:最重要的是devServer: hot hotOnly(浏览器不自动刷新) 以及plugins中的 webpack插件NamedModulesPlugin、HotModuleReplacementPlugin;

const path = require("path");
const HtmlWebpackPlugin = require("html-webpack-plugin");
const webpack = require("webpack");
module.exports = {
  devServer: {
    port: 3000,
    open: true, // 自动打开浏览器,
    contentBase: path.resolve(__dirname, 'build'),
    hot: true, // 开启热更替
    hotOnly: true , // 即使HMR没有生效 浏览器也不会自动更新 必须设置
  },
  entry: './src/index.js',
  mode: 'development',
  output: {
    filename: 'build.js',
    path: path.resolve(__dirname, 'build')
  },
  devtool: 'eval-source-map',
  module: {
    rules: [{
      test: /\.js$/,
      exclude: /node_modules/,
      include: path.resolve(__dirname, 'src'),
      use: [{
        loader: 'babel-loader',
        options: {
          presets: [
            '@babel/preset-env',
            '@babel/preset-react'
          ]
        }
      }]
    }]
  },
  plugins: [
    new HtmlWebpackPlugin({
      template: './src/index.html',
      filename: 'index.html'
    }),
    new webpack.NamedModulesPlugin(), // 打印出那些变化文件的路径
    new webpack.HotModuleReplacementPlugin(), // 热更替组件
  ]
}

step2: 使用HMR 替换根组件

  1. 使用HMR前, 代码like this:
  // root.js
  import React from 'react';

  export default class Root extends React.Component{
    render(){
      return <div>Test</div>
    }
  }
  import React from 'react';
  import {render} from 'react-dom';

  import RootContainer from './root.js';
  render(<RootContainer></RootContainer>, document.getElementById('root'));
  1. 加入热更替, 代码like this
import React from 'react';
import ReactDOM from 'react-dom';

import RootContainer from './root.js';

const render = (App) => {
  ReactDOM.render(<App />, document.getElementById('root'));
} 

render(RootContainer)

if(module.hot){
  module.hot.accept('./root.js', ()=>{
    console.log('root 更新了...'); // 浏览器中显示
    const NextRootContainer = require('./root.js').default;
    render(NextRootContainer)
  });
}

说明: 红框内console的日志及webpack.NamedModulesPlugin() 打印出来的更新文 件的路径; 示图如下:

hot_module

  1. 创建component Demo, 并在root.js 引入
  // demo.js
  import React from 'react';
  export default class Demo extends React.Component{
    render(){
      return <div>Demo</div>
    }
  }
  // root.js
  import React from 'react';
  import Demo from './demo.js';

  export default class Root extends React.Component{
    render(){
      return <div>
        <div>Test</div>
        <Demo></Demo>
      </div>
    }
  }

运行结果:

result

当更新root.js Test to Test1, 结果显示 root.js 改变了, Demo组件更新路径没有 显示, 如图:

changeRootTest

改变Demo.js Demo to Demo1, 显示了root demo 更新了,因为demo 是root组件, root 的路径也显示出来了,如图:

changeDemo

note: 这似乎满足了我们要求, 需要在验证一点 state 能否保持不变, 在root 新增state, 代码地址, 点击Test text 变成 Test1, 相同操作 Demo to Demo1, 当我们把aa变成了aaa, Test1变成Test Demo1变成Demo;state没有保存;结果如下:

dlbn0-x45zc

注意: 没有进一步的步骤,这已经足够热重载更改来响应组件,但是它们的内部组件状态将不会被保留,因为组件的新副本已被挂载,并且其状态将被重新初始化。 保存在状态存储(例如Redux)外部的状态显然不会丢失。

step3: 新增React hot Loader 保存组件状态

  1. 安装react-hot-loader npm i react-hot-loader -D

  2. 修改webpack.config.js 相关配置

  // old
  {
    test: /\.js$/,
    exclude: /node_modules/,
    include: path.resolve(__dirname, 'src'),
    use: [{
      loader: 'babel-loader',
      options: {
        presets: [
          '@babel/preset-env',
          '@babel/preset-react'
        ]
      }
    }]
  }
// new 
{
  test: /\.js$/,
  exclude: /node_modules/,
  include: path.resolve(__dirname, 'src'),
  use: [{
    loader: 'react-hot-loader/webpack'
  },{
    loader: 'babel-loader',
    options: {
      presets: [
        '@babel/preset-env',
        '@babel/preset-react'
      ],
      "plugins": [ "react-hot-loader/babel" ]
    }
  }]
}
  1. 更新entry, 并修改index.js
entry: './src/index.js',

to

entry: ['react-hot-loader/patch','./src/index.js']
  // index.js
import React from 'react';
import ReactDOM from 'react-dom';
import { AppContainer } from 'react-hot-loader';
import RootContainer from './root.js';

const render = (App) => {
  ReactDOM.render(
    <AppContainer>
      <App />
    </AppContainer>,
  document.getElementById('root'));
} 

render(RootContainer)

if(module.hot){
  module.hot.accept('./root.js', ()=>{
    console.log('root 更新了...'); // 浏览器中显示
    const NextRootContainer = require('./root.js').default;
    render(NextRootContainer)
  });
}

结果显示 Test1 Demo1没有变化, 热更替success

lkarf-p07uv