Immer.js原理探索学习

        一年没更新过了,各位客爷过的都怎么样,老菜鸟这一年经历了太多的事,感觉人一下子成熟了好多,连我的母上大人都说我现在的表达比以前好太多了。

        毕竟我们在任何处境下都要保持学习,由此进入正题吧,来讨论学习下关于Immer.js的前世今生。

认识Immer

        提到Immer.js就不得不提Immutable.js,在老菜鸟学习React的时候经常听到一个词叫“副作用”,后来才知道是React在操作复杂数组的时候,没有给数组做好充分的“拷贝”,并且结合React自身的diff比较缺陷,亦导致了在修改数组深层元素时,无法触发页面的刷新。(由此看来Vue各版本的diff做的是多好)

      (注:React的差异比较使用的是shallowEqual,各位有精力的话自行学习哈~)

        所以为了修复这个bug,Facebook的大佬们设计并实现了一套’可持久化数据结构‘。

        简单点就是在数据被修改的时候,我先保存一套下来,你改的只是我Copy出来的一份,所以页面做Diff刷新时,比较的就是两个不同地址的数组。

                                                                                                        ——前端装逼架构师 Yubble

        起初这个想法的具体实现是由Immutable.js来完成的,用过的客爷最熟悉不过了,其中提供了常用的数据类型,Map,Set,List等等,使用的时候用它提供的方法包裹下,经过各类数据结构及算法操作后,即可拷贝出一套新的数据出来。(没有JSON.stringfy,或者for循环递归这么简单哈,有兴趣的客爷还是要针对研究下)。

这是在下见过的说服力最强的不可变数据结构图,改变数据栈地址的同时兼容复用

        那么为什么又推出了Immer呢,两方面,性能和使用成本。

        1、虽说Immutable.js通过算法,降低了递归带来的时间复杂度,但是树形结构终归是对时间及空间复杂的一大考验,且Immutable.js针对不同的数据类型又实现了不同的拷贝方法,所以整体库比较大。

        2、其次最重要的方面,Immer用起来要比Immutable简单太多了

多嵌套数据更新对比图

        诸位,各位,在齐位,看下Immer的修改简单太多了吧,直接操作数据的分身draft即可,相比之下Immutable的fromJS,setIn,getIn都是什么鬼,不光是修改,连读取都是有心智成本的。学不完根本学不完。

探究Immer

        开玩笑的,该学还是得学,毕竟房贷还没还完呢......

        那么聪明又好学的你一定会问“Immer这么先进,它具体是怎么实现的呢。”

        我看到Immer的源码之后第一反应就是,哦~做替身。

秦桧经历过一次危险后,吓怕了,分裂出一个替身,之后不论是下达指令,还是行刺被杀,都是替身的活儿

        同样的,在Immer中,每个数据只要被更新过,就会分裂出一个分身,之后的千变万化,再怎么折腾也是在分身上,本体丝毫不受伤害,由此在React diff比对时能灵活的更新Dom。

        那么针对这个思路,我们可以模拟出几个角色了。

        首先是一个状态机,它应该包含数据本身,是否被修改,以及修改后的替身数据这三个成员变量。

用类方式实现,为什么不用返回成员字面量来实现,因为需要在类上绑上原型方法

        紧接着我们有了状态机,那么状态的修改查看在哪里实现呢,所以给这个状态机加上两个方法,分别是get和set。

我们通过set和get给状态机增加了两个对外Api,并实现了一个最简单shallowCopy,由此状态机就完成了

        其次我们开始考虑劫持器,这里源码中用到的是Proxy元编程,不熟悉的客官翻下我之前的博文哈。

        劫持的目标是谁?应该是我们之前包装好的状态机(createState)的实例吧,只有对他动手脚的话,他才能根据自己有没有被更新过而判断返回主体还是返回替身呢~

        而判断返回主体还是替身的这段逻辑,已经被状态机写好了(get, set, _markChanged),所以我们把对它的操作劫持下来,直接调用方法就好。

        有个细节老菜之前不明白,就好比[].push()这个方法,劫持算get还是set呀,事实证明,还是算get的,应该是说访问了数组的push方法,于是问题就来了,我arr[1]下表这种也算get访问,arr.push()也算get访问,那么怎么返回真正数据内容呢,所以人家在考虑的时候,也帮我们想到了。即用一个标识代表我访问了数据内容。

这是我的劫持器,target代表状态机的实例,最后也是调用状态机提供的get和set方法,注意这个target.get和target.set是我们在上面手动实现的哈,不是js自带的拦截。

        于是现在我们两大功能角色(状态机、劫持器)都准备好了,我们写个生产方法把它整合起来吧。

        生产函数做的事情更存粹,既为我们各个角色的整合,它的出入参分别是:

        入参:state,原始数据。producer,操作数据的回调

        出参:返回主体或替身数据

        看代码大致就是做了三件事:

            1、声明状态机,用劫持器代理他

            2、将被代理后的状态机抛出去,供开发者操作

            3、抛出一个数据供Dom渲染

        至此一个简约的Immer就搞出来了,我们可以用一下试试

写个todo list,给数组中新增一条todo:收拾屋子,并且将第二条:学习法律的进程置为:已完成,看下返回的两条数据

        可以看到新生成的nextState确实已更新出第三条todo:收拾屋子,由此可以触发React的Dom更新了。

        如果客官够细心的话会发现draftState[1].done = true这一条同样会影响到本体数据,这是因为Proxy的劫持也只能劫持到数据的第一层操作,如果是复杂数据层级较多的情况下,仍然需要使用递归嵌套的方式做逐层劫持,有心的客官可以研究下Proxy代理多层结构

        如果只谈劫持以及返回替身数据的话,我们的简易Immer.js够用了,但是要想达到性能优化的Persistent data structure,我们还缺少算法这一步,在下精力实在有限,这期先不研究了


吐槽时间

        再来说下React,老菜鸟之前一直是Vue系的,所以对Vue一直情有独钟,不过Vue2.0的缺陷确实也无可厚非,它的MVVM让开发者省了不少事,但OOP这种设计模式也确实让Vue2.0功能的单一性极其受限,节省了心智成本的同时也降低了抽象方法的可拓展性,所以Vue3.0也在向函数式看齐~

        但是React咱就说说呀,能不能把该你做的事情做完,不要老让开发者花费心智理解什么不可变数据,感觉用你开发附加的学习成本还是不小的,看看人家Vue3.0为什么从来没让开发者为这些事发过愁,再次站队一番Vue3~

        老菜鸟经过一番心理斗争,从当前公司选择离职了,毕竟身体和家庭在我心里是最重要的,没了健康挣再多的钱有什么用,奉劝各位量力而行吧,我们在资本面前都是耗材,能陪你走到最后的就是家人和这副伤痕累累的自己。

        最后祝各位能在这并不安稳的年代找到自己奋斗的目标与价值,有钱的捧个钱场,没钱的帮在下找个班儿上。

看下我家刘lucky的傻样
最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 204,445评论 6 478
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 85,889评论 2 381
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 151,047评论 0 337
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 54,760评论 1 276
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 63,745评论 5 367
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 48,638评论 1 281
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 38,011评论 3 398
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 36,669评论 0 258
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 40,923评论 1 299
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 35,655评论 2 321
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 37,740评论 1 330
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,406评论 4 320
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 38,995评论 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 29,961评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,197评论 1 260
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 45,023评论 2 350
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 42,483评论 2 342

推荐阅读更多精彩内容