Code and Life

  • 这里记录了一些我关于技术的总结。如你所见,这些总结通常包含很多个人的偏见和谬误,不过这些偏见和谬误也正是学习的一部分。大约由于工作内容的原因,这里汇集了软件开发方法论,自动化测试,代码整洁等比较抽象的内容,又有诸如测试驱动开发,重构等等理论与实践并重的内容,以及一些零散的工具使用介绍之类。
  • 2018年之后,我的很多技术总结也会以英文发布在medium上,不过我通常也会随后将其翻译成中文并记录在这里。

给你点颜色看看 - 上篇

给你点颜色看看 - 上篇 毫无疑问,正确的色彩运用可以让一幅设计作品快速且有效的传递出设计师想要传递的信息。它可以唤起人们的某种情感,可以引导视线使其聚焦于特定元素,也可以传递美感,提升设计的亲和力等等。 同时,在视觉设计中色彩又是一个相对比较难的主题。虽说没有万能的配色方案,但是大部分受使用者还是可以轻松的区分一个良好的,令人愉悦的配色和一个不那么好的的配色。那么是什么让一组色彩比另一组更有效(好看)呢? 在这篇文章中,我想要讨论一些关于色彩的基本原理,一些在实际项目中比较保险(未必非常酷炫,但是比较安全)的配色方法,以及一些工程中的实践(比如accessibility等)。通常来说,大部分人都可以相对轻松的觉察出良好的配色方案,或者说很容易对比出配色方案A比方案B更加的“和谐”。而另一方面,当需要自己从头设计出一个合理的配色时,则往往会不知如何着手。 这篇文章大约不太可能帮你找到五彩斑斓的黑,但是应该可以避免你落入“红配绿,赛狗屁”的窘境(以及如果被要求这么做的时候如何正确应对)。 颜色是什么 原色 有科学研究表明,人类之所以可以进化出远超过其他任何动物的智能,与其视觉能力 – 特别是能够辨别足够多的色彩的能力有着极深的关系。 能够分辨红色的成熟而充满糖分的水果与未成熟营养物质较少的水果,在某种程度上让灵长类有了更大的进化优势。人类大脑更是有超过一半的皮层用来处理视觉信息。能够辨别色彩斑斓的捕食者与丛林中灌木的颜色 – 即使在光线比较暗淡的情况下 – 更是我们祖先的基因得以延续的重要原因(毕竟,无法区分狮子和树干的颜色的基因已经被自然选择所淘汰)。 我们平时所说的颜色是指人眼可见的,在一个波长范围内的电磁波(360 - 400 纳米 ~ 760 - 830 纳米)。这些电磁波通过眼睛,在视锥细胞的作用下产生电信号,并在大脑中产生的不同刺激而产生色觉。自然界处于可见光频谱之外的电磁波更多,只是人眼/人脑对它们没有感应而已。 如果按照频率的顺序将这些颜色排列出来,我们会得到这样一条线: 而如果将这条线弯曲成一个环,则会得到我们经常见到的色轮/色环: 颜色的表示 在诸如显示器、手机等屏幕设备上,人们发现通过将红(Red),绿(Green),蓝(Blue)三种光通过不同比例的混合,就会在让人“感觉”到不同颜色。每种原色的光可以有不同的亮度等级(0-255),这样的组合可以产生256*256*256 种不同的颜色。通常我们使用16进制或者10进制来表示其亮度,记为#004c97或者rgb(200, 38, 118)等。RGB是目前是一种应用广泛的颜色表示法。 和电子屏幕对应的,CMYK(青Cyan,品红Magenta,黄Yellow,黑Black)颜色表示法则多用于印制品(比如纸质海报,彩色书籍等)。通过将3种基本颜色的油墨和黑色混合即可以得到足够多的混合色。相对于RGB,CMYK的可以表示的颜色要少一些(101*101*101),因此印制品的颜色也会显得没有那么多彩。 应该注意的是,这两种颜色模式的呈现方式完全不同。屏幕设备会自主发光,通过将光叠加起来会得到希望获得的色彩(比如三种原色叠加在一起时结果为白色)。而对于印制品,由于颜色是不同颜料对白色的反射光(而不是自主发光),因此不同颜色相当于互相抵消了(比如三种原色CMY叠加在一起时结果为黑色,即吸收所有光而没有反射)。 对于屏幕颜色来讲,RGB并非唯一的色彩表示法。另一种在计算机种经常使用的颜色表示法为HSL或者HSV。H表示色调(hue),S表示饱和度(saturation),L表示明度(lightness)或者V表示色值(value)。相对于RGB,HSL更容易和我们的自然语言联系起来。 比如问一个熟练的Web开发者#3c4492是什么颜色,九成九的回答是不知道(这就好像在问将60份红光,68份绿光和146份蓝光混在一起,是什么颜色?)。而HSL则不同,它通过一个角度和两个百分比来表示颜色,角度是指在色轮上的位置(0度为12点钟方向,颜色是红色,然后依次红橙黄绿青蓝紫,360度再到红色),第一个百分数为饱和度,0为饱和度最低,而100为最高;第二个百分数为明度,0为黑色,100最亮。所以hsl(0, 60%, 50%)大约是一种略显暗淡的红色,而hsl(180, 50%, 50%)则为青色等。 色彩的属性 我们在本文接下来的部分会专注于屏幕色彩的讨论。 色调/色相(Hue) 三原色相邻的两种混合,即可得到6种颜色。在得到的六种颜色再相邻的两两混合,即可得到12种。将这个过程不断重复,就会得到整个色谱。可以想象这个色谱是一条线,线上的每一个点都是一种特定的颜色(hue),也就是我们通常说的颜色,是为hue。当然,日常生活中,我们说到红色可能并不是一个点,而是这条线上的一个区间。 此外,我们通常说的颜色并不是饱和度最高的色调,而往往是有很多层次的。比如对于绿色而言,有墨绿,深绿,浅绿,淡绿,翡翠色等等,这些绿色可能背后对应的色调/色相都是同一个。 饱和度(Saturation) 如果往某一种颜色hue中逐步加入白色,则该颜色会逐渐变淡,直到完全变白。应该注意的是,在整个混合的过程中,色调并没有变,只是饱和度在逐步降低。比如混合了部分灰色的红色依然是红色(在自然语言中将其成为淡红)。 明度(Lightness) 类似的,往某一种色调(Hue)中逐步加入黑色,则该颜色会逐渐变暗,直到完全变黑。 我们可以通过一个例子来详细说明。在下图中,右上角是色谱上的一个点,也就是一个特定的色调(hue),通过向其中加入白色,饱和度逐渐降低,直到最后变成纯白色(越往左颜色越淡,饱和度越低)。另一方面,如果向该色调中加入黑色,明度(或者叫颜色值)则逐渐降低,直到最后变为纯黑色(越往下颜色越深,明度越低)。 应当注意的是,在白色和黑色之外还有大量的灰色值 - 即同时调整饱和度和明度,这样就会得到更多的该色调的变化。也可以这么理解,在色环/线上的任何一个点,展开之后(通过调整饱和度和明度)都对应了一个面。 色值(Value) 另一个与容易和色彩的明度混淆的概念是色值。即使在饱和度最高,明度最高的颜色,色彩之间还是有明暗差异的,这个就是色值。 比如下图中,由于色调的存在,我们事实上很难看清那种颜色更亮(有时候甚至最引人瞩目的颜色反而比较暗,或者说色值比较低): 如果将上图不做任何其他编辑,仅仅转化为灰度图: 可以看到,红色几乎和蓝色一样暗,但是在彩色图片中红色显得更加亮一些。 此外,颜色是很多时候都是一个相对概念,即人眼无法精确区分颜色,而且还善于从上下文的对比中得出错误的结论。比如在著名的灰色棋盘中,A点和B点的色值事实上是一样的,但是由于阴影的原因,大脑错误的假设,由于影子的存在,B点应该比实际上更亮(感兴趣的同学可以用color picker动手试一下)。 色彩的作用 色彩在设计中有着非常广泛的用途。通过不同色彩可以将平面划分为不同的部分(比如不同section具有不同的背景色),可以将元素归位一组(比如高亮选中的行),色彩的对比可以使用户的视线聚焦在需要强调的元素(比如会立刻引起用户注意的下单按钮)等等。 强调特定元素 我们来看一个网页设计,如果将其中的颜色去掉: 由于灰度图的颜色仅有256种,相较彩色图片,很多细节被拍平成了一种颜色。而如果将颜色加回去的话,我们的注意力瞬间就会几种在Try it for free这个按钮上。 如果我们希望强调页面上的其他元素,只需要选用某种颜色即可使之与背景分离。...

