无法使Webpack 2 HMR React正常工作 [英] Can't get Webpack 2 HMR React to work

查看:94
本文介绍了无法使Webpack 2 HMR React正常工作的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

当前,我正在努力使HMR在Webpack 2设置中正常工作.我将解释我的整个设置,所以我希望这足以使某人了解正在发生的事情.

Currently I'm struggling to get HMR working in my Webpack 2 setup. I'll explain my entire setup so I hope this is enough for someone to understand what's happening.

我的项目的结构:

config
  dev.js
  prod.js 
dist
  css
  js
  index.html
node_modules
src
  components
    // some JavaScript components
  shared
  stylesheets
  index.js
.babelrc
package.json
webpack.config.js

这是我的webpack.config.js文件的内容,放在我项目的根目录中:

This are the contents of my webpack.config.js file, placed in the root of my project:

function buildConfig(env) {
  return require('./config/' + env + '.js')(env)
}

module.exports = buildConfig;

因此在此文件中,我可以选择将不同的环境传递给buildConfig函数.我使用此选项将不同的配置文件用于开发和生产.这是我的package.json文件中的内容:

So in this file I've the option to pass different environments to the buildConfig function. I use this options to use different config files for development and production. This are the contents in my package.json file:

{
  "main": "index.js",
  "scripts": {
    "build:dev": "node_modules/.bin/webpack-dev-server --env=dev",
    "build:prod": "node_modules/.bin/webpack -p --env=prod"
  },
  },
  "devDependencies": {
    "autoprefixer-loader": "^3.2.0",
    "babel-cli": "^6.18.0",
    "babel-core": "^6.24.1",
    "babel-loader": "^6.2.5",
    "babel-preset-latest": "^6.16.0",
    "babel-preset-react": "^6.16.0",
    "babel-preset-stage-0": "^6.16.0",
    "css-loader": "^0.25.0",
    "extract-text-webpack-plugin": "^2.1.0",
    "json-loader": "^0.5.4",
    "node-sass": "^3.13.1",
    "postcss-loader": "^1.3.3",
    "postcss-scss": "^0.4.1",
    "sass-loader": "^4.1.1",
    "style-loader": "^0.13.1",
    "webpack": "^2.4.1",
    "webpack-dev-server": "^2.4.2"
  },
  "dependencies": {
    "babel-plugin-react-css-modules": "^2.6.0",
    "react": "^15.3.2",
    "react-dom": "^15.3.2",
    "react-hot-loader": "^3.0.0-beta.6",
    "react-icons": "^2.2.1"
  }
}

我的package.json中当然有更多字段,但是由于它们无关紧要,所以在此不显示它们.

I've of course more fields in my package.json but I won't shown them here since they're irrelevant.

因此,在开发过程中,我在终端中运行npm run build:dev命令.这将使用config文件夹中的文件dev.js.这是dev.js文件的内容:

So during development I run the npm run build:dev command in my terminal. This will use the file dev.js from the config folder. This are the contents of the dev.js file:

const webpack = require('webpack');
const { resolve } = require('path');
const context = resolve(__dirname, './../src');

