3

目前我正努力让HMR在我的Webpack 2设置中工作。我会解释我的整个设置,所以我希望这足以让某人了解正在发生的事情。无法使Webpack 2 HMR React工作

我的项目结构:

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文件的内容,摆在我的项目的根目录:

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

module.exports = buildConfig; 
在这个文件

所以我的选择将不同的环境传递给buildConfig函数。我使用这个选项来使用不同的配置文件进行开发和生产。这是我package.json文件中的内容:在我的package.json

{ 
    "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" 
    } 
} 

我有,当然更多的领域,但我会因为他们是无关的不是他们在这里显示。

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

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文件:

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我看到我的网站工作正常。这是在控制台输出:

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() { 
    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启动。我将渲染函数更改为此。

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> 
); 
} 

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

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. 

你会说这是很好的。 但我的网站不更新任何东西。

然后,我改变了HMR代码在我index.js这样:

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

和它的作品。我只是不明白。

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

BTW这个设置是根据设置从https://webpack.js.org/guides/hmr-react/

我希望有人能帮助我:为什么,如果这是我的HMR代码不发挥作用。如果有人需要更多信息,请不要犹豫。提前致谢!

UPDATE

忘记而我.babelrc文件。这是它:

{ 
    "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. 
    ] 
} 
+0

你有没有在你的'.babelrc'文件中禁用ES2015模块? https://webpack.js.org/guides/hmr-react/#babel-config –

+0

O耶对不起,我忘了发布我的'.babelrc'文件的内容。现在就做这个。 – DavidWorldpeace

回答

5

进口是静态的,更新已module.hot.accept被确定之后,你再次渲染完全相同的部件,如TodoApp仍持有旧版本的模块和HMR意识到并没有按” t刷新或更改您应用中的任何内容。

您想使用Dynamic import: import()。为了使它与babel一起工作,您需要添加babel-plugin-syntax-dynamic-import,否则它会报告语法错误,因为它不会将import用作函数。该react-hot-loader/babel不需要,如果你在你的WebPack配置使用react-hot-loader/patch,所以你在你的.babelrc插件成为:

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

在你render()功能,你现在可以导入TodoApp和渲染。

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出口。


即使上述为真,那么的WebPack文档不要求你使用动态的进口,因为处理的WebPack ES模块开箱,在react-hot-loader docs - Webpack 2还描述了,因为的WebPack也处理HMR它会知道该怎么做。为了这个工作,你不能将模块转换为commonjs。您使用["es2015", {"modules": false}]来完成此操作,但是您的webpack配置中还配置了latest预设,这也会转换模块。为避免混淆,您应该在.babelrc中拥有所有babel配置,而不是将某些配置拆分为加载器选项。

完全从您的webpack配置中删除babel-loader中的预设,它将工作,因为您已在.babelrc中拥有必要的预设。 babel-preset-latest已被弃用,如果您想使用这些功能,您应该开始使用babel-preset-env,这也取代了es2015。所以,你在.babelrc预设是:

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

谢谢,这确实是问题!我的'.babelrc'文件中的设置被'webpack.config'文件覆盖。 – DavidWorldpeace

+0

通过webpack.config使用.babelrc的任何理由?为什么不使用webpack.config? –

+1

@JohnLeidegren'.babelrc'适用于使用Babel的所有内容。如果您在webpack配置中配置它,它将只适用于webpack。 webpack之外的Babel有很多用途。一个例子是测试,因为问题[意外的令牌 - 对现有的React + Web应用程序的赞赏](https://stackoverflow.com/questions/45372145/unexpected-token-jest-for-existing-react-web-包 - 应用程序)显示。 –

1

入住这issue在GitHub上或使用此在您的index.js:

import React from 'react' 
import ReactDOM from 'react-dom' 
import { AppContainer } from 'react-hot-loader' 

import App from './components/App' 

const render = Component => { 
    ReactDOM.render(
     <AppContainer> 
      <Component/> 
     </AppContainer>, 
     document.getElementById('react-root') 
    ) 
} 

render(App) 

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