August 26, 2021 · 1 min · 邱俊涛 | Juntao Qiu

给你点颜色看看 - 下篇

给你点颜色看看 - 下篇 [[给你点颜色看看-上]]主要讲述颜色的定义,以及颜色在设计中的作用。在下篇中,我们从实践层面来讨论如何搭配颜色,并在实际场景中正确运用。 配色 接下来的一个重要主题就是配色了。在多个世纪以来,艺术家们发现某些色彩和其他色彩一起可以工作的很好,而另外一些色彩的组合则最好不要出现在同一幅作品中。 这里我们可以一起讨论几种常见且安全易用的配色方案。 单色配色(Monochromatic Color Scheme) 单色配色,顾名思义,是一种只用一种色调,但是通过使用不同饱和度和明度的配色方案。它是最简单的一种配色,我个人最喜欢的一种配色法。 比如: 相似色配色(Analogous) 和单色类似,相似色配色选用在色轮上相邻的颜色。由于自然界中存在大量的类似色彩场景:比如树林中不同层次的绿色,黄昏时天空中从红到橙再到黄的自然渐变等等,因此相邻的颜色天然就会比较和谐。 应该注意的是,虽然是相邻,但是颜色之间需要有足够的距离,以示区分。 或者 通常来说,在选择相似色时,应当避免使用超过4个颜色,通常3个就已经足够。 应当注意的是,和单色对比,相似色(右图)通常可以让画面更加立体,而单色则会略显扁平(左图)。 互补色(Complementary) 互补配色是指在色轮上处于对立的颜色,如果通过色轮的圆心做一条直径,则直径两端的色彩互为互补色。这种配色在需要做出较强烈对比的时候尤其有用。 比如非常常见的蓝色/黄色,红色/绿色的组合,就是互补色的典型场景。下图中有两组互补色,比如宜家就采用类似左侧的蓝黄配色。 使用互补色的一个关键点是避免使用饱和度过高的色彩,我们可以通过调整饱和度和明度来得到颜色的变种(variation),来中和强烈的对比。 比如在上图中,第一行和第三行在采用相通的色调的前提下,通过调整饱和度和明度得到一些相对比较和谐的组合。此外,如果将一个颜色的饱和度降低,同时可以减少另一个色彩的明度,从而形成比较合理的明暗对比,使得内容的可读性更高。 比如下面的几组对比中,我们可以很好的看到不同组合所带来的视觉差异: 通过降低红色球和背景的的饱和度,即使红配绿这样的组合也可以比较和谐。 或者反过来,将降低饱和度的绿色球于更暗的红色放在一起: 三角配色(Triad) 在色轮上画一个等边三角形,则三个顶点位置的颜色组成的即为选定的配色方案。由于有三种颜色的参与,三角配色需要有不同的明暗对比和饱和度变化才不至于凌乱。通常我们可以重点突出一种主色,其他两种颜色辅助。或者一种浅色作为背景色,一种深色作为前景色,第三种为高亮色(用于特别强调少量细节)。 比如,如果直接从色轮上选取等边三角形上的颜色,得到的配色会互相争夺注意力。下图中的两个配色方案都有这样的问题: 通过调整明度和饱和度,我们可以得到更合理的配色: 或者替换主色和辅助色: 以上列举的是几种最常见的配色方案,除此之外还有众多的变化。比如补色分割(Split-Complementary)是对三角配色和互补配色的一个合并。四角配色(Tetradic)则使用一个矩形而不是三角形来进行色彩的选取。 安全用色 借鉴优秀作品 毋庸置疑,最好的学习方法就是借鉴优秀作品(但是一定不要照抄)。很多设计网站,诸如dribbble,behance以及designspiration 等都有大量优秀作品。 比如在dribbble上,你还可以通过颜色(比如搜索所有蓝色的设计)来进行过滤,来学习其他设计师如何配色,以及如何调整颜色的参数来让设计更加和谐。 使用图片生成调色板 通过使用一些工具将摄影作品中的色彩方案自动抽取出来,往往会得到极好的效果。这是因为自然界中的众多颜色都为我们的大脑所熟知(碧水蓝天的海滩,层林尽染的秋林等等),而好的摄影作品往往已经经过后期专业调色,通常可以得到很好的效果。 我通常会在诸如unsplash或者pexels上找到合适的照片,然后上传到Adobe Color,然后按照需要的类型进行自动抽取即可: 当然你无需止步于此,你可以将生成的配色作为基础,进行饱和度,明度的调整,满足项目实际的需求。 关注对比 正如我们在讨论颜色的作用时提及的,对比永远时设计中的重中之重。有意思的是,很多视觉设计师都是从灰度图开始设计的。使用灰度图的好处是更容易将需要强调的元素标记出来 - 如果可以在灰度图上做好对比 – 那么最后的上色则事实上会轻松很多。 下面这个图中,在对应的灰度线稿上(线稿是我在网上找的,我只是做了一点点配色),我采用了两套完全不同的配色,但是需要强调的城堡的拱门始终处于焦点所在: 运用中性色 除了鲜艳明亮的色彩之外,实际中更多是略显平稳的中性色。比如不同级别的灰色,棕色,土黄色等。通过与饱和度较高,色值较为明亮的色彩配合使用,不但可以使得可用性增强(比如内容的色彩的对比度高时更容易阅读等),还可以使得整体配色得到更好的平衡。 要得到和谐的中性色,最简单方式是通过调整主色的饱和度和亮度(比如低饱和且高亮度的作为亮的灰色,低饱和且低亮度的作为暗的灰色)。另一个简单的方式是通过使用一些预设的灰色(一个亮灰色,一个暗灰色),然后通过图片的混合模式来生成不同的组合(这里介绍了一种使用Photoshop来操作的教程)。 主次之分 另一个和对比相关的重点是分清主次。无论采取的配色方案是那种,都应该避免采用过于饱和的颜色。因为饱和色通常以为着强调,如果有超过一个的高饱和色,他们就会互相干扰。就好像我们经常说的,强调一切恰好意味着什么都没有强调。通过饱和度和明度的调整(另外在一些场景下还可以设置透明通道)则可以着重以一种颜色为主色,而其他颜色的存在是为了衬托。 一个简单的法则是,在熟练运用颜色之前,尽量将色彩控制在2个以内。比如我们在互补色配色小节讨论的蓝黄配色方案中,如果以蓝色作为基调(主色),黄色为高亮色,我们可以搭配一些中性的灰色来体现一些差异即可形成一个有5种颜色的调色板。 同样的方法,加入中性的灰色之后,我们将红绿配色调配成: 总结 本文讨论了数种最常见的配色方案,从单色到三色的组合,如何通过调整饱和度/明度等来中和多种色彩间潜在的竞争,如何通过调整颜色的主次来达成平衡,以及如何应用这些方案来定义自己项目所需的配色。最后,我们列举了一些安全用色(如何保险的使用颜色)所需的技术细节。 通过使用这些技术,结合之前的色彩基础理论,以及不断的练习,我们就可以比较容易且安全的选出高效且和谐的配色方案。 P.S. 这篇文章是对我在最近一段时间学习设计理论,特别是关于色彩的使用的一个回顾。事实上早在《三周三页面》的后期,我就开始仔细学习色彩理论,后来也偶尔会看一些资料(诸如色彩心理学之类)。但是直到几个月前,我在学习udemy上的一个设计课程的时候,突然将很多关于色彩的理论和实践都连接了起来,那是一种特别有趣的体验。就好像乔布斯有一次在演讲里提到的,你无法在事先看到这些知识的联系,直到某一刻,忽然你就将所有珠子串起来,形成了一条项链。希望这篇文章可以成为某些人那个的触发器。 P.P.S. 其实这本来是一篇文章,但是囿于篇幅,又不忍心删节,所以就拆分成上下两篇。另外,邮件的长度好像也有限制,但是拆分后可以好一些。