module.exports = function(env) {
  return {
    context,
    entry: {
      app: [
        'react-hot-loader/patch',
        // activate HMR for React
        'webpack-dev-server/client?http://localhost:3000',
        // bundle the client for webpack-dev-server
        // and connect to the provided endpoint
        'webpack/hot/only-dev-server',
        // bundle the client for hot reloading
        // only- means to only hot reload for successful updates
        './index.js'
        // the entry point of our app
      ]
    },
    output: {
      path: resolve(__dirname, './../dist'), // `dist` is the destination
      filename: '[name].js',
      publicPath: '/js'
    },
    devServer: {
      hot: true, // enable HMR on the server
      inline: true,
      contentBase: resolve(__dirname, './../dist'), // `__dirname` is root of the project
      publicPath: '/js',
      port: 3000
    },
    devtool: 'inline-source-map',
    module: {
      rules: [
        {
          test: /\.js$/, // Check for all js files
          exclude: /node_modules/,
          use: [{
            loader: 'babel-loader',
            query: {
              presets: ['latest', 'react'],
              plugins: [
                [
                  "react-css-modules",
                  {
                    context: __dirname + '/../src', // `__dirname` is root of project and `src` is source
                    "generateScopedName": "[name]__[local]___[hash:base64]",
                    "filetypes": {
                      ".scss": "postcss-scss"
                    }
                  }
                ]
              ]
            }
          }]
        },
        {
          test: /\.scss$/,
          use: [
            'style-loader',
            {
              loader: 'css-loader',
              options: {
                sourceMap: true,
                modules: true,
                importLoaders: 2,
                localIdentName: '[name]__[local]___[hash:base64]'
              }
            },
            'sass-loader',
            {
              loader: 'postcss-loader',
              options: {
                plugins: () => {
                  return [
                    require('autoprefixer')
                  ];
                }
              }
            }
          ]
        }
      ]
    },
    plugins: [
      new webpack.HotModuleReplacementPlugin(),
      // enable HMR globally
      new webpack.NamedModulesPlugin()
      // prints more readable module names in the browser console on HMR updates
    ]
  }
};

最后但并非最不重要的是,我的HMR设置.我在index.js文件中有了此设置:

And last but not least, my HMR setup. I've this setup in my index.js file:

import React from 'react';
import ReactDOM from 'react-dom';
import { AppContainer } from 'react-hot-loader';
import TodoApp from './components/TodoApp';
import './stylesheets/Stylesheets.scss';

const render = (Component) => {
  ReactDOM.render(
      <AppContainer>
        <Component />
      </AppContainer>,
      document.querySelector('#main')
  );
};

render(TodoApp);

// Hot Module Replacement API
if (module.hot) {
  module.hot.accept('./components/TodoApp', () => {
    render(TodoApp)
  });
}

因此,当我在浏览器中运行npm start build:dev并转到http://localhost:3000时,我看到我的网站按预期运行.这是控制台中的输出:

So, when I run my npm start build:dev in my browser and go to http://localhost:3000 I see my site working as expected. This is the output in the console:

dev-server.js:49 [HMR] Waiting for update signal from WDS...
only-dev-server.js:66 [HMR] Waiting for update signal from WDS...
TodoApp.js:102 test
client?344c:41 [WDS] Hot Module Replacement enabled.

test文本来自我的TodoApp组件中的render函数.该函数如下所示:

The test text comes from the render function in my TodoApp component. This function looks like this:

render() {
  console.log('test');
  return(
      <div styleName="TodoApp">
        <TodoForm addTodo={this.addTodo} />
        <TodoList todos={this.state.todos} deleteTodo={this.deleteTodo} toggleDone={this.toggleDone} updateTodo={this.updateTodo} />
      </div>
  );
}

所以,现在重要的东西.我更新了此渲染函数的返回值,这将触发HMR进入.我将渲染函数更改为此.

So, now the important stuff. I update the return of this render function, which should trigger the HMR to kick in. I change the render function to this.

render() {
  console.log('test');
  return(
      <div styleName="TodoApp">
        <p>Hi Stackoverflow</p>
        <TodoForm addTodo={this.addTodo} />
        <TodoList todos={this.state.todos} deleteTodo={this.deleteTodo} toggleDone={this.toggleDone} updateTodo={this.updateTodo} />
      </div>
  );
}

这是我在控制台中得到的输出:

This is the output I get in the console:

client?344c:41 [WDS] App updated. Recompiling...
client?344c:41 [WDS] App hot update...
dev-server.js:45 [HMR] Checking for updates on the server...
TodoApp.js:102 test
log-apply-result.js:20 [HMR] Updated modules:
log-apply-result.js:22 [HMR]  - ./components/TodoApp.js
dev-server.js:27 [HMR] App is up to date.

您会说这很好. 但是我的网站没有任何更新.

然后将index.js中的HMR代码更改为此:

Then I change the the HMR code in my index.js to this:

// Hot Module Replacement API
if (module.hot) {
  module.hot.accept();
}

它有效.我就是不明白.如果这是我的HMR代码,为什么它不起作用:

And it works. I just don't get it. Why doesn't it work if this is my HMR code:

// Hot Module Replacement API
if (module.hot) {
  module.hot.accept('./components/TodoApp', () => {
    render(TodoApp)
  });
}

顺便说一句,此设置基于 https://webpack.js.org的设置/guides/hmr-react/

BTW this setup is based on the setup from https://webpack.js.org/guides/hmr-react/

我希望任何人都能帮助我.如果有人需要更多信息,请随时询问.预先感谢!

I hope that anyone can help me. If someone needs more information don't hesitate to ask. Thanks in advance!

更新

忘记发布我的.babelrc文件.就是这样:

Forgot to post my .babelrc file. This is it:

{
  "presets": [
    ["es2015", {"modules": false}],
    // webpack understands the native import syntax, and uses it for tree shaking

    "react"
    // Transpile React components to JavaScript
  ],
  "plugins": [
    "react-hot-loader/babel"
    // EnablesReact code to work with HMR.
  ]
}

推荐答案

导入是静态的,并且在module.hot.accept中识别出更新后,您将再次渲染完全相同的组件,因为TodoApp仍保留旧版本HMR意识到这一点,并且不会刷新或更改应用程序中的任何内容.

The imports are static and after an update has been identified in module.hot.accept you render the exact same component again, as the TodoApp still holds the old version of your module and HMR realises that and doesn't refresh or change anything in your app.

您要使用动态导入:import() .要使其与babel一起使用,您需要添加babel-plugin-syntax-dynamic-import,否则它将报告语法错误,因为它不希望将import用作函数.如果在Webpack配置中使用react-hot-loader/patch,则不需要react-hot-loader/babel,因此.babelrc中的插件将变为:

You want to use Dynamic import: import(). To make it work with babel you need to add babel-plugin-syntax-dynamic-import, otherwise it will report a syntax error as it didn't expect import to be used as a function. The react-hot-loader/babel is not needed if you use react-hot-loader/patch in your webpack config, so your plugins in your .babelrc become:

"plugins": [
  "syntax-dynamic-import"
]

render()函数中,您现在可以导入TodoApp并进行渲染.

In your render() function you can now import the TodoApp and render it.

const render = () => {
  import('./components/TodoApp').then(({ default: Component }) => {
    ReactDOM.render(
      <AppContainer>
        <Component />
      </AppContainer>,
      document.querySelector('#main')
    );
  });
};

render();

// Hot Module Replacement API
if (module.hot) {
  module.hot.accept('./components/TodoApp', render);
}

import()是一个将与模块一起解决的承诺,并且您想使用default导出.

import() is a promise that will resolve with the module, and you want to use the default export.

即使以上情况是正确的,但webpack文档并不需要您使用动态导入,因为webpack可以开箱即用地处理ES模块,这也在进行了此操作,但是在Webpack配置中还配置了latest预设,这也可以转换模块.为避免混淆,您应该将所有babel配置都放在.babelrc中,而不是将其中的一些内容拆分到加载程序选项中.

Even though the above is true, the webpack documentation doesn't require you to use dynamic imports, because webpack handles ES modules out of the box, also described in react-hot-loader docs - Webpack 2, and because webpack is also handling the HMR, it will know what to do in that case. For this to work, you must not transform the modules to commonjs. You did this with ["es2015", {"modules": false}], but you also have the latest preset configured in your webpack config, which also transforms the modules. To avoid confusion, you should have all babel configurations in .babelrc instead of splitting some to the loader options.

完全从Webpack配置中删除babel-loader中的预设,它将起作用,因为.babelrc中已经有必要的预设. babel-preset-latest 已过时,如果要使用这些功能,应开始使用<一个href ="http://babeljs.io/docs/plugins/preset-env/" rel ="noreferrer"> babel-preset-env ,它也替换了es2015.因此,您在.babelrc中的预设为:

Remove the presets in the babel-loader entirely from your webpack config and it will work as you already have the necessary presets in your .babelrc. babel-preset-latest is deprecated and if you want to use these features you should start using babel-preset-env which also replaces es2015. So your presets in .babelrc would be:

"presets": [
  ["env", {"modules": false}],
  "react"
],

这篇关于无法使Webpack 2 HMR React正常工作的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