持续创作,加速成长!这是我参与「掘金日新计划 · 10 月更文挑战」的第10天,点击查看活动详情
前言
哈喽大家好,我是Lotzinfly,一位前端小猎人。欢迎大家再次来到前端丛林,在这里你将会遇到各种各样的前端猎物,我希望可以把这些前端猎物统统拿下,嚼碎了服用,并成为自己身上的骨肉。今天是我们冒险的第十天,今天继续给大家介绍一下React中的Immutable,让我们深入Immutable探寻其中的奥秘,介绍一下React中的性能优化。话不多说,开始我们的冒险之旅吧!
1.React性能优化
1.1 计数器
每次调用setState的时候组件都会刷新
import React, { Component } from 'react';
import ReactDOM from 'react-dom';
class Counter extends Component {
state = { counter: { number: 0 } }
handleClick = () => {
let amount = this.amount.value ? Number(this.amount.value) : 0;
this.state.counter.number = this.state.counter.number + amount;
this.setState(this.state);
}
shouldComponentUpdate(nextProps, nextState) {
return true;
}
render() {
console.log('render');
return (
<div>
<p>{this.state.number}</p>
<input ref={input => this.amount = input} />
<button onClick={this.handleClick}>+</button>
</div>
)
}
}
ReactDOM.render(
<Caculator />,
document.getElementById('root')
)
1.2 深度克隆+浅比较
- 可以通过浅比较判断是否需要刷新组件
- 浅比较要求每次修改的时候都通过深度克隆每次都产生一个新对象
import _ from 'lodash';
handleClick = () => {
let amount = this.amount.value ? Number(this.amount.value) : 0;
let state = _.cloneDeep(this.state);
state.counter.number = this.state.counter.number + amount;
this.setState(state);
}
shouldComponentUpdate(nextProps, nextState) {
for (const key in nextState) {
if (this.State[key] !== nextState[key]) {
return true;
}
}
return false;
}
1.3 深比较
- 可以通过深度比较的方式判断两个状态的值是否相等
- 这样做的话性能非常低
shouldComponentUpdate(nextProps, prevState) {
return !_.isEqual(prevState, this.state);
}
1.4 immutable
import React, { Component } from 'react';
import ReactDOM from 'react-dom';
import { is, Map } from 'immutable';
class Caculator extends Component {
state = {
counter: Map({ number: 0 })
}
handleClick = () => {
let amount = this.amount.value ? Number(this.amount.value) : 0;
let counter = this.state.counter.update('number', val => val + amount);
this.setState({ counter });
}
shouldComponentUpdate(nextProps = {}, nextState = {}) {
if (Object.keys(this.state).length !== Object.keys(nextState).length) {
return true;
}
for (const key in nextState) {
if (!is(this.state[key], nextState[key])) {
return true;
}
}
return false;
}
render() {
return (
<div>
<p>{this.state.counter.get('number')}</p>
<input ref={input => this.amount = input} />
<button onClick={this.handleClick}>+</button>
</div>
)
}
}
ReactDOM.render(
<Caculator />,
document.getElementById('root')
)
2.redux+immutable手工实现
我们试着用redux+immutable手工实现一下~
import React, { Component } from 'react';
import ReactDOM from 'react-dom';
import PropTypes from 'prop-types'
import { createStore, combineReducers, applyMiddleware } from 'redux'
import { Provider, connect } from 'react-redux'
import immutable, { is, Map } from 'immutable';
import PureComponent from './PureComponent';
const ADD = 'ADD';
const initState = Map({ number: 0 });
function counter(state = initState, action) {
switch (action.type) {
case ADD:
return state.update('number', (value) => value + action.payload);
default:
return state
}
}
const store = createStore(counter);
class Caculator extends PureComponent {
render() {
return (
<div>
<p>{this.props.number}</p>
<input ref={input => this.amount = input} />
<button onClick={() => this.props.add(this.amount.value ? Number(this.amount.value) : 0)}>+</button>
</div>
)
}
}
let actions = {
add(payload) {
return { type: ADD, payload }
}
}
const ConnectedCaculator = connect(
state => ({ number: state.get('number') }),
actions
)(Caculator)
ReactDOM.render(
<Provider store={store}><ConnectedCaculator /></Provider>,
document.getElementById('root')
)
3.redux-immutable中间件
我们试着用redux-immutable中间件来体验一下~
import { combineReducers } from 'redux-immutable';
function combineReducers(reducers) {
return function (state = Map(), action) {
let newState = Map();
for (let key in reducers) {
newState = newState.set(key, reducers[key](state.get(key), action));
}
return newState;
}
}
let reducers = combineReducers({
counter
});
const ConnectedCaculator = connect(
state => {
return ({ number: state.getIn(['counter', 'number']) })
},
actions
)(Caculator)
4.react-router-redux使用
我们试着用react-router-redux来体验一下~
import React from "react";
import ReactDOM from "react-dom";
import { createStore, applyMiddleware } from "redux";
import { Provider } from "react-redux";
import { combineReducers } from 'redux-immutable';
import createHistory from "history/createBrowserHistory";
import { Route } from "react-router";
import { Map } from 'immutable';
import {
ConnectedRouter,
routerMiddleware,
push,
LOCATION_CHANGE
} from "react-router-redux";
const initialRouterState = Map({
location: null,
action: null
});
export function routerReducer(state = initialRouterState, { type, payload = {} } = {}) {
if (type === LOCATION_CHANGE) {
const location = payload.location || payload;
const action = payload.action;
return state
.set('location', location)
.set('action', action);
}
return state;
}
const history = createHistory();
const middleware = routerMiddleware(history);
const store = createStore(
combineReducers({
router: routerReducer
}),
applyMiddleware(middleware)
);
window.push = push;
window.store = store;
let Home = () => <div>Home</div>
let About = () => <div>About</div>
let Topics = () => <div>Topics</div>
ReactDOM.render(
<Provider store={store}>
<ConnectedRouter history={history}>
<div>
<Route exact path="/" component={Home} />
<Route path="/about" component={About} />
<Route path="/topics" component={Topics} />
</div>
</ConnectedRouter>
</Provider>,
document.getElementById("root")
);
5.react-router-redux实现
我们试着用react-router-redux来实现一下~
import React, { Component } from "react";
import ReactDOM from "react-dom";
import { createStore, combineReducers, applyMiddleware } from "redux";
import { Provider } from "react-redux";
import createHistory from "history/createBrowserHistory";
import { Router, Route } from "react-router";
import { Link } from "react-router-dom";
import PropTypes from 'prop-types';
// import {
// ConnectedRouter,
// routerReducer,
// routerMiddleware,
// push
// } from "react-router-redux";
const CALL_HISTORY_METHOD = '@@router/CALL_HISTORY_METHOD';
const LOCATION_CHANGE = 'LOCATION_CHANGE';
var initialRouteState = {
location: null
}
class ConnectedRouter extends Component {
static contextTypes = {
store: PropTypes.object
};
handleLocationChange = (location) => {
this.store.dispatch({
type: LOCATION_CHANGE,
payload: location
});
}
componentWillMount() {
this.store = this.context.store;
this.history = this.props.history;
history.listen(this.handleLocationChange);
}
render() {
return <Router {...this.props} />
};
}
function routerReducer(state = initialRouteState, action) {
let { type, payload } = action;
if (type === LOCATION_CHANGE) {
return { ...state, location: payload };
}
return state;
}
function routerMiddleware(history) {
return function () {
return function (next) {
return function (action) {
if (action.type !== CALL_HISTORY_METHOD) {
return next(action);
}
var _action$payload = action.payload,
method = _action$payload.method,
args = _action$payload.args;
history[method].apply(history, args);
};
};
};
}
//push
function push(...args) {
return {
type: CALL_HISTORY_METHOD,
payload: { method: 'push', args: args }
};
}
// Create a history of your choosing (we're using a browser history in this case)
const history = createHistory();
// Build the middleware for intercepting and dispatching navigation actions
const middleware = routerMiddleware(history);
// Add the reducer to your store on the `router` key
// Also apply our middleware for navigating
const store = createStore(
combineReducers({
router: routerReducer
}),
applyMiddleware(middleware)
);
window.push = push;
window.store = store;
// Now you can dispatch navigation actions from anywhere!
// store.dispatch(push('/foo'))
let Home = () => <div>Home</div>
let About = () => <div>About</div>
ReactDOM.render(
<Provider store={store}>
{/* ConnectedRouter will use the store from Provider automatically */}
<ConnectedRouter history={history}>
<div>
<Link to="/">Home</Link>
<Link to="/about">About</Link>
<Route exact path="/" component={Home} />
<Route path="/about" component={About} />
</div>
</ConnectedRouter>
</Provider>,
document.getElementById("root")
);
结尾
好啦,这期的前端丛林大冒险先到这里啦!这期我们深入了解了Immutable,也介绍了一下React的性能优化,相信让大家对Immutable有了更深入的了解,但是这期内容比较干不容易理解,大家一定要好好啃下来嚼烂嚼透。希望大家可以好好品尝并消化,迅速升级,接下来我们才更好地过五关斩六将!好啦,我们下期再见。拜拜!