August 26, 2021 · 1 min · 邱俊涛 | Juntao Qiu

前端性能调优心法

前言 性能问题是软件开发中的常见问题,我们在几乎每个项目在某个时期(往往是在后期快要交付的时候,或者已经上线以后收到用户反馈)都或多或少会遇到。这篇文章想要从流程方面和具体的技术细节上对软件性能优化上遇到的问题做一些总结和分类,以方便在后续类似的场景下可以提供给开发者一个参考。 严格意义上,这篇文章并没有太多的新内容,甚至有一些具体的技术细节我在另一篇文章中已经讨论过,这里主要还是提供一些常见的关于性能优化思路的总结。 在修改之前 性能优化之法,曰立,曰测,曰理,曰拆,曰分,曰剥,曰拖,曰缓。 我们讨论性能提升,往往需要首先建立一套测量机制。因为仅凭直觉来猜测可能的性能瓶颈非常低效,而且往往直觉认为有性能问题的地方未必真有问题。一旦测量机制建立,则犹如我们代码有了单元测试/集成测试的守护,总的来说会向着正确的方向演进。 立字诀 重中之重的是,定义好指标。即DoD(Definition of Done),我们需要回答的问题是:什么是好的性能?达到何种标准就算是提升,而达不到就算是失败?这一点从项目的确立角度非常关键。 如果说希望某个页面的性能较之以前来说,加载时间提高20%为成功,则一切的后续开发可以做到有的放矢,而不至于无疾而终。 测字诀 一旦我们定义好了何为提升。接下来就需要建立相应的测量机制,并设置基线。这一步相当于将上一步定义好的标准实例化到build pipeline中,使得具体目标可视化起来,从而每次的修改都能看到和目标的差距。 比如从请求发出到页面渲染完成(比如检测到某个标的在页面上的存在与否),总共耗时3秒,然后我们将3秒设置为基线,并围绕这个基线设置测试的上限。和其他测试一样,如果后续的代码修改使得页面渲染时间大于基线值,则build失败。与之对应的还可以有诸如bundle的尺寸(压缩后的静态资源大小)首次渲染时间等等指标。 有了具体的目标,我们就可以设置相应的测试机制。比如通过运行yslow或者其他lighthouse来进行。 理字诀 当我们定义了性能优化成功的含义,也有了相应的反馈机制,如何做才会成为最重要的主题。对于这个问题,常用的工具就是分析和分类。 首先需要的分析“慢”的类型,是纯性能问题,还是架构问题,或者是软件设计上的问题。纯性能的问题往往较为具体,也最容易解决,比如使用了性能较低的包作为依赖,则只需要替换为性能更好的库即可;又或者使用debounce/throttle来减少对函数的频繁调用等等。 与纯粹的性能问题相对应的另一大类问题,都可以归结到设计问题(大到软件架构,小到模块间的耦合/依赖等问题)。这类问题通常需要引入的修改比较大,但是收益也会很高,而且长期来看,对于代码的可维护性和缺陷率也会带来好的回报。 因此,这一步的目标是识别出哪些问题可以通过简单修改就可以达成,而另外的一些则需要大的改动。事实是,有可能对于我们之前定义好的基线,只需要解决纯粹的性能问题就可以达成,那我们也无需花费大量的工作在更大的修改上。 总纲 或曰,性能优化之诀窍,唯推拖二字也。推者,不是我的事儿我绝不干,谁爱干谁干。拖者,能明天做的事儿,今天绝不去碰。 如果纯粹的最佳实践无法满足要求,我们则需要花费更多的时间来重构代码的设计来满足性能需求。 我们将通过一些具体的例子来仔细讨论。总的来说,我们需要识别代码中的耦合问题,并在合理的方向上进行抽象,并完成拆分,使得每个独立的模块/组件都尽可能的高内聚,低耦合。 拆字诀 比如在文中讨论的Avatar和Tooltip的例子,头像组件Avartar的核心功能并不包含Tooltip,而且两者的耦合程度其实很低,可以通过拆分的方式将其隔离。 修改后的Avatar不再将Tooltip做为依赖: import Avatar from "@atlaskit/avatar"; import Tooltip from "@material-ui/core/Tooltip"; const MyAvatar = (props) => ( <Tooltip title="Juntao Qiu" placement="top" classes={...}> <Avatar name="Juntao Qiu" url="https://avatars.githubusercontent.com/u/122324" /> </Tooltip> ); 分字诀 在另外一些情况下,一个组件和其依赖间的耦合较为紧密,但是又不具备不可替代性。比如在文中讨论的InlineEdit和InlineDialog的场景。 这时候可以通过render props来进行控制反转,使得组件不再依赖于某个具体实现,而是一个接口。这样所有实现了该接口的组件都可以即插即用,又可以节省默认依赖的部分开销(定义在package.json中的)。 注意这种场景和“拆字诀”里的场景非常类似,不过区别是这里拆分出去的组件和当前组件间有一个隐式的协定:即需要接受render传递过去的所有参数。 const MyEdit = () => { return ( <InlineEdit editView={(fieldProps, isInvalid, error) => ( <Popover open={isInvalid}> <Typography>{error}</Typography> <Textfield {....

June 16, 2021 · 2 min · 邱俊涛 | Juntao Qiu

组件设计之组合原则

前言 在过去的几个月里,我和客户团队在对一个组件库Atlaskit进行优化。表面上看起来这个优化工作包括两大部分:性能优化和结构重整。不过经过这几个月对十多个组件的重构之后,我们发现这两部分工作在很大程度上是同一件事的两个方面:好的设计往往可以带来更好的性能,反之亦然。 这是一个非常有趣的发现,我们在讨论性能优化的时候,一个经常被忽略的因素恰恰是软件本身的设计。我们会关注文件大小,是否会有多重渲染,甚至一些细节如CSS selector的优先级等等,但是很少为了性能而审视代码的设计。另一方面,如果一个组件写的不符合S.O.L.I.D原则,我们会认为它的可扩展性不够好,或者由于文件体量过大,且职责不清而变得难以维护,但是往往不会认为糟糕的设计会对性能造成影响(也可能是由于性能总是在实现已经完成之后才被注意到)。 为了更好的说明这个问题,以及如何在实践中修改我们的设计,使得代码更可能具有比较优秀的性能,我们可以一起讨论几个典型的例子。 头像组件Avatar 在Atlaskit比较早的一个版本中,头像Avatar组件有一个很方便的功能:如果给Avatar传入了name属性,则当鼠标悬停到头像时,头像下方会显示一个提示信息(Tooltip),内容为对应的name。 在实现中,Avatar使用了另一个组件Tooltip来完成这个功能: import Tooltip from "@atlaskit/tooltip"; const Avatar = (props) => { if (props.name) { return ( <Tooltip content={props.name}> <Circle> <img src={props.url} /> </Circle> </Tooltip> ); } return ( <Circle> <img src={props.url} /> </Circle> ); }; 这个功能本身并没有问题,不过当用户提出更多的需求后,我们就开始失去了对Avatar的控制。比如用户A希望鼠标悬停的时候,Tooltip可以显示在头像的上方。而用户B则希望可以定制Tooltip的背景色/字体/字号等等。 当然,我们可以开放一些新的参数给Avatar来实现这些需求,比如: <Avatar tooltipPosition="top" tooltipBackgroundColor="blue" tooltipColor="whitesmoke" />; 或者更进一步,开放一个选项对象: <Avatar tooltipProps={{ position: "top", backgroundColor: "blue", color: "whitesmoke", }} />; 然后在实现中我们将其透传给Tooltip组件。不过很快我们会发现这样的方式会带来一些问题: 由于Avatar依赖于Tooltip,打包后文件的尺寸会增加 如果用户需要以新的方式定制Tooltip,Avatar的接口也需要相应的更新 由于这个依赖,当Tooltip的API变化时,Avatar需要重新打包 而如果我们审视Avatar组件的话,会发现Tooltip对其核心功能(显示用户头像)来说,更像是起到了辅助作用,而并非不可或缺。比如,假设不使用Tooltip组件,我们可以把Avatar简化为: const Avatar = (props) => ( <Circle> <img src={props....

May 1, 2021 · 3 min · 邱俊涛 | Juntao Qiu

自动化重构 - jscodeshift

自动化重构(jscodeshift) 在这篇文章里我想要通过一些小例子来介绍使用jscodeshift来进行自动化重构的技术。具体来说,我想要介绍在一个组件库的开发和维护过程中,如何使用jscodeshift来自动修改公开的API接口,从而尽可能小的产生对组件用户的影响。 如果你们团队开发的组件被其消费者(组织内部或者外部)使用了,而这些代码又不在你的控制之内,那么这里讨论的技术和模式可能对你很有帮助。而如果你的日常工作更多的是使用组件库来开发应用程序,我希望这里的知识和技巧仍然对你有所启发,毕竟在软件系统中,我们往往都既是某些库的消费者,又同时是另外一些库的生产者。 从一个简单场景出发 设想这样一个场景,你发布了一个酷炫的组件库(fancylib),其中有一个按钮(Button)组件。这个Button的一个属性是当点击后处于加载中(loading)状态时现实一个表示加载中的小图标。 (图片来源:https://xd.adobe.com/ideas/process/ui-design/designing-interactive-buttons-states/) 在代码实现中,这个加载中状态被定义为了名为isInLoadingStatus公开prop。用户可以通过设置其值来控制Button的状态: import Button from '@fancylib/button'; const app = () => ( <Button isInLoadingStatus>Click me</Button> ) 一个实习生在某一天code review的时候提出了一个问题:在组件库中的其他地方,所有的boolean状态都是用一个单词来表示的,比如checked, disabled等。如果按照这个惯例,这里应该把isInLoadingStatus简化为loading。好主意! import Button from '@fancylib/button'; const app = () => ( <Button loading>Click me</Button> ) 假如所有用到Button的地方都在你的控制之内,字符串替换大约是一个快速且80%有效的方案。不过稍微分析一下,你就会发现简单的Shift+F6会遇到很多问题。 复杂情况 比如用户对其做了二次包装以适配更符合自己用户的使用习惯,这使得简单的全局字符串替换变成了不可能:: import Button as FancyButton from '@fancylib/button'; const MyEvenFancierButton = (props: FancyButtonProps) => ( const theme = { backgroundColor: "orangered", color: "white" }; <FancyButton {...props} theme={theme}>Click me</FancyButton> ); 除了这些问题之外,由于这是一个非常受欢迎的组件库,Button在很多(包括内部和外部的)产品中都有使用,你没有办法访问所有的用户代码,更没有办法让所有人都用手工的查找替换来做更新,你需要另寻出路。 你需要一个工具 – 一个可以读懂代码意图的工具 – 来帮助你做修改,而且整个过程最好可以自动化,比如通过执行一个脚本来完成。...

December 11, 2020 · 2 min · 邱俊涛 | Juntao Qiu