webpack4中的react和redux的热更新

在之前一篇webpack4的热更新的最后留下了一点思考,传统的前端开发方式套上webpack去实现热更新是很麻烦的,因为html和JavaScript是分离的,创建和销毁JavaScript无法完全控制。在现在前端的开发方式中,html的所有元素都是由JavaScript控制的,那么是否可以很容易的实现热更新呢??


2018-12-20更新

react-hot-loader最新更新到了4.6.x,提供了一种新的API,官方介绍说可以提供更好的错误管理,

1
2
3
4
// App.js
import { hot } from 'react-hot-loader/root'
const App = () => <div>Hello World!</div>
export default hot(App)

需要特别注意的是,因为这种方式需要使用Object.assign,如果需要兼容IE11以下的浏览器,需要提供polyfill,如可以安装core-js添加import "core-js/fn/object/assign

可以对比下4.6.x之前的写法

1
2
3
import { hot } from 'react-hot-loader'
const App = () => <div>Hello World!</div>
export default hot(module)(App)


准备

先来准备两个文件,React入口文件index.js和组件App.js
index.js代码

1
2
3
4
5
6
7
8
9
10
import React from "react";
import ReactDOM from "react-dom";
import App from "./App";
import "./public/style.css";
const wrapper = document.getElementById("root");

ReactDOM.render(
<App />,
wrapper
);

App.js代码,App中的Button组件非关键代码就不贴了

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
import React from "react";
import { Button } from "./components/button";
import "./public/style.css";


let App = (props) => {
return (
<>
<Button
onClick={() => {
console.log(Button.c);

console.log("test arrow function");
}}
value="click me , don't answer you"
/>
<p>看我实现热更新</p>
</>
);
};

export default App;

添加热更新代码

首先按照webpack4的热更新中的步骤完成webpack.config.js的配置。
然后修改上一节中的入口文件为:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
import React from "react";
import ReactDOM from "react-dom";
import App from "./App";
import "./public/style.css";

const wrapper = document.getElementById("root");

ReactDOM.render(
<App />,
wrapper
);

// 增加热更新代码
if (module.hot) {
module.hot.accept("./App", () => {
const App = require("./App").default;

ReactDOM.render(
<App />,
wrapper
);
});
}

尝试更新App.js及其任意组件,体验下舒爽的热更新

使用react-hot-loader

额,为什么还有react-hot-loader这么个东西,热更新不都已经实现了么,是的,可是并不完美,打开浏览器的控制台Elements标签,然后去更新App.js里面的p标签,你会发现button也跟着更新了,我没有更新button啊。。。。

当然如果页面足够简单也就无所谓了,可是如果组件很多,嵌套很深就不美好了,这个时候就要react-hot-loader登场了,使用很简单,跟着走就行了。

  • 首先把index.jsApp.js还原为文章最开始的样子
  • 安装react-hot-loader@4.x

    1
    npm install react-hot-loader
  • 添加react-hot-loader/babel到你的.babelrc

    1
    2
    3
    4
    // .babelrc
    {
    "plugins": ["react-hot-loader/babel"]
    }
  • 修改App.js

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    import React from "react";
    // 引入loader
    import { hot } from "react-hot-loader";
    import { Button } from "./components/button";
    import "./public/style.css";


    let App = (props) => {
    return (
    <>
    <Button
    onClick={() => {
    console.log(Button.c);

    console.log("test arrow function");
    }}
    value="click me , don't answer you"
    />
    <p>看我实现热更新</p>
    </>
    );
    };
    // 增加热更新
    if (module.hot) {
    App = hot(module)(App);
    }
    export default App;

尝试更新App.js及其任意组件,体验下舒爽的热更新, 再也不用担心性能了

Redux热更新

通过store.replaceReducer可以实现对redux的热替,直接上代码

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
import { createStore } from "redux";
import reducer from "./reducer";

// const store = createStore(reducer);
export const configureStore = () => {
const store = createStore(reducer);

if (module.hot) {
// Enable Webpack hot module replacement for reducers
module.hot.accept("./reducer", () => {
const nextRootReducer = require("./reducer").default;
store.replaceReducer(nextRootReducer);
});
}

return store;
};

需要注意的是createStore不要放在App.js中,避免React组件的更新影响到store,放到index.js中就可以了

1
2
3
4
5
6
7
8
9
10
11
12
import React from "react";
import ReactDOM from "react-dom";
import App from "./App";
import { configureStore } from "./redux/store";

const store = configureStore();
const wrapper = document.getElementById("root");

ReactDOM.render(
<App store={store}/>,
wrapper
);

更新App.js

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
import React from "react";
// 引入loader
import { hot } from "react-hot-loader";
import { Button } from "./components/button";
import "./public/style.css";
// 引入Provider
import { Provider } from "react-redux";

let App = (props) => {
return (
<Provider store={props.store}>
<Button
onClick={() => {
console.log(Button.c);

console.log("test arrow function");
}}
value="click me , don't answer you"
/>
<p>看我实现热更新</p>
</Provider>
);
};
// 增加热更新
if (module.hot) {
App = hot(module)(App);
}
export default App;

修改 reducer 也可以实现热更新了

总结

终于可以愉快的玩耍了!!