immutability-helper 学习笔记 -1

8次阅读

共计 5034 个字符,预计需要花费 13 分钟才能阅读完成。

本来想将有关于 immutability-helper 的博文放在一起学 React 系列博文中,但是考虑到该插件不仅仅在 React 中实用到,所以就单独拿出来分两期写。
发现问题
immutability 意为不变,不变性,永恒性。至于该插件能做什么,我想它的作者对它的标注已经很明确了 mutate a copy of data without changing the original source,意为:在不改变原始来源的情况下改变数据副本。同时在这里笔者再推荐另一个与之相似的插件,那就是 Facebook 出品的 immutable-js (我们可以认为 immutability-helper 是 immutable-js 的终极简化版)。脸书对 immutable-js 的标注也很明确:Immutable persistent data collections for Javascript which increase efficiency and simplicity,意为:使得 Javascript 中的持久数据集合保持不可变,从而提高效率和简单性。
仔细阅读他们对两个插件格子的描述,我们能得到一个关键字,那就是不可变。为什么要不可变?在这里笔者觉得从 React 角度来解释可能会更加清晰。我们都知道 React 组件本身会包含 N 个状态,而且这些状态会通过 setState 方法去更新,也就是说 React 组件的状态是可变的,实际上本就是这么一回事。但是作为 React 的初学者可能会误解状态是可变的这句话。怎么说?因为对于 React 组件而言,状态的变化是 State 的变化而不是 State 值的变化,什么?看不懂?那我们继续往下说。假如我们初始化了这样一个状态:
this.state={
name:’xiaoming’,
hobbies:[‘qq’,’wx’]
}
现在才是解释什么叫 State 的变化而不是 State 值的变化。假如我们先这样做:
this.setState({
name:’hanmeimei’
})
经过这个操作以后,name 对应的 DOM 节点会得到更新,而如果我们这样做:
function changeHobby() {
let hobbies = this.state.hobbies;
hobbies.push(‘Coding’);
this.setState({
hobbies: hobbies
})
}
引出问题
运行了这个方法以后会发生什么?Nothing Happened, But why? 这个问题也是经常发生在 React 初学者身上,因为 React 在检测状态变化时候是一个浅检测,换句话说就是只关注 name 或者 hobbies 这两个 key 对应的 value 有没有变化,有变化就重新 Render 否则不做任何动作。那如何去检测 value 有没有变化,其实就是根据变量的地址。我们都知道变量在内存中都有唯一的地址。拿刚刚的两个例子来说,为什么更新了 name 和更新了 hobbies 这两个状态会有两个不同的结果?那是因为在 JavaScritp 中,string 就是被设计成不可变,而数组则可变。换句话说我们无法在保证字符串内存地址不变的情况下改变字符串但却可以保证数组在内存地址不变的情况下增加或者删除其中的某一个元素。所以这就是为什么我们更新 hobbies 这个状态的情况组件并没有刷新的原因,我们用例子来证明下数组这一特性:
let hobbies = [‘qq’, ‘wx’];
let hobbies_2 = hobbies;
hobbies_2.push(‘Coding’);

console.log(hobbies === hobbies_2);
可以看出这两个变量实际上是同一个。再举个例子并且是在日常开发中肯定会遇到过的。假如现在要将后台的传来的数据渲染成一个列表,数据如下:
[
{‘id’:0,name:’xiaoming’},
{‘id’:1,name:’lilei’},
{‘id’:2,name:’hanmeimei’},
]
这是一个很简单的操作。然后我们做了一个更新的功能,当我们将 id 为 1 的那条的姓名改成 ’Miss Li’,那该怎么办才能保证在 setState 后让该列表组件重新渲染?估计大家心里立刻有两个方案,一个是重新获取后台数据然后重新渲染,另一个是重新拷贝一份数据然后更改相应的地方后重新 set 给某个 State 完成重新渲染。但是我们可以看出,这两个操作最终的效果仍然是创建另一个与初始数组在内容上完全相同的数组,虽然相对繁琐但是着实有效。不过这两个方案各有缺点,比如第一个方案需要额外增加一次网络请求,第二个是万一数据量过于庞大就会造成内存的浪费。既然这样有什么更好的解决方案呢?到这里我相信大家就明白了该篇博客的用意了,因为这个 immutability-helper 在笔者近期项目中使用最频繁,所以觉得有必要拿出来说一说。
immutability-helper 使用方法
使用方法很简单,首先是安装依赖
npm install immutability-helper –save
然后是在有需要的地方引入即可
import update from ‘immutability-helper’;
这样我们可以使用 update 这个方法做我们想做的事情了。
那就有人问了,为什么它就能完美的解决问题呢?其实很简单,就那刚刚的列表数据来说,immutability-helper 会输出一个全新的数组对象并且只会更新跟 id= 1 有关的那条数据,剩下的通过地址引用的方式引入到新数组中,这样会最大限度的使用数据和内存。
immutability-helper 工作方式
immutability-helper 的工作方式也很简单,通过在 update 方法在使用指令就可以实现对数据的修改。immutability-helper 的指令组成也很简单:$+ 关键字。而且这个关键字我们仍然很熟悉,接着往下看。它支持的指令一共有:

{$push: array} push() all the items in array on the target.
{$unshift: array} unshift() all the items in array on the target.
{$splice: array of arrays} for each item in arrays call splice() on the target with the parameters provided by the item. Note: The items in the array are applied sequentially, so the order matters. The indices of the target may change during the operation.
{$set: any} replace the target entirely.
{$toggle: array of strings} toggles a list of boolean fields from the target object.
{$unset: array of strings} remove the list of keys in array from the target object.
{$merge: object} merge the keys of object with the target.
{$apply: function} passes in the current value to the function and updates it with the new returned value.
{$add: array of objects} add a value to a Map or Set. When adding to a Set you pass in an array of objects to add, when adding to a Map, you pass in [key, value] arrays like so: update(myMap, {$add: [[‘foo’, ‘bar’], [‘baz’, ‘boo’]]})
{$remove: array of strings} remove the list of keys in array from a Map or Set.

这些指令关键字是不是很熟悉?因为我们在日常开发中经常使用到这些。下面就开始一个一个介绍。
指令使用
$push
push,顾名思义和数组有关,其实就是向源数组中增加一个元素并且输出一个内容想通了的新数组。看例子:
var update = require(“immutability-helper”);
const state1 = [“x”];
const state2 = update(state1, { $push: [“y”] }); // [‘x’, ‘y’]

console.log(state1, state2);
console.log(`state1===state2: ${state1===state2}`);
输出结果:

同时也来熟悉下 update 方法的使用:update 方法接受两个参数,第一个是源数据,这个很好理解;第二个是操作线 (笔者 YY 出来的),用来描述我们如何去操作源数据,key 是指令,value 是指令所需要的数据。
$unshift
unshift 的作用就是向源数组的开头批量添加元素
var update = require(“immutability-helper”);
const state1 = [“x”];
const state2 = update(state1, { $unshift: [“y”,”Z”] });

console.log(state1, state2);
输出结果:

$splice
splice 的作用就是向源数组中添加 / 删除元素。参数接受多个数组,每个数组为一组操作。每一组与实际 splice 方法的参数相同。
var update = require(“immutability-helper”);
const state1 = [0, 1, 2, 4];
const state2 = update(state1, {
$splice: [
[3, 1, 3, 4, 5, 6, 7]
]
});

console.log(state1, state2);
输出结果:
$set
set 指令被用来改成字面量对象中的某个 key 的值。
var update = require(“immutability-helper”);
const data = {‘id’: 0, name: ‘xiaoming’};
const data2 = update(data, { name: { $set: ‘Miss Li’} });
console.log(data, data2);
输出结果:

$toggle
toggle 意为切换,该方法是用来对 Boolean 对象进行切换,比如 True 切换为 False
var update = require(“immutability-helper”);
const data = [true, false];
const data2 = update(data, { $toggle: [0] });
console.log(data, data2);
输出结果:使用方法如上,针对数组中的第 1 个 Boolean 值做切换处理。
本篇先介绍这 5 个指令方法。剩下的 5 个我们下一篇继续。接下来我们尝试从上面这 5 个指令中找出相应指令去解决我们前面提到的表格数据的问题:首先是表格的数据:
[
{‘id’:0,name:’xiaoming’},
{‘id’:1,name:’lilei’},
{‘id’:2,name:’hanmeimei’},
]
可以分析出我们需要对两中对象进行处理,一个是数组,一个是字面量对象。然后假如我们修改了 id= 1 的那条记录的‘name’属性,‘name= 张伟’,该怎么做?首先第一步我们要找到 id= 1 的那条记录的 index,这里是 1;然后是确定需要更改的字段和更改后的值。
var update = require(“immutability-helper”);
const data = [
{‘id’: 0, name: ‘xiaoming’},
{‘id’: 1, name: ‘lilei’},
{‘id’: 2, name: ‘hanmeimei’},
]
const data2 = update(data, { 1: { name: { $set: ‘ 张伟 ’} } });
console.log(data[1][‘name’], data2[1][‘name’]);
console.log(`data===data2: ${data===data2}`);
输出结果:

这样就实现了在源数据的基础上更改了值并且输出一个与之地址完全不同数组。

正文完
 0