Immutable.js 到底值不值得用?

导语

我是一个前端开发人员,拥有四年工作经验,目前在一个大型软件团体里工作,制作一个以React框架和Redux库为基础建立起来的新单页程序。

创作一个前所未有的网站,这对所有开发人员而言都有着令人兴奋的前景。我们会天真地眨着大眼睛,满满地抓起一把新技术,把它们全投入到这个node平台服务器上去,再抽身引退,对自己那领先时代的天赋惊叹不已。

选择的技术之中,有一个是Facebook公司的Immutable软件库。我们准备利用这个库来实现数据的表现方式,加强数据的不可变性(immutability),以此为开始,建立起面向功能的编程模式。这篇帖子就是要对其进行一次审视。

不可变数据与Redux库

不可变数据是面向功能编程(functional programming)的核心概念,这种概念在JavaScript中的应用已渐占优势。使用React框架和Redux库时,不可变数据能帮助巩固这两者的核心原则:如果程序状态(app state)没有发生改变,那网页的文档对象模型(DOM)也不用改变。

不少文章已经写到过使用不可变数据的优点,主要包括:

简化贯穿程序的数据流

不再需要数据复制的防御机制

优化对数据变化的检测

通过记忆化(memoization)技术提高程序性能

Immutable库

Immutable库是Facebook公司的一个开源软件库。我们使用redux-immutable模块将这个库整合进我们的程序,这样我们就能以Immutable库提供的数据类型来存储程序状态(app state)了。

要将程序状态(app state)渲染成网页,我们得把状态数据从Redux的存储对象(store)中转移到React组件里去。这是通过react-redux模块的“connect()”修饰函数来实现的。

在程序开发过程中,我们注意到了以下优点和缺点。

[优点]强化了不可变性

不管选用哪个库,使用不可变数据类型的头一条理由肯定是能够保证做项目的人不能违反不可变原则。

严格地说,Immutable库有助于简化开发过程,因为大家不再需要在代码中追踪数据,寻找数据变更的位置。不可变数据类型取而代之,能始终精确表现当前存储对象(store)中存储的程序状态(app state)。

有了这个库,我们就能发挥上述不可变数据类型的优点,似乎没什么不好的。然而,缺点也确实存在,而且等到开发工作正式开始时,这些缺点才显露了出来。

[缺点]文档与调试

Facebook给前端开发人员提供的不仅仅是一个软件框架,而是整个程序制作的软件生态系统。然而,和React之类的框架比起来,Immutable库的文档极其不完整。

不清楚Immutable库句法,或者代码无法像预想的那样起作用时,开发人员都会求助于文档,不过常常是看了还不明白。代码为什么不对?既然看了还不明白,最终大家都会使用终端日志console.log()大法。不过很可惜,用日志审查数据时会发现自己一直在自定义数据类型的属性里翻来翻去。

终端日志打印出来的Immutable库对象

要解决这个问题,可以在任何Immutable库的对象上调用toJS()函数,把对象转换成一个纯JavaScript对象,再打印出来。但这类小问题会减缓开发速度,要是文档能再完善点,情况就会更好些。

不管怎么样,如果仅仅为了确定当前有什么数据就要看文档、作调试,那作为制作程序的基础来说真不怎么样。

[缺点]有反模式化的酸腐气息

我们可以通过connect()修饰函数,从程序的存储对象(store)中取得数据,以此访问Immutable库的数据对象。但我们团队以前通常会用原生数据类型写组件。为了转换数据,我们开发了一个模式,在connect()修饰函数中用了toJS()函数,如下所示:

// 从存储对象(store)里获取数据

[@connect](http://twitter.com/connect"Twitter profile for @connect")((state)=>{

// 将存储对象(store)数据转换成原生JavaScript对象

user:state.get(”user“).toJS(),

wine:state.getIn([”drinks“,”wines“]).toJS()

})

classHelloWineextendsComponent{

render(){

// 用ES6版本格式把属性(props)里的数据解构出来

const{user,wines:{houseRed}}=this.props

return{`Hi${user}! Fancy some${houseRed}?`}

}

}

这个模式看起来很方便也很安全,但用在移动设备上时,我们发现启动Redux的行为(actions)功能慢得受不了。下面是在三星S5上打开程序侧边菜单时记录下来的JavaScript性能剖析。

低效渲染剖析

打开菜单用了2秒多,对一个用前沿技术做的网站程序来说可不怎么爽!

我们对程序运行进行了追踪,发现上面写的那个模式就是问题所在。在后台发生的情况是Redux把行为对象(action)发送到存储对象(store),然后用reducer()函数产生的新状态(state)更新存储对象(store)。

组件用connect()函数修饰以后,每次都会检查数据是否更新。数据有更新,组件才会通过React生命周期触发重渲染。这使Redux库能选择性地渲染React框架组件,提升性能。

每次运行connect()函数时,通过toJS()函数,程序状态(app state)都被转换成了一个原生JavaScript对象,每次都会产生一个新的对象。因此和之前的状态相比,即使当前的Immutable库对象没有变化,产生的对象仍然是不同的。换句话说,任何行为(action)发动时,每个用connect()函数修饰的元素以及子元素都会被重新渲染过。

如果别的都记不住,那记住这点:toJS()函数绝对不要在connect()修饰函数中调用。

[缺点] 不怎么符合ES6版本的格式

如果程序状态(app state)存储在Immutable库数据类型中的话,那我们的组件也应该运用同样的数据类型,就这样决定了。于是我们照此重组了代码,却产生了一个很大的缺陷,那就是原生功能的缺失。

比如,ES6的解构(de-structuring)功能现在就变成了几个get()函数和getIn()函数调用的结合。

const{wines:{houseRed:{name,year}}}=this.props

// 变成

const{wines}=this.props

constname=wines.getIn([”houseRed“,”name“])

constyear=wines.getIn([”houseRed“,”year“])

代码变长了,没那么漂亮了。而且个人而言,我不喜欢用那么多字符串,因为如果打错一个字,本来程序会抛出JavaScript错误,提醒错误所在,现在能得到的只是一个undefined,而真正的问题可能无法发现。

另外,ES6版本的展开句法(spreading)功能也丢失了,这会使属性重新赋值的语句变得很冗长。

<AmazingComponent{…props}/>

<AmazingComponent prop1={props.prop1}prop2={props.prop2}prop3={props.prop3}/>

这些Immutable库句法的缺点触及到了我们的痛处,又让我想起了为什么一开始要做那个转换模式。如果核心数据类型处理出了问题,即使都是些小问题,也会让人感到沮丧,又会浪费更多宝贵的开发时间。

结果

为了使用Immutable库,我们重组了一些组件的代码。然后我们重新评估了目前所处的局面,讨论了上面讲的那些方面,结论就是Immutable库唯一的好处就是能强化不可变性,但意义何在?面向功能编程真正的意思是大家不要尝试去修改状态,所以状态的具体数据类型是不是可变只是个技术问题,和思路没有关系。

在使用Immutable库过程中我们考虑了所有的缺点,最终决定把它从项目里完全移除。只要遵循面向功能编程的原则,我们就有信心处理自己的数据。我们对开发人员的信任,加上相互之间的代码审查已经足够保证不犯低级错误了。

感谢阅读,希望有所帮助!

本文来自企鹅号 - 爆米兔精选媒体

发表于

我来说两句

0 条评论
登录 后参与评论

相关文章

来自专栏python3

面向对象作业——校园管理系统

2. 创建linux , python , go 3个课程 ,linux\py 在北京开,go 在上海开

1472
来自专栏PPV课数据科学社区

据说这篇总结覆盖了一般Python开发面试中可能会问到的大部分问题

? 原文标题:一名python web后端开发工程师的面试总结 先介绍下我的情况 通信背景,工作一年多不到两年。之前一直在做C++的MFC软件界面开发工作。公...

4886
来自专栏编程之旅

微信小程序——使用setData修改数组中的单个对象

微信小程序已经出来挺久的时间了,之前只是在文档上粗略的看了一下,最近稍得空闲,便利用微信小程序平台写一个练手的项目,顺便学习一下小程序开发,感觉大体跟前端开发基...

2402
来自专栏编程之路

7天从0到一个完整的小程序

如果你熟悉公众号开发的套路,那么入手小程序就很快了。如果你熟悉app开发,那入手也是很顺畅的,我认为关键在于移动开发思想。

2617
来自专栏从零开始学自动化测试

新手学习selenium路线图(老司机亲手绘制)

前言: 最近群里有不少小白,想入手selenium,但是一直没找到学习路线,还没入门就迷路了,于是小编亲手绘制了一幅学习路线图。希望能帮助小白快速入门,帮助已经...

3866
来自专栏牛客网

CVTE 前端一面面经

【每日一语】在年轻的时候,在那些充满了阳光的长长的下午,我无所事事,也无所惧怕,只因为我知道,在我的生命里有一种永远的等待。挫折会来,也会过去,热泪会流下,也会...

773
来自专栏数据之美

深入分析诡异的 Excel 求和统计缺失问题

1、背景 昨天有同学在用 Excel 做数据统计时偶然发现 Excel 会少算一些数据,而且这个坑让这位同学排查了很久才确认不是自己统计程序错误而只是 Exce...

21610
来自专栏程序员宝库

我的编程之路:知识管理与知识体系

本文的资料放到了Github Repo(https://github.com/wxyyxc1992/Coder-Knowledge-Graph)(本文介绍的这种...

3565
来自专栏程序员笔记

Unity3D入门:做个第一人称射击游戏

6087
来自专栏IMWeb前端团队

Typescript: the Good Parts

本文作者:IMWeb 杨文坚 原文出处:IMWeb社区 未经同意,禁止转载 Typescript: the Good Parts Douglas C...

1989

扫码关注云+社区

领取腾讯云代金券