immutable实现深浅拷贝
我们都知道在Redux里面在prevstate 与newData之间赋值的时候要实现深拷贝,之前的时候我们都通过const newData = JSON.parse(JSON.stringify(prevState));来实现深拷贝,但是我们知道这个方法太消耗性能,所以我们可以使用node自带的Api lodash
lodsh 用法
需要先npm i lodsh
const { cloneDeep } = require("lodash");
const state = {
str: "韩梅梅",
obj: { x: 1 },
arr: [1, 2, 3],
};
const newState = cloneDeep(state)
console.log(newState === state)//这个地方就是用了loadsh里面的cloneDeep方法实现了深拷贝
immutable用法
Documentation v4.1.0 — Immutable.js 官网
首先来看下immutable的定义
不可变数据 数据一旦被创建就不修改
对immutable数据做操作的时候都会返回一个新的immutable对象, 修改对象的值和原值一样 相当于是没修改
优缺点
优点
节省内存 优化代码 深拷贝 函数式编程 可以溯源,所有改变数据的操作都会记录
缺点
需要学习新的api 容易和原生的api混淆 需要引入第三方库
immutable用法
首先需要下载依赖 npm i immutable
然后再node里面用的时候
下面是把immutable里面我们用到的方法进行解构出来我们挨个解释
const { Map, List, Seq, fromJS, Set, is } = require("immutable");
Map
首先map是用来深拷贝对象的
set,get方法
例如:
const map1 = Map({//这里是把对象放到Map里面
a:1,
b:2,
c:3
})
const map2 = map1.set("a",4) //这里是把map1里的a属性改成4在赋值给map2,这里map1是不变的,但是map2的a就是4了
console.log(map1.get('a'),map2.get('a'))//1,4
console.log(map1 === map2) //false
但是如果是下面这样的话
const map2 = map1.set("a",1) ,map1里面的a原本就是1,所以改了原值并没有改
console.log(map1 === map2) //true ,所以这里是true
get方法
首先来看下js对象里面
const obj = {a: "one"}
console.log(obj["a"])//在用[]的时候里面要不是个字符串,就是一个变量,不能obj[a]获取,这样会报错a is a notdefind
//但是当key是一个数字的时候,我们可以
const obj = {1: "one"}
console.log(obj[1],obj["1"]) //这样可以获取
在immutable对象中只能用['字符串']
例如
const obj = {1: "one"}
const map1 = fromJS(obj) //将原生的js对象({},[])转化为immutable对象
console.log(map1.get(1),map1.get('1')) // undefined one 通过写数字是拿不到的
对于深度的元素取值 和修改值 通过 getIn([deep1,deep2,....]) updateIn([deep1,deep2],修改的回调)
setIn getIn updateIn方法
例如:
const nested = fromJS({ a: { b: { c: [3, 4, 5] } } }); //这是一个深层嵌套的对象
//我们想改变深层的b的值,当我们用set的时候
const map1 = nested.set("b",123)
console.log(map1.toJS()) //这样打印出来是{ a: { b: { c: [Array] } }, b: 123 },set是找第一层,有的话就修改,没有的话就添加,第一层没b所以就添加了一个b:123
setIn 改深层次的值,例如
const nested = fromJS({ a: { b: { c: [3, 4, 5] } } });
const map1 = nested.setIn(["a", "b"],"hehehda")
console.log(map1.toJS()) //{ a: { b: 'hehehda' } },这样就能修改深层次的b的value了
updateIn 通过 更新这个方法更新深层的值,可以操作之前的值,第二个参数是一个回调
const nested = fromJS({ a: { b: { c: [3, 4, 5] } } });
const map1 = nested.updateIn(['a','b'],(value) => {
console.log('xxx',value.toJS())
//xxx { c: [ 3, 4, 5 ] },所以这里的value是b的value,所以我们下面可以return对这个value进行操作的值
return 123 //return这个值就是代替b的value
})
console.log(map1.toJS())
getIn 获取深层次的值 ,例如
const nested = fromJS({ a: { b: { c: [3, 4, 5] } } });
console.log(nested.getIn(["a", "b"]).toJS());
//{ c: [ 3, 4, 5 ] },会直接拿到对象b的值
equals方法
用处:判断immutable对象值是不是相等
例如:
const map1 = Map({
a:1,
b:2,
c:3
})
const map2 = Map({
a:1,
b:2,
c:3
})
console.log(map1 === map2) //false这两个里面的值是一样的,但是地址不一样是false
console.log(map1 == map2)//false 同样是false
console.log(map1.equals(map2))//equals是用来判断值是否相等的,这两个的值相等,所以返回true
之前我们在判断两个对象的值是否相等的时候是使用JSON.stringify()转换成Json字符串在进行比较。例如
const obj1 = {
a:1,
b:2,
c:3
}
const obj2 = {
a:1,
b:2,
c:3
}
console.log(obj1 === obj2) // false
console.log(obj1 == obj2) // fasle
console.log(JSON.stringify(obj1) === JSON.stringify(obj2)) // true
map方法
之前是用在数组的现在用来处理对象,例如
const map1 = Map({ a: 1, b: 2, c: 3 });
const result = map1.map((v,k) => { //对对象进行了映射,改变为其他值在返回出来
console.log(v,k) //1 a 2 b 3 c
return v*v
})
console.log(result.toJS()) //{ a: 1, b: 4, c: 9 }
json方法
之前是把数组拼接起来,这里是对对象的value的值进行拼接,例如
const map1 = Map({ a: 1, b: 2, c: 3 });
console.log(map1.join("**"))//输出来的是1**2**3
merge方法
通过merge 可以合并map对象与原生js对象 并且前面的map会被obj覆盖,如果map在后面不会覆盖obj里的属性,例如
const map1 = Map({ a: 1, b: 2, c: 3 });
const obj = { o:100, a:200, d:111 } //obj里面的a覆盖了Map1里面的a,但是map2里面的d不会覆盖obj里面的d
const map2 = Map({ b: 4, c: 6, d:5 });
const map3 = map1.merge(map2,obj)
console.log(map3.toJS()) //{ a: 200, b: 4, c: 6, d: 111, o: 100 }
List
是用来处理数组的
例如:
const list1 = List([1,2])
const list2 = list1.push(3,4,5) //不在返回的是长度而是,拼接成的新数组,并且不会改变原数组
const list3 = list1.unshift(0)//不在返回的是长度而是,拼接成的新数组,并且不会改变原数组
const list4 = list1.concat(list2,list3)
console.log(list1.toArray())//[1,2]
console.log(list2.toArray())[1,2,3,4,5]
console.log(list3.toArray())[0,1,2]
console.log(list4.toArray())//[1, 2, 1, 2, 3, 4, 5, 0, 1, 2]
concat方法
他会把List对象与数组拼接起来,当然同样其他方法,push,unshift也都可以例如
const list = List([1,2])
const result = list.concat(List([2,4]),[1,2,3])
console.log(result.toJS())//[1, 2, 2, 4,1, 2, 3]
Seq
惰性计算,用到的时候才处理不用的时候不计算
例如
const map1 = Map({
a:1,
b:2,
c:3
})
const seq = Seq(map1).map((v,k) => {
console.log(v,k)
return v
} )
console.log(seq.toJS())
//1 a 2 b 3 c 这是上面的console.log(v,k)
//{ a: 1, b: 2, c: 3 } //上面的return v
//这里要是没有使用的话,上面的console.log(v,k)是不会执行的
fromJS
将原生的js对象({},[])转化为immutable对象
例如:
const obj = {1: "one"}
const map1 = fromJS(obj)
//这里就相当于我们之前的
const map1 =Map({1: "one"})
//数组的话也是同样的
const array =[1,2]
const list1 =fromJS(array )
//就相当于我们之前的
const list1 = List([1,2])
Set
const map1 = Map({a:1,b:2,c:3})
const map2 = Map({a:1,b:2,c:3}) //随便改变一个值,下面就是false
const set = Set().add(map1)
console.log(set.has(map2)) //true
is
也是来判断两个immutable对象的值是不是一样
const map1 = Map({a:1,b:2,c:3})
const map2 = map1.set('b',1000)
const map3 = map1.set('b',1000)
console.log(map2 === map3) //false,因为是通过map1,返回出来了两个不同的对象,地址不一样
console.log(map2.equals(map3))//true
console.log(is(map2,map3))//true //两个的值都是一样的
console.log(is(map1, map2))//false//两个的值都是不一样的
在redux里面的reducer函数用的时候
方法一:只在函数里面用
思路:
在reducer的入口将将所有的state通过fromjs 处理成immutable对象,return 值得时候在将immutable变成js对象
1. 只在reducer中使用immutable 正常的组件中还是使用原生js对象,组件的代码不需要改动
2. formjs tojs 效率相对要低
代码:
const DefaultState = {
info: {
age: 16
}
}
import {Map, fromJS} from "immutable"
import { CHANGE_AGE, CHANGE_NAME } from "./actionTypes";
export default (prevState = DefaultState, action) => {
// const newData =JSON.parse(JSON.stringify(prevState));
console.log("prevState", prevState)
let newData = null;
let preImmutable = fromJS(prevState)
// 注意不能修改源数据
const { type, payload } = action;
switch (type) {
case CHANGE_NAME:
newData.name = payload;
break;
case CHANGE_AGE: //点击实现改名的事件
newData = preImmutable.setIn(["info","age"],30)
console.log("xxxxnewData",newData)
break;
default:
newData = preImmutable
break;
}
return newData.toJS();
};
方法二:
不论实在reducer 还是在组件中都使用immutable
import {map} from "immutable"
import { combineReducers } from "redux-immutable"