immutability-helper 学习笔记 -1

原本想将有关于immutability-helper的博文放在一块儿学React系列博文中,可是考虑到该插件不只仅在React中实用到,因此就单独拿出来分两期写。git

发现问题

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中的持久数据集合保持不可变,从而提升效率和简单性github

仔细阅读他们对两个插件格子的描述,咱们能获得一个关键字,那就是不可变。为何要不可变?在这里笔者以为从React角度来解释可能会更加清晰。咱们都知道React组件自己会包含N个状态,并且这些状态会经过setState方法去更新,也就是说React组件的状态是可变的,实际上本就是这么一回事。可是做为React的初学者可能会误解状态是可变的这句话。怎么说?由于对于React组件而言,状态的变化是State的变化而不是State值的变化,什么?看不懂?那咱们继续往下说。假如咱们初始化了这样一个状态:npm

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这个状态的状况组件并无刷新的缘由,咱们用例子来证实下数组这一特性:app

let hobbies = ['qq', 'wx'];
let hobbies_2 = hobbies;
hobbies_2.push('Coding');

console.log(hobbies === hobbies_2);

能够看出这两个变量其实是同一个。
再举个例子而且是在平常开发中确定会遇到过的。假如如今要将后台的传来的数据渲染成一个列表,数据以下:ide

[
    {'id':0,name:'xiaoming'},
    {'id':1,name:'lilei'},
    {'id':2,name:'hanmeimei'},
]

这是一个很简单的操做。而后咱们作了一个更新的功能,当咱们将id为1的那条的姓名改为'Miss Li',那该怎么办才能保证在setState后让该列表组件从新渲染?估计你们内心马上有两个方案,一个是从新获取后台数据而后从新渲染,另外一个是从新拷贝一份数据而后更改相应的地方后从新set给某个State完成从新渲染。可是咱们能够看出,这两个操做最终的效果仍然是建立另外一个与初始数组在内容上彻底相同的数组,虽然相对繁琐可是着实有效。不过这两个方案各有缺点,好比第一个方案须要额外增长一次网络请求,第二个是万一数据量过于庞大就会形成内存的浪费。
既然这样有什么更好的解决方案呢?到这里我相信你们就明白了该篇博客的用意了,由于这个immutability-helper在笔者近期项目中使用最频繁,因此以为有必要拿出来讲一说。ui

immutability-helper使用方法

使用方法很简单,首先是安装依赖this

npm install immutability-helper --save

而后是在有须要的地方引入便可spa

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}`);

输出结果:

clipboard.png

同时也来熟悉下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);

输出结果:

clipboard.png

$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);

输出结果:
clipboard.png

$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);

输出结果:

clipboard.png

$toggle

toggle意为切换,该方法是用来对Boolean对象进行切换,好比True切换为False

var update = require("immutability-helper");
const data = [true, false];
const data2 = update(data, { $toggle: [0] });
console.log(data, data2);

输出结果:
clipboard.png
使用方法如上,针对数组中的第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}`);

输出结果:

clipboard.png

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

相关文章
相关标签/搜索