多应用+插件架构,代码干净,二开方便,首家独创一键云编译技术,文档视频完善,免费商用码云13.8K 广告
## 模块热替换(Hot Module Replacement)—— 有点问题 到目前为止,当我们修改代码的时候,浏览器会自动刷新,不信你可以去试试。 不过我们可以学习一下把别的项目改成支持热更新模块。 可以参考一下[webpack模块热替换](https://doc.webpack-china.org/guides/hot-module-replacement)教程。 首先在`package.json`增加`--hot`。 ~~~ "dev": "webpack-dev-server --config webpack.dev.config.js --color --progress --hot" ~~~ `src/index.js` 增加`module.hot.accept()`,如下。当模块更新的时候,通知`index.js`。 ~~~ import React from 'react' import ReactDom from 'react-dom' import Router from './router/router' if (module.hot) { // 命令行 --hot module.hot.accept() } ReactDom.render(<Router />, document.getElementById('app')) ~~~ 现在我需要说明下我们命令行使用的`--hot`,可以通过配置`webpack.dev.config.js`来替换, 向文档上那样,修改下面三处。但我们还是用`--hot`吧。下面的方式我们知道一下就行,我们不用。同样的效果。 ~~~ const webpack = require('webpack') devServer: { hot: true } plugins:[ new webpack.HotModuleReplacementPlugin() ] ~~~ HRM配置其实有两种方式,一种命令行`CLI`方式,一种`Node.js API`方式。我们用到的就是命令行`CLI`方式,比较简单。 `Node.js API`方式,就是建一个`server.js`等等,网上大部分教程都是这种方式,这里不做讲解了。 但是上面的配置对react模块的支持并不是太好。 比如写个demo,当模块热替换的时候,state会重置,而我们则希望能保持原有的数据状态。 在原来的`Home.js`,增加一个数据状态`state` `src/pages/Home/Home.js` ~~~ import React, { Component } from 'react' export default class Home extends Component { // 新增 constructor(props) { super(props) // 一般在构造函数内初始化state this.state = { count: 0 } } // 新增(私有方法用_开头) _handleClick() { this.setState({ count: ++this.state.count }) } render() { return ( <h1> This is Home <p> 当前计数:{this.state.count} <button onClick={() => this._handleClick()} >自增+1</button> </p> </h1> ) } } ~~~ 这时候可以看到页面初始化count为0,点击自增+1会改变count,然后当修改代码的时候,webpack更新了页面的同时,也把count初始化为0了。 为了在react模块更新的同时,能保留state等页面中其他状态,我们需要引入[react-hot-loader](https://github.com/gaearon/react-hot-loader)。 Q: 请问`webpack-dev-server`与`react-hot-loader`两者的热替换有什么区别? A: 区别在于`webpack-dev-server`自己的`--hot`模式只能即时刷新页面,但状态保存不住。因为`React`有一些自己语法(JSX)是`HotModuleReplacementPlugin`搞不定的。 而`react-hot-loader`在`--hot`基础上做了额外的处理,来保证状态可以存下来。(参考[segmentfault](https://segmentfault.com/q/1010000005612845)) 下面我们来加入`react-hot-loader v3`, > 记录于2017-12-20 现在发现react-hot-loader v3.1.3以下报错无法使用,因为不支持按需加载那边的HOC。 > 有两种解决办法: > 1. 等v3.1.4发布后,就会解决这个问题。 > 2. 不要使用按需加载。 所以我们来安装下一个版本的依赖: `npm install react-hot-loader@next --save-dev` 根据[文档](https://gaearon.github.io/react-hot-loader/getstarted/),我们做如下几个修改: 1. `.babelrc`增加`react-hot-loader/babel` ~~~ /* .babelrc */ { "presets": [ "es2015", "react", "stage-0" ], // 新增 "plugins": [ "react-hot-loader/babel" ] } ~~~ 2. `webpack.dev.config.js` 入口增加`react-hot-loader/patch` ` webpack.dev.config.js` ~~~ /*入口*/ // entry: path.join(__dirname, 'src/index.js') /*从单入口变成多入口*/ entry: [ 'react-hot-loader/patch', path.join(__dirname, 'src/index.js') ] ~~~ 3. `src/index.js`修改如下 `src/index.js` ~~~ import React from 'react' import ReactDOM from 'react-dom' // 如果你不需要保存数据状态就不需要用react-hot-loader import { AppContainer } from 'react-hot-loader' import Router from './router/router' /*初始化*/ renderWithHotReload( < Router / > ) /*热更新*/ if (module.hot) { module.hot.accept('./router/router', () => { // const getRouter = require('./router/router').default renderWithHotReload( < Router / > ) // }) } function renderWithHotReload(RootElement) { ReactDOM.render( <Router />, document.getElementById('app') ) } ~~~ 参考[gaearon/react-hot-loader#243](https://github.com/gaearon/react-hot-loader/issues/243)