目前,我正在努力使HMR在Webpack 2设置中正常工作。我将解释我的整个设置,所以我希望这足以使某人了解正在发生的事情。
我的项目结构:
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文件的内容,位于项目的根目录中:
webpack.config.js
function buildConfig(env) { return require('./config/' + env + '.js')(env) } module.exports = buildConfig;
因此,在此文件中,我可以选择将不同的环境传递给该buildConfig函数。我使用此选项将不同的配置文件用于开发和生产。这是我package.json文件中的内容:
buildConfig
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" } }
我当然有更多字段,package.json但由于它们无关紧要,因此在此不再显示。
因此,在开发过程中,我npm run build:dev在终端中运行命令。这将使用文件dev.js从config文件夹中。这是dev.js文件的内容:
npm run build:dev
dev.js
config
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文件中有此设置:
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我看到我的网站按预期工作。这是控制台中的输出:
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组件。该函数如下所示:
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(); }
而且有效。我就是不明白。如果这是我的HMR代码,为什么它不起作用:
// Hot Module Replacement API if (module.hot) { module.hot.accept('./components/TodoApp', () => { render(TodoApp) }); }
顺便说一句,此设置基于https://webpack.js.org/guides/hmr- react/中的设置
我希望任何人都能帮助我。如果有人需要更多信息,请随时询问。提前致谢!
更新
忘记发布我的.babelrc文件。就是这个:
.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. ] }
导入是静态的,在module.hot.accept您确定更新后,您将再次呈现完全相同的组件,因为TodoApp仍然保留了模块的旧版本,并且HMR意识到这一点,并且不会刷新或更改应用程序中的任何内容。
module.hot.accept
您要使用 动态导入:import()。要使其与babel一起使用,您需要添加babel-plugin-syntax- dynamic-import,否则它将报告语法错误,因为它没有期望import用作函数。在react-hot- loader/babel不需要,如果你使用react-hot-loader/patch你的WebPack配置,让你在你的插件.babelrc变成:
import()
babel-plugin-syntax- dynamic-import
import
react-hot- loader/babel
react-hot-loader/patch
"plugins": [ "syntax-dynamic-import" ]
render()现在,您可以在函数中导入TodoApp和呈现它。
render()
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导出。
default
即使以上情况是正确的,但webpack文档并不需要您使用动态导入,因为webpack开箱即用地处理ES模块(也在react-hot- loaderdocs-Webpack 2中进行了描述),并且因为webpack也正在处理HMR,它将知道在那种情况下该怎么办。为此,您不得将模块转换为commonjs。您使用进行了此操作["es2015", {"modules": false}],但是您还在latestwebpack配置中配置了预设,这也可以转换模块。为避免混淆,应将所有babel配置都放入其中,.babelrc而不是将其中一些拆分为加载程序选项。
react-hot- loader
["es2015", {"modules": false}]
latest
babel-loader从Webpack配置中完全删除这些预设,它将在您已经包含必要的预设的情况下起作用.babelrc。babel- preset-latest已弃用,如果要使用这些功能,则应开始使用babel-preset- env,这些功能也将取代es2015。因此,您的预设.babelrc为:
babel-loader
babel- preset-latest
babel-preset- env
es2015
"presets": [ ["env", {"modules": false}], "react" ],