React 數據爲何要使用immutable方式?淺複製與深複製思考

深複製與淺複製

let obj = {
    a: 1,
    arr: [1, 2]
};
let obj1 = obj;            //淺複製
obj1.a = 2

console.log(obj) // { a:2, arr: [1,2] };

//一樣的方式
let obj = {
    a: 1,
    arr: [1, 2]
};
let obj2 = deepCopy(obj);  //深複製
obj2.a = 2
console.log(obj) // { a:1, arr: [1,2] };

由於JavaScript存儲對象都是存地址的,因此淺複製會致使 obj 和 obj1
指向同一塊內存地址,大概的示意圖以下。而深複製通常都是開闢一塊新的內存地址,將原對象的各個屬性逐個複製出去。html

es6-Object.assign()方法

深複製只有一層,以後爲淺複製(除非再次使用Object.assign嵌套方式賦值)react

let obj = {
    a: 1,
    arr: [1, 2]
};
let obj1 = Object.assign({}, obj);

obj1.a = 2
//不變
console.log(obj) // { a:1, arr: [1,2] };



let obj = {
    a: {
        b: 20
    },
    arr: [1, 2]
};
let obj1 = Object.assign({}, obj);

obj1.a.b = 2;
//除非再次使用Object.assign嵌套方式賦值
//變化
console.log(obj) // { a:{b:2}, arr: [1,2] };

爲何使用不可變(immutable)的數據?

(pureRender結合immutable,見末尾)

下面是項目中實際的一個例子

第一種方式git

//recduer.js(cart)第一種方式
case types.CART_PUT_MAIN + '_SUCCESS':
    //更新數據
    carts = state.main.carts; // carts 選中的id數組
    id = action.param.id;
    newState = {
        ...state,
        main:{
            ...state.main,
            itemObj:{
                ...state.main.itemObj,
                [id]:{
                    ...state.main.itemObj[id],
                    quantity:action.param.quantity
                    
                }
            }
        }
    };
    sum = sumCommon(carts, newState.main.itemObj);
    newState = {
        ...newState,
        main:{
            ...newState.main,
            ...sum
        }
    };
    return newState;

讓咱們來看一下對數據層的變化:es6

componentWillReceiveProps(nextProps){
    console.log(nextProps); 
    //next:顧名思義是接收到的next->props,輸出的是上面方法中的newState的值
    console.log(this.props);
    //cur:是當前的props的值,由於使用的是類immutable的方式,因此數據不變;
}

第二種方式github

//recduer.js(cart)第一種方式
case types.CART_PUT_MAIN + '_SUCCESS':
    newState = Object.assign({}, state);
    carts = newState.main.carts; // carts 選中的id數組
    id = action.param.id;
    //淺複製
    newState.main.itemObj[id].quantity = action.param.quantity;;
    sum = sumCommon(carts, newState.main.itemObj);

    newState = Object.assign({}, newState, {
        main: Object.assign({}, newState.main, sum)
    });
    return newState;

讓咱們來再來看一下對數據層的變化:api

componentWillReceiveProps(nextProps){
    console.log(nextProps); 
    //next:顧名思義是接收到的next->props,輸出的是上面方法中的newState的值
    console.log(this.props);
    //cur:是當前的props的值,而這個因爲淺複製,這個值被改變了
}

爲了讓數據變化更加可測,咱們應當使用深複製相關,讓咱們本身的數據更加安全數組

處理方法一:es7 ... 的方式

直接{...obj}賦值屬於淺複製,在修改值時{...obj,a:1}就起到了類深複製的效果
更新一個 Object ,則:安全

let obj = {
    a: 0,
    b: 20,
}
obj = {...obj, a: obj.a + 1}

而不是:babel

obj.a = obj.a + 1

一樣的爲了不對 Object 的 in-place editing,數組也是同樣:性能

let arr = [
    { id: 1,a: 1}
]
arr = [...arr, { id: 2,a: 2} ]

而不是:

let arr = [
    { id: 1, a:1}
]
arr.push({ id: 2, a,2});

以這樣的方式,無需 Immutable.js ,咱們可讓應用程序狀態是 不可變(Immutable) 的。

...注意事項及要求

let obj = {
    a: 20,
    arr: [1, 2]
};
let obj1 = { ...obj }; //於obj1=obj同樣
// 保持統一,儘可能不要使用這樣的替換(有可能形成沒必要要的麻煩)
obj1.a = 2
//...儘可能使用這樣的賦值形式
obj1 = { ...obj1 , a:2 }
//深複製
console.log(obj) // { a:20, arr: [1,2] };
console.log(obj1) // { a:2, arr: [1,2] };

...與Object.assign屬於一個道理(這裏和層級相關)

//你能夠將其轉化爲
let obj = {
    a: {
        b: 20
    },
    arr: [1, 2]
};
let obj1 = obj
obj1 = Object.assign({}, obj1, {
    a: Object.assign({}, obj1.a,{b:2})
});
console.log(obj) //{ a:{b:20}, arr: [1,2] }
console.log(obj) //{ a:{b:2}, arr: [1,2] }

因此儘可能使用...代替Object.assign

處理方法二:使用immutable.js

爲何須要使用immutable.js

以前方式的多層嵌套

//深複製(類immutable)
newState = {
    ...state,
    main:{
        ...state.main,
        itemObj:{
            ...state.main.itemObj,
            [id]:{
                ...state.main.itemObj[id],
                prop:action.param.props_str,
                product_id:action.param.product_id,
                price:action.param.price
            }
        }
    }
};
//淺複製
newState.main.itemObj[id].prop = action.param.props_str;
//immutable.js方式
...參考immutable的api吧,暫時就不提供了--!

PureRenderMixin使用請參考如下內容

簡單的說就是數據變化,比較先後兩次的數據是否相同,判斷是否從新render;不然你的父容器一改變數據,全部的子組件都從新渲染了,爲了增長性能請使用pureRender;

(封裝好的PureRender以下:)

'use strict';

import { is } from 'immutable';

let hasOwnProperty = Object.prototype.hasOwnProperty;
function shallowEqual(objA, objB) {
    if (objA === objB || is(objA, objB)) {
        return true;
    }

    if (typeof objA !== 'object' || objA === null || typeof objB !== 'object' || objB === null) {
        return false;
    }

    let keysA = Object.keys(objA);
    let keysB = Object.keys(objB);

    if (keysA.length !== keysB.length) {
        return false;
    }
    let bHasOwnProperty = hasOwnProperty.bind(objB);
    for (let i = 0; i < keysA.length; i++) {
        if (!bHasOwnProperty(keysA[i]) || !is(objA[keysA[i]], objB[keysA[i]])) {
            return false;
        }
    }

    return true;
}
function shallowCompare(instance, nextProps, nextState) {
    return !shallowEqual(instance.props, nextProps) || !shallowEqual(instance.state, nextState);
}
function shouldComponentUpdate(nextProps, nextState) {
    return shallowCompare(this, nextProps, nextState);
}
function pureRenderDecorator(component) {
    component.prototype.shouldComponentUpdate = shouldComponentUpdate;
}
module.exports = pureRenderDecorator;

/*使用方式*/
import pureRender from 'pure-render-decorator';
//babel配置中引入一個transform-decorators-legacy插件
@pureRender
class XXX extends React.Component {
    //...
}

PureRender的使用要求:對於子組件須要什麼參數傳遞什麼,不要把一大塊無用的數據引入,不然兩次傳入的this.props可能始終會不同,致使PureRender無效

相關文章
相關標籤/搜索