模块热替换(HMR)
概念
- 在应用程序运行过程中替换、添加或删除模块,而无需重新加载整个页面
- 也是用在开发环境中加快效率得一种方式
- 这个是除了code spliting之外的webpack的另外一个非常重要的功能
模块的热加载和热替换有什么区别呢?
- 热加载:当修改某一个模块的时候
- 会触发资源的重新挂载(也就相当于刷新页面)
- 页面在store中存储的数据以及state数据都会丢失
- 热替换:当修改某一个模块的时候,
- 只会触发被修改的模块的重新挂载而不会引发其他模块的重新挂载
- 不会影响的任何数据
那么如何实现模块的热替换呢?
-
热替换(HMR):非常类似于React的diff算法,因此搭配react使用很合适
- 使用React-Hot-Loader
- webpack-dev-server
- 其涉及的代码很多
修改webpack的配置
entry: {
main: [
'react-hot-loader/patch' //开启react模块的热替换功能
]
},
plugins: [
new webpack.NamedModulesPlugin(),
//作用在控制台中显示出项目中那一个模块被修改了
// [HMR] Updated modules:
//log-apply-result.js:22 [HMR] - //./app/src/containers/User/LoginContainer/index.js
//log-apply-result.js:22 [HMR] - //./app/src/routes.js
//log-apply-result.js:22 [HMR] - //./app/src/Root.js
//dev-server.js:27 [HMR] App is up to date.
new webpack.HotModuleReplacementPlugin()
// 给每个react组件的模块加上hot的相关信息 开启热替换
],
devServer: {
// ... 其他配置
hot: true,
// 开启服务器的模块热替换(HMR)
}
- 修改.babelrc
{
"presets": [
"es2015",
"react",
"stage-0"
],
"plugins": [
"lodash",
["import", {"libraryName": "antd"}]
],
"env": {
"development": {
"plugins": [
"react-hot-loader/babel", //开启模块热替换
]
},
}
//.babelrc中由于热替换仅仅用于开发环境因此只有开发环境包含
//疑惑的是,网上说需要将es2015De module设置成false,但是我没有设置也成功了很奇怪
}
- 修改代码分成两个部分:
- 替换组件
//index.js(入口文件) import {AppContainer as ReactHotLoader} from 'react-hot-loader' import { getStoreInstance, history } from './store' import { render, unmountComponentAtNode } from 'react-dom' const MOUNT_NODE = document.getElementById('app') const renderApp = () => { const Root = require('./Root').default render( <ReactHotLoader> <Root store={getStoreInstance()} history={history}/> </ReactHotLoader>, MOUNT_NODE ) if(process.env.NODE_ENV === 'production' && module.hot){ //使用module.hot来判断有没有给每个module都加上了热替换的功能 module.hot.accept('./Root',()=>{ //检测Root组件和其子组件,只要有module的变化就会调用这个回调函数,进行组件的替换 unmountComponentAtNode(MOUNT_NODE) //将MOUNT_NODE中的所有React组件移除 renderApp() //将原来的组件移除,替换上新的组件 }) }
}
- 替换store
```js
let storeInstance = null
export const getStoreInstance = function (initialState) {
return storeInstance || configureStore(initialState)
}
function configureStore(initialState) {
storeInstance = createStore(
rootReducer,
initialState,
composedEnhancers,
)
if (module.hot) {
module.hot.accept('./reducers', () => {
const nextReducer = require('./reducers')
storeInstance.replaceReducer(nextReducer)
})
}
return storeInstance
}