9

欧雷的周报:2021 年第 8 周

 3 years ago
source link: https://ourai.ws/posts/report-for-8th-week-of-2021/
Go to the source link to view the article. You can view the picture content, updated content and better typesetting reading experience. If the link is broken, please click the button below to view the snapshot at that time.

相对来说,除夕及刚过完年那几天略微「放纵」,没写一行代码。出去看了两场电影,分别是《唐人街探案 3》和《刺杀小说家》。

《唐人街探案 3》说实话看着不过瘾,里面还穿插着烂梗,如「东京热」,看得我一脸尴尬……结尾处几个「神秘人」好像是在英国(貌似看到了大本钟)开「圆桌会议」,还露脸了,神秘感荡然无存了好吗!反倒片尾曲给了我小惊喜,是南征北战NZBZ的《酷你吉娃》,因为从除夕前我就开始被他们的《骄傲的少年》所洗脑……

《刺杀小说家》比《唐人街探案 3》吸引我,头一次看到用这种虚实两个世界交替手法的中国电影,让我感到眼前一亮。并且动画部分做得质量也不错,从当前国产动画的主流采用 3D 形式来看,这方面的技能和积累不用担心。片中的那个「铠甲」让我想起了《 寄生兽 》中的「小右」……

BBFnEj.jpg!mobile 《寄生兽》漫画

这几天开始渐渐进入工作状态——上午八、九点起床,写代码到下午一、两点,玩游戏到晚饭前。

Q1 OKR

这周进展还可以,在 Petals 中加了 20 个以上的 UI 组件 interface。

看到这个数字,肯定会有人觉得:「才写这么几个就觉得还不错了?!而且还是 interface 而不是成品!」

我不是照搬 Ant Design、Element 或 Vuetify 等,每往 Petals 里加一个 UI 组件,我都要好好想下它的存在意义以及 API 如何设计;在进行 API 设计时又会纠结该有哪些,并查资料佐证我的想法——往 Petals 里加 UI 组件的过程,就是我重新理解交互并定义 UI 组件的过程,因此会花费更多的时间。

具体加了什么 UI 组件就不说了,主要分享下这过程中的一点思考——

受控结构生成

什么是「受控结构」?在这里是指「可被使用者自定义的受 UI 组件本身控制的内部结构」。它有别于子组件,会被渲染到 UI 组件内部预先设置的「指定坑位」——Vue 中具名插槽的内容就是。

具名插槽是个很是便利的机制,即便如此,作为一个跨环境的 UI 组件体系,要么在每个环境中都实现具名插槽机制,要么在 Vue 中舍弃具名插槽而另辟蹊径——显然后者是更明智的选择。

不考虑具名插槽,生成受控结构主要有两个手段:一是通过 UI 组件的属性传入数据结构或返回数据结构的函数,如所谓的「render prop」,常用于比较简单的结构;二是设计专门的 UI 组件,如 Ant Design 中的 TabPane 之于 Tabs ,主要在结构较为复杂时使用。

我之前说过,在设计业务无关的 UI 组件时应该注意——

UI 组件的属性值尽可能是简单数据类型,也就是数字、字符串等。

在 Ant Design 中,UI 组件的属性大量使用虚拟 DOM 节点,这在我的体系里是个反模式。

假如有个卡片(Card)组件,它的内部从上到下被划分为 header、body 和 footer 三个部分:header 中是标题、图标和一些操作;子组件被放在 body 内;footer 中则可以是操作或其他附加信息。默认情况下,header 和 footer 中是没内容的,所以连它们本身都不生成。

我会设计一个字符串类型的 title 属性用来设置标题,在使用者给这个属性传值后会生成 header。当使用者想在标题前显示图标,或在卡片组件的右上角显示一些操作时,该怎么办?

Ant Design 的做法是 title 属性接收虚拟 DOM 节点,这就可以传入一个视图结构了;同理,又弄了个接收虚拟 DOM 节点的 extra 属性去控制卡片组件右上角的结构。

在这里,我的做法是——设计一个专门的 UI 组件,姑且叫做 CardHeader ,作用就是让使用者能够完全自定义 header 中的内容;当检测到 CardHeader 时就忽略 title 属性。

为什么不是给卡片组件添加 icon 属性去控制图标的显示,设计 actions 属性传入对象数组去生成右上角的操作呢?

首先,如果把 iconactions 这两个属性加到卡片组件上,没看过使用手册的使用者在看到它们时,第一反应会想到它们实际是影响 header 部分吗?

若是加到卡片组件上,它们既可以被理解成在 header 里,又可以被认为是在 body 或 footer 里,因为这两个词不具备唯一性,不像 title ;反而加到 CardHeader 上比较合适。

其次,要是非要直接通过卡片组件来控制,就得加上限定词让人更容易理解,如: headerIconheaderActions 。但这样一来,结构控制都集中到了卡片组件上,它的属性将越来越多,这就又违背了另外一个原则——

UI 组件是什么?可以认为它是一个返回视图结构的函数,而 UI 组件的属性(prop)和事件(event)就是这个「函数」的参数。属性是 UI 组件的外部与其内部进行主动通信的数据,事件则是进行被动通信的回调函数。

一个封装得好的函数,它的参数应尽可能少,要想明白每个参数的语义,且必须确实有其存在的意义——UI 组件的属性和事件的设计也该如此。

在设计 UI 组件的属性时,先思考下要加的这个属性是不是属于这个 UI 组件本身的特性?若不是,那要加的属性的值所对应的 UI 组件的特性是什么?如果这两个问题都没有得到答案,那么这个属性可以不用加了。

再者,如上文所说,用复杂数据类型做属性值是要尽量避免的,即用数字、字符串等简单数据类型做属性值。虽然如此,但有时传对象或数组却可能会更合适些。

比如,有些 UI 组件会包含一些可自定义的操作,这种情况下再让使用者手动拼个视图结构的话,未免太不贴心了;这时若能通过传入一组代表操作配置的数据结构去生成一堆按钮,应该会更好些。需要注意的是,这个配置应当是(一定范围内)规范化的对操作(动作)的抽象。

综上所述,受控结构的生成优先考虑设计一个专门的 UI 组件,让属性列表尽可能简洁与整洁,把视图结构放回到它该待的地方;「不得已」时可以采用一个接收具有配置语义的数据结构的属性,如可能泛化的操作配置。

基础组件拆分

假设有道面试题:「什么样的 UI 组件算是基础组件?」你会如何作答?是「没有业务逻辑的 UI 组件」吗?那么,什么是「业务逻辑」?一个专门用来设置 cron 定时任务的 UI 组件是不是基础组件?

需要知道并承认的是,很多概念是模糊不清的,没有明确的边界的,无法被精确定义的——你能描述下什么是「桌子」吗?

影响一个 UI 组件是不是「基础组件」的因素之一,就是它所处的组件体系对「原子性」的定义。

相较而言,Vuetify 比 Ant Design 和 Element 更加原子化,因而更加灵活。但更为原子化就代表更好吗?这不一定。

在我的体系中,原子性主要体现在 UI 组件语义的唯一性上——不存在像 Input 这种,根据 type 属性的值改变了它实际语义的万能 UI 组件;也不存在两个语义十分相近,以至于让使用者产生很大疑惑的 UI 组件。

不是说没有相对泛化、抽象的 UI 组件,它们一般作为被继承的 interface、被组合的函数存在,而不是真正有视图结构的 UI 组件。

结语

终于又重操「主机游戏玩家」这个「旧业」了,还是有蛮多感慨的,并且在社交软件方面又有了几个「小」决定;由于本次周报在 Q1 OKR 部分占用了较多篇幅,就日后再说吧。

如果让我选择一个对他人、对社会没啥价值和贡献,在别人看来是「堕落」的活法,我就天天看动漫、打游戏。


About Joyk


Aggregate valuable and interesting links.
Joyk means Joy of geeK