一、前言
Immer 是 mobx 的作者写的一个 immutable 库,核心实现是利用 ES6 的 proxy,几乎以最小的成本实现了 js 的不可变数据结构,简单易用、体量小巧、设计巧妙,满足了我们对JS不可变数据结构的需求。
二、Immutable的缺陷问题
- 需要使用者学习它的数据结构操作方式,没有 Immer 提供的使用原生对象的操作方式简单、易用;
- 它的操作结果需要通过
toJS
方法才能得到原生对象,这使得在操作一个对象的时候,时刻要注意操作的是原生对象还是 ImmutableJS 的返回结果,稍不注意,就会产生意想不到的 bug。
三、immer基本使用
语法:
produce(currentState, recipe: (draftState) => void | draftState, ?PatchListener): nextState
-
defaultState:
被操作对象的最初状态
-
draftState:
根据
defaultState
生成的草稿状态,它是defaultState
的代理,对draftState
所做的任何修改都将被记录并用于生成nextState
。在此过程中,defaultState
将不受影响 -
nextState:
根据
draftState
生成的最终状态 -
produce:
用来生成
nextState
或producer
的函数 producer
生产者
通过produce
生成,用来生产nextState
,每次执行相同的操作recipe
生产机器
用来操作draftState
的函数
const { produce } = require('immer');
let defaultState = {
num: 10,
str: 'alley',
arr: [10,20,30,40],
obj: {
nickname:'alley-巷子',
age:20
}
}
let data = produce(defaultState, drafy => {
drafy.num = 100;
})
console.log(data === defaultState ) // false;
3-1、原理
const { produce } = require('immer');
let defaultState = {
num: 10,
str: 'alley',
arr: [10,20,30,40],
obj: {
nickname:'alley-巷子',
age:20
}
}
let nextState = produce(defaultState, draftState => {
draftState.arr.push(100);
})
console.log(nextState === defaultState ) // false;
console.log(nextState.arr === defaultState.arr ) // false;
console.log(nextState.num === defaultState.num ) // true;
console.log(nextState,defaultState)
由此可见,对
draftState
的修改都会反应到nextState
上,而Immer
使用的结构是共享的,nextState
在结构上又与currentState
共享未修改的部分,共享效果如图(借用的一篇Immutable
文章中的动图,侵删):
Immer
还在内部做了一件很巧妙的事情,那就是通过produce
生成的nextState
是被冻结(freeze
)的,(Immer
内部使用``Object.freeze方法,只冻结
nextState跟
currentState相比修改的部分),这样,当直接修改
nextState时,将会报错。 这使得
nextState `成为了真正的不可变数据
3-2、高阶函数用法
语法:
produce(recipe: (draftState) => void | draftState, ?PatchListener)(currentState): nextState
recipe
是否有返回值,nextState
的生成过程是不同的:
-
recipe
没有返回值时:nextState
是根据recipe
函数内的draftState
生成的; -
recipe
有返回值时:nextState
是根据recipe
函数的返回值生成的;
let producer = produce((draft) => {
draft.x = 2
});
let nextState = producer(currentState);
/* 没有返回值 */
let producer = produce((draft) => {
draft.num = 100;
});
let nextState = producer(defaultState);
console.log(nextState);
/*
{
num: 100,
str: 'alley',
arr: [ 10, 20, 30, 40 ],
obj: { nickname: 'alley-巷子', age: 20 }
}
*/
/* 有返回值 */
let producer = produce((draft) => {
return {
num:11
}
});
let nextState = producer(defaultState);
console.log(nextState);
/*
{
num: 11
}
*/
3-3、recipe中的this
ecipe 函数内部的
this
指向 draftState ,也就是修改this
与修改 recipe 的参数 draftState ,效果是一样的。
注意:此处的 recipe 函数不能是箭头函数,如果是箭头函数,this
就无法指向 draftState 了
produce(currentState, function(draft){
// 此处,this 指向 draftState
draft === this; // true
})
本文章参考:https://segmentfault.com/a/1190000017270785