Webpack 热模块替换,在之前已传递给 createRoot() 的容器上反应 18 ReactDOMClient.createRoot()

ptm*_*mva 12 javascript reactjs webpack

我将 React 更新到 v18,每当热模块替换触发并注入新的 javascript 代码时,我的 Webpack 开发服务器都会出现控制台错误:

Warning: You are calling ReactDOMClient.createRoot() on a container that has already been passed to createRoot() before. Instead, call root. render() on the existing root instead if you want to update it.

index.js文件

import React from 'react';
import ReactDOM from 'react-dom/client';
import App from './App.jsx';

const container = document.getElementById('root');
const root = ReactDOM.createRoot(container);

root.render(<App />);

if (module.hot) module.hot.accept(function (err) {
    console.log('An error occurred while accepting new version');
});
Run Code Online (Sandbox Code Playgroud)

webpack.config.js

const path = require('path');
const HtmlWEbpackPlugin = require('html-webpack-plugin');

module.exports = (env) => {
  let cfg = {
    mode: 'development',
    entry: './src/index.js',
    output: {
      path: path.resolve(__dirname, 'dist'),
      filename: 'bundle.[contenthash:6].js',
      publicPath: '/',
      clean: true
    },
    module: {
      rules: [
        {
          test: /\.(js|jsx)$/,
          exclude: /node_modules/,
          use: {
            loader: 'babel-loader'
          }
        },
      ]
    },
    plugins: [new HtmlWEbpackPlugin({ template: './src/index.html' })
    ],
    devServer: {
      static: {
        directory: path.join(__dirname, 'public'),
      },
      compress: true,
      port: 3000,
      hot: true,
      open: true,
      
    },
    performance: {
      hints: false
    }

  }

  return cfg;
};
Run Code Online (Sandbox Code Playgroud)

sxk*_*xkx 19

React 18 原生支持热重载,这称为快速刷新。反应刷新自述文件的摘录:

快速刷新是一项功能,可让您在运行的应用程序中编辑 React 组件而不会丢失其状态。它类似于称为“热重载”的旧功能,但快速刷新更可靠并且受到 React 官方支持。

要在 Webpack 5 中使用它,您将需要一个名为react-refresh-webpack-plugin 的插件。为了让它工作,我建议查看 git 存储库中包含的示例,尤其是webpack-dev-server示例。

注意:在撰写此答案时,它react-refresh-webpack-plugin处于实验状态,但create-react-app已经在使用它,因此它可能足够稳定,可以使用。

以下内容直接取自react-refresh-webpack-plugin的示例webpack-dev-server

src/index.js

import { createRoot } from 'react-dom/client';
import App from './App';

const container = document.getElementById('app');
const root = createRoot(container);
root.render(<App />);
Run Code Online (Sandbox Code Playgroud)

webpack.config.js

const path = require('path');
const ReactRefreshPlugin = require('@pmmmwh/react-refresh-webpack-plugin');
const HtmlWebpackPlugin = require('html-webpack-plugin');

const isDevelopment = process.env.NODE_ENV !== 'production';

module.exports = {
  mode: isDevelopment ? 'development' : 'production',
  devServer: {
    client: { overlay: false },
  },
  entry: {
    main: './src/index.js',
  },
  module: {
    rules: [
      {
        test: /\.jsx?$/,
        include: path.join(__dirname, 'src'),
        use: 'babel-loader',
      },
    ],
  },
  plugins: [
    isDevelopment && new ReactRefreshPlugin(),
    new HtmlWebpackPlugin({
      filename: './index.html',
      template: './public/index.html',
    }),
  ].filter(Boolean),
  resolve: {
    extensions: ['.js', '.jsx'],
  },
};
Run Code Online (Sandbox Code Playgroud)

babel.config.js

module.exports = (api) => {
  // This caches the Babel config
  api.cache.using(() => process.env.NODE_ENV);
  return {
    presets: [
      '@babel/preset-env',
      // Enable development transform of React with new automatic runtime
      ['@babel/preset-react', { development: !api.env('production'), runtime: 'automatic' }],
    ],
    // Applies the react-refresh Babel plugin on non-production modes only
    ...(!api.env('production') && { plugins: ['react-refresh/babel'] }),
  };
};
Run Code Online (Sandbox Code Playgroud)

您可以从 Webpack 入口点删除以下内容:

src/index.js

// ...
if (module.hot) {
  module.hot.accept()
}
Run Code Online (Sandbox Code Playgroud)

这有一个小缺点,即每当您修改入口点 ( src/index.js) 时,都需要完全重新加载。Webpack 非常明确地告诉您需要完全重新加载,并向您显示以下日志消息。

[图像]

这真的让我很恼火。在查看create-react-app如何解决此问题时,我发现他们禁用了 的客户端日志记录webpack-dev-server,或者至少将日志级别设置为warnerrorclient.logging您可以通过在配置中设置属性来设置日志级别devServer

webpack.config.js

// ...
devServer: {
  client: {
    overlay: false,
    logging: 'warn' // Want to set this to 'warn' or 'error'
  }
}
// ...
Run Code Online (Sandbox Code Playgroud)

关于“警告”的奇怪之处在于,它根本不是警告,它只是一条伪装成警告的信息消息。

希望这可以帮助。