React进阶篇2

1. 可共享可变状态是万恶之源

let objA = { name: 'zfpx' };
let objB = objA;
objB.name = '9';
console.log(objA.name);
复制代码

2. 什么是 Immutable

  • Immutable Data 就是一旦创建,就不能再被更改的数据。对 Immutable 对象的任何修改或添加删除操作都会返回一个新的 Immutable 对象
  • Immutable 实现的原理是 Persistent Data Structure(持久化数据结构),也就是使用旧数据创建新数据时,要保证旧数据同时可用且不变 同时为了避免 deepCopy 把所有节点都复制一遍带来的性能损耗
  • Immutable 使用了 Structural Sharing(结构共享),即如果对象树中一个节点发生变化,只修改这个节点和受它影响的父节点,其它节点则进行共享
  • immutable-js immutablejs

3.Immutable类库

内部实现了一套完整的 Persistent Data Structure,还有很多易用的数据类型。像 Collection、List、Map、Set、Record、Seq

3.1 Map

方法作用
isMap判断是否是Map
clear清空值
set设置值
delete删除值
update更新值
merge合并值
setIn设置值
deleteIn删除值
updateIn更新值
mergeIn合并值
get获取值
getIn获取值
keyskey的数组
valuesvalue的数组
entriesentry的数组
toJS转成普通JS对象
toObject转成普通对象
toJSON转成JSON对象
toArray转成数组
let obj1 = immutable.Map({ name: 'zfpx', age: 8 });
let obj2 = obj1.set('name', 'zfpx2');
let obj3 = obj2.update('age', x => x + 1);
let obj4 = obj3.merge({ home: '北京' });
console.log(obj1, obj2, obj3, obj4);


let obj6 = immutable.fromJS({ user: { name: 'zfpx', age: 8 }, 'k': 'v' });
let obj7 = obj6.setIn(['user', 'name'], 'zfpx2');
let obj8 = obj7.updateIn(['user', 'age'], x => x + 1);
let obj9 = obj8.mergeIn(["user"], { home: '北京' });
console.log(obj6, obj7, obj8, obj9);

console.log(obj6.get('user'));

console.log(obj6.getIn(['user', 'name']));
console.log(...obj6.keys());
console.log(...obj6.values());
console.log(...obj6.entries());

var map1 = immutable.Map({ name: 'zfpx', age: 9 });
var map2 = immutable.Map({ name: 'zfpx', age: 9 });
assert(map1 !== map2);
assert(Object.is(map1, map2) === false);
assert(immutable.is(map1, map2) === true); 
复制代码

3.2 List

方法作用
isList判断是否是List
size统计个数
push添加
pop弹出最后一个
update更新
delete删除指定元素的数组delete(2)
insert插入指定元素的数组insert(2)
clear清空数组clear()
concat合并
map映射
filter过滤
get获取
find查找
includes判断包含
last最后一个
reduce计算总和
count统计个数
let immutable = require('immutable');
let arr1 = immutable.fromJS([1, 2, 3]);
console.log(arr1.size);
let arr2 = arr1.push(4);
console.log(arr2);
let arr3 = arr2.pop();
console.log(arr3);
let arr4 = arr3.update(2, x => x + 1);
console.log(arr4);
let arr5 = arr4.concat([5, 6]);
console.log(arr5);
let arr6 = arr5.map(item => item * 2);
console.log(arr6);
let arr7 = arr6.filter(item => item >= 10);
console.log(arr7);
console.log(arr7.get(0));
console.log(arr7.includes(10));
console.log(arr7.last());
let val = arr7.reduce((val, item) => val + item, 0);
console.log(val);
console.log(arr7.count());

// 0 1 2 3 4 5  => 3 4 5 => 6 8 10 => 6 8 10 => 8 10 => 18
let ret = immutable.Range(1, 6).skip(3).map((n) => n * 2).filter((n) => n % 2 == 0).take(2).reduce((a, b) => a + b, 0);
console.log(ret);
复制代码

4.Immutable优势

4.1 降低复杂度

let obj = {age:8};
handle(obj);
console.log(obj.age);
复制代码

4.2 节省内存

let Immutable=require('immutable');
let p1=Immutable.fromJS({
    name: 'zfpx',
    home:{name:'beijing'}
});
let p2 = p1.set('name','zfpx2');
console.log(p1.get('home')== p2.get('home'));
复制代码

4.3 方便回溯 只要把每次的状态都放在一个数组中就可以很方便的实现撤销重做功能

5. React性能优化

5.1 计数器

import React, { Component } from 'react';
import ReactDOM from 'react-dom';
import _ from 'lodash';
class Caculator extends Component {
    state = {
        number: 0
    }
    handleClick = () => {
        let amount = this.amount.value ? Number(this.amount.value) : 0;
        this.state.number = this.state.number + amount;
        this.setState(this.state);
    }
    shouldComponentUpdate(nextProps, prevState) {
        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')
)
复制代码

5.2 深度克隆

handleClick = () => {
        let amount = this.amount.value ? Number(this.amount.value) : 0;
        let state = _.cloneDeep(this.state);
        state.number = this.state.number + amount;
        this.setState(state);
}
复制代码

5.3 深比较

 shouldComponentUpdate(nextProps, prevState) {
        return !_.isEqual(prevState, this.state);
 }
复制代码

5.4 immutable

import React, { Component } from 'react';
import ReactDOM from 'react-dom';
import _ from 'lodash';
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;
        this.setState({ counter: this.state.counter.update('number', val => val + amount) });
    }
    shouldComponentUpdate(nextProps = {}, nextState = {}) {
        nextState = nextState == null ? {} : nextState;
        const thisProps = this.props || {}, thisState = this.state || {}, nextState = this.state || {};
        if (Object.keys(thisProps).length !== Object.keys(nextProps).length ||
            Object.keys(thisState).length !== Object.keys(nextState).length) {
            return true;
        }

        for (const key in nextProps) {
            if (!is(thisProps[key], nextProps[key])) {
                return true;
            }
        }

        for (const key in nextState) {
            if (thisState[key] !== nextState[key] && !is(thisState[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')
)
复制代码

6. 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')
)
复制代码

7. 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)
复制代码

8. 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")
);
复制代码

9. 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")
);
复制代码
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值