前因
当前应用中有个模块实验着采用 react 来做,想看看这东西能否解决之前的一些痛点。此模块采用 webpack 打包,于是顺便加上了热加载功能。
由于 HTML 页面是现有系统生成的,所以需要在开发环境下集成热加载功能有些要点需要注意,仔细翻了官方文档之后搞定。
下面把一些要点记录下来。
更新说明
一开始采用的是 react-hot-loader,不过在配合 antd 的时候老是报错:
Uncaught TypeError: Cannot read property '__reactAutoBindMap' of null
后来按照 Antd issue 的提示,换成了 react-transform 后问题解决。
环境说明
- 现有系统服务器端口为 8080
- webpack 热加载服务端口为 3000
- 测试模块名为:testModule
- webpack 配置已经实现,但没有热加载相关配置(这些配置转移到了启动脚本中了,参见下面源码)
实现方式
webpack 热加载支持两种方式
一种是直接命令行来搞:
webpack-dev-server ...
不过这种方式比较死板,不推荐。
还是采用专门写个 js 来定义,然后采用 npm scripts 命令来执行:
npm run start:testModule
准备工作
此处假定已经配置好 webpack 打包环境,下面需要安装热加载相关组件:
npm install --save-dev webpack-dev-server
(此条已作废)npm install --save-dev react-hot-loader
npm install --save-dev babel-preset-react-hmre
babel 相关配置
"env": { "development": { "presets": [ "react-hmre" ] } }
热加载服务启动脚本:testModuleServer.js
/*eslint-disable no-console */ var WebpackDevServer = require('webpack-dev-server'); var webpack = require('webpack'); // 引入现有 webpack 设置 var config = require('../webpack.config.js'); // webpack 热加载服务的端口号 var port = 3000; // 在 webpack config 中将需要的模块的 entry 中增加下面两条设置 // 采用的 only-dev-server 而不是 dev-server 是为了在语法出错的时候不会重载浏览器页面 config.entry.testModule.unshift( `webpack-dev-server/client?http://0.0.0.0:${port}`,'webpack/hot/only-dev-server' ); // 我的 babel loader 位于第一个位置,所以这儿直接采用 [0] 来重新设置此 loader // 加上了 react-hot 来处理 babel 编译后的源码 // 由于改用 react-transform,所以下面设置也作废 // config.module.loaders[0].loader = 'react-hot!babel'; // publicPath 必须设置,这是在现有 HTML 页面中嵌入 script的 路径 // 如果不设置,热加载生成的一些内部脚本将会无处依存 config.output.publicPath = `http://localhost:${port}/assets/`; // 在 webpack 配置中增加热加载插件 // 这个必须有,否则即使下面的 hot: true 设置了也没用 // 但是也要注意一下源配置文件中是否已经设置过了,不要重复设置 // 我的建议是原配置中不要牵扯任何跟热加载有关的东西,保持纯净 config.plugins.push(new webpack.HotModuleReplacementPlugin()); var compiler = webpack(config); // 下面是具体的服务设置 var server = new WebpackDevServer(compiler,{ // 这个显然必须有 // 需要注意的是这儿的这个属性和命令行中的同名属性有所区别 // 这儿设置了并不会自动增加 HotModuleReplacementPlugin // 所以上面才需要设置一个 hot: true,// 注意:这个属性也必须设置,且与上面的 publicPath 中的相应位置一致 // 否则也不起作用 publicPath: "/assets/",stats: { colors: true } //historyApiFallback: true }); server.listen(port);
至此 webpack 部分设置完毕。
react 相关设置(已作废)
由于换用了 react-transform,所以 react 相关设置已作废
:bangbang: 如果你的 react 是直接采用 webpack 打包的话,下面设置可以忽略。
不过我为了节省编译时间,是把 react 和 react-dom 直接在 HTML 中引入的,所以这儿还需要做一些额外设置。
在模块入口文件(一般为 index.js 吧)中一般有:
ReactDOM.render( <Provider store={store}> <App /> </Provider>,document.getElementById('root') );
将此部分代码赋值给一个变量,然后增加热加载相关代码(参考):
var rootInstance = ReactDOM.render( <Provider store={store}> <App /> </Provider>,document.getElementById('root') ); if (process.env.NODE_ENV !== 'production') { if (module.hot) { require('react-hot-loader/Injection').RootInstanceProvider.injectProvider({ getRootInstances: function () { // Help React Hot Loader figure out the root component instances on the page: return [rootInstance]; } }); } }
npm 命令
在 package.json 的 scripts 部分增加一行:
"start:testModule": "NODE_ENV=development node ./server/testModuleServer.js"
最后执行:
npm run start:testModule
搞定。