immutabel

我们平时写的对象赋给变量,都是共享堆内存,列入

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

什么是 Immutable

Immutable Data 就是一旦创建,就不能再被更改的数据。对 Immutable 对象的任何修改或添加删除操作都会返回一个新的 Immutable 对象 Immutable 实现的原理是 Persistent Data

Structure(持久化数据结构),也就是使用旧数据创建新数据时,要保证旧数据同时可用且不变 同时为了避免 deepCopy 把所有节点都复制一遍带来的性能损耗

Immutable 使用了 Structural Sharing(结构共享),即如果对象树中一个节点发生变化,只修改这个节点和受它影响的父节点,其它节点则进行共享

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

下面我们用一下这个的基本功能(记得先安装哦)

// // const obj1 = { user: { name: 'zfpx' } };
// // const obj2 = obj1;
// // obj2.user.name = 'zfpx2';
// // console.log(obj1.user.name);

let immutable = require('immutable');
let { Map, List ,is} = immutable;
// function Map(obj){
//     obj.set(key,value){
//         // return Object.assign(obj,{[key]: value})
//         return {...obj,[key]: value}
//     }
//     return obj
// }
// let obj1 = Map({ name: 'zfpx', age: 9, home: { name: '北京', number: 0 } });
let obj1 = immutable.fromJS({ name: 'zfpx', age: 9, home: { name: '北京', number: 0 } });
let obj2 = obj1.set('name', 'zfpx2');
console.log(obj1, obj2);
console.log(obj1.home === obj2.home);
console.log(obj1.size);
console.log(obj1.count());
let obj3 = obj1.setIn(['home', 'name'], '山东');
console.log(obj3.get('name'), obj3.getIn(['home', 'name']));

let obj4 = obj3.update('age', val => val + 1);
console.log(obj4);
let obj5 = obj4.updateIn(['home', 'number'], val => val + 2);
console.log(obj5);
let obj6 = obj5.delete('name');
console.log(obj6);
let obj7 = obj5.clear();
console.log(obj7);
let obj8 = obj7.merge({ name: 'zfpx', age: 8 });
console.log(obj8);
console.log(obj8.toJS());
console.log(obj8.toJSON());
console.log(obj8.toObject());

console.log(...obj8.keys());
console.log(...obj8.values());
console.log(...obj8.entries());


let arr1 = List([1, 2, 3]);
let arr2 = arr1.push(4);
let arr3 = arr2.pop();
let arr4 = arr3.map(item => item * 2);
let arr5 = arr4.filter(item => item < 5);
console.log(arr1);
console.log(arr2);
console.log(arr3);
console.log(arr4);
console.log(arr5);
let arr6 = arr5.update(1, val => val + 1);
console.log(arr6);
let arr7 = arr6.delete(0);
console.log(arr7);
let arr8 = arr7.push(10);
let arr9 = arr8.last();
console.log(arr8);
console.log(arr9);
//[0,1,2,3,4,5,6,7,8,9,10]
console.log(immutable.Range(1, 6));//[1,2,3,4,5]
//4,5 -> 8 10 => 18
let ret = immutable.Range(1, 6).skip(3).map((n) => n * 2).filter((n) => n % 2 == 0).take(1).reduce((a, b) => a + b, 0);
console.log(ret);

let obj11 = Map({ name: 'zfpx', age: 9 });
let obj22 = Map({ name: 'zfpx', age: 9 });
// true immutable 的is比较的值
console.log(Object.is(obj11, obj22), is(obj11, obj22));
function handle() {
    let obj = immutable.fromJS({ name: 'zfpx' });
    invoke(obj);
    console.log(obj.get('name'));//9
}
handle();

复制代码

imuutabel主要用于优化,如果state没有变化就不进新房刷新,那怎么做到的呢?主要是用啦PureComponent,PureComponent的实现原理其实是shouldComponentUpdate函数,因为setState内部实现也是靠shouldComponentUpdate(newState)来决定是否渲染,通常我们判断state是否更新,需要深拷贝,和深度对比,这样级占内存,也消耗CPU,严重影响性能,如下

index.js

import React, { Component} from 'react';
import PureComponent from './PureComponent';
import _ from 'lodash'; //深拷贝用
import ReactDOM from 'react-dom';
// import { Map } from 'immutable';
//组件的优化的原理是重写了shouldComponentUpdate,如果说老的状态对象和新的状态对象不是一个对象话才会刷新
/**
 * 1. 每次一定都要生成新的对象 深克隆是非常消耗内存的。
 */
class Counter extends PureComponent {
    state = {
        counter: Map({ number: 0 })
    }
    handleClick = (event) => {
        let state = _.cloneDeep(this.state);
        // let state = {...this.state}; 不可用
        let amount = this.amount.value ? Number(this.amount.value) : 0;
        state.counter.number = state.counter.number + amount;
        this.setState(state);
    }
    render() {
        console.log('render');
        return (
            //<div>
                //<p>{this.state.counter.number}</p>
                //<input ref={input => this.amount = input} />
                //<button onClick={this.handleClick}>+</button>
            //</div>
        )
    }
}
// function setState(newState) {
//     if (shouldComponentUpdate(newState)) {
//         render();
//     }
// }
//ReactDOM.render(<Counter />, document.querySelector('#root'));
复制代码

PureComponent.js

import React , {Component} from 'react';
import {is} from 'immutable';
export default class PureComponent extends Component{
    shouldComponentUpdate(newProps, newState){
        return !_.isEqual(nextState,this.state) //深度比较,深度克隆,占内存和cpu
        return false;
    }
}
复制代码

优化后 index


import React, { Component} from 'react';
import PureComponent from './PureComponent';
// import _ from 'lodash';
import ReactDOM from 'react-dom';
import {Map} from 'immutable'
// import { Map } from 'immutable';
//组件的优化的原理是重写了shouldComponentUpdate,如果说老的状态对象和新的状态对象不是一个对象话才会刷新
/**
 * 1. 每次一定都要生成新的对象 深克隆是非常消耗内存的。
 */
class Counter extends PureComponent {
    state = {
        counter: Map({ number: 0 })
    }
    handleClick = (event) => {
        // let state = _.cloneDeep(this.state);
        // let state = {...this.state};
        let amount = this.amount.value ? Number(this.amount.value) : 0;
        let state = {...state , counter: this.state.counter.update('number',val =>val + amount)};
        // state.counter.number = state.counter.number + amount;
        // let state = { ...state, counter: this.state.counter.update('number', val => val + amount) }
        this.setState(state);
    }
    render() {
        console.log('render');
        return (
            //<div>
                //<p>{this.state.counter.get('number')}</p>
                //<input ref={input => this.amount = input} />
                //<button onClick={this.handleClick}>+</button>
            //</div>
        )
    }
}
// function setState(newState) {
//     if (shouldComponentUpdate(newState)) {
//         render();
//     }
// }
//ReactDOM.render(<Counter />, document.querySelector('#root'));
复制代码

PureComponent

import React , {Component} from 'react';
import {is} from 'immutable';
import _ from 'lodash';
export default class PureComponent extends Component{
    shouldComponentUpdate(newProps, newState){
        // return !_.isEqual(nextState,this.state) //深度比较,深度克隆,占内存和cpu
        let oldState = this.state || {};
        newState = newState ? newState : {};
        if (Object.keys(oldState).length !== Object.keys(newState).length) {
            return true;
        }
        for (let key in newState) {
            if (!is(newState[key], oldState[key])) {
                return true;
            }
        }
        return false;
    }
}
复制代码

配合redux

componet/counter.js

import React,{Component} from 'react';
import { connect } from 'react-redux';
class Counter extends Component{
    render(){
        return(
            <div>
                <p>{this.props.number}</p>
                <input ref={input => this.amount = input} />
                <button onClick={() => this.props.add(1)}>+</button>
            </div>
        )
    }
}
//actions
let actions = {
    add(payload){
        return {type : 'ADD', payload}
    }
}
export default connect(
    state => ({ number: state.getIn(['counter','number'])}), 
    //本身这里也应该是一个immutable对象,但是这里不是,不太合理
    actions
)(Counter)
复制代码

store/index.js

import {createStore,applyMiddleware} from 'redux';
import createSagaMiddleware from '../redux-saga';
import {rootSaga} from '../saga'
import {Map} from 'immutable';
// import {combineReducers} from 'react-immutable'

let sagaMiddleware = createSagaMiddleware();
//创建个immutable对象
let initState = Map({ number: 0 });
//设定reducers,需要传入状态,和action,对action做处理
function counter(state = initState , action){
    switch (action.type) {
        case 'ADD':
            return state.update('number', val => val + action.payload);
        default:
            return state;
    }
}
//combineReducers方法的实现
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;
    }
}
//合并reducer
let reducer = combineReducers({
    counter
})
//通过reducer创建仓库
let store = createStore(reducer);
export default store
复制代码

src/index.js

import React, { Component} from 'react';
import PureComponent from './PureComponent';
// import _ from 'lodash';
import ReactDOM from 'react-dom';
import {Map} from 'immutable'
// import { Map } from 'immutable';
//组件的优化的原理是重写了shouldComponentUpdate,如果说老的状态对象和新的状态对象不是一个对象话才会刷新
/**
 * 1. 每次一定都要生成新的对象 深克隆是非常消耗内存的。
 */
class Counter extends PureComponent {
    state = {
        counter: Map({ number: 0 })
    }
    handleClick = (event) => {
        // let state = _.cloneDeep(this.state);
        // let state = {...this.state};
        let amount = this.amount.value ? Number(this.amount.value) : 0;
        let state = {...state , counter: this.state.counter.update('number',val =>val + amount)};
        // state.counter.number = state.counter.number + amount;
        // let state = { ...state, counter: this.state.counter.update('number', val => val + amount) }
        this.setState(state);
    }
    render() {
        console.log('render');
        return (
            <div>
                <p>{this.state.counter.get('number')}</p>
                <input ref={input => this.amount = input} />
                <button onClick={this.handleClick}>+</button>
            </div>
        )
    }
}
// function setState(newState) {
//     if (shouldComponentUpdate(newState)) {
//         render();
//     }
// }
ReactDOM.render(<Counter />, document.querySelector('#root'));
复制代码

immutabel结合react-roter

目的是为了实现通过派发发动作创建路由

src/index.js

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 LOCATION_CHANGE = 'LOCATION_CHANGE';
class ConnectedRouter extends Component {
    static contextTypes = {
        store: PropTypes.object
    }
    //连接仓库和路由,监听路由变化,当路径发生变化的时候派发动作
    componentWillMount() {
        this.store = this.context.store;
        //window.addEventListener('hashchange',()=>{})
        //window.addEventListener('pushState',()=>{})
        //history是一个封装,让hashhistory 和browerhistory使用相同的api
        this.props.history.listen(location => {
            this.store.dispatch({
                type: LOCATION_CHANGE,
                location
            });
        });
    }
    render() {
        return <Router {...this.props} />
    }
}
let initRouterState = { location: {} };
function routerReducer(state = initRouterState, action) {
    switch (action.type) {
        case LOCATION_CHANGE:
            return { ...state, location: action.location };
        default:
            return state;
    }
    return state;
}
const CHANGE_LOCATION = 'CHANGE_LOCATION';
function push(pathname) {
    return {
        type: CHANGE_LOCATION,
        pathname
    }
}
function routerMiddleware(history) {
    return function ({ getState, dispatch }) {
        return function (next) {
            return function (action) {
                if (action.type == CHANGE_LOCATION) {
                    history.push(action.pathname);
                } else {
                    next(action);
                }
            }
        }
    }
}
// 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)
);

let Home = () => <div>Home</div>
let About = () => <div>About</div>
let Topics = () => <div>Topics</div>
// Now you can dispatch navigation actions from anywhere!
// store.dispatch(push('/foo'))
window.store = store; //将store暴漏在全局
window.push = push;
ReactDOM.render(
    <Provider store={store}>
        {/* ConnectedRouter will use the store from Provider automatically */}
        <ConnectedRouter history={history}>
            <div>
                <Link to="/about">about</Link>
                <Link to="/topics">topics</Link>
                <Route exact path="/" component={Home} />
                <Route path="/about" component={About} />
                <Route path="/topics" component={Topics} />
            </div>
        </ConnectedRouter>
    </Provider>,
    document.getElementById("root")
);
复制代码
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值