最新公告
  • 欢迎您光临起源地模板网,本站秉承服务宗旨 履行“站长”责任,销售只是起点 服务永无止境!立即加入钻石VIP
  • 聊聊前端 UI 组件:组件设计

    正文概述 掘金(欧雷)   2021-01-28   390

    在本系列文章《聊聊前端 UI 组件:组件体系》中初步说明了 UI 组件的架构设计,本文将在此基础上进一步展开说说那篇文章中一笔带过的部分,并阐述在设计一个 UI 组件时应该注意的点有哪些。

    目录结构

    在《聊聊前端 UI 组件:组件体系》中列出的目录结构的基础上做了些许调整——

    component
       ├── demo                       # 示例相关文件
       │   └── ...
       ├── test                       # 测试相关文件
       │   └── ...
       ├── style                      # 样式相关文件
       │   ├── _functions.scss        # Sass 函数(可选)
       │   ├── _properties.scss       # CSS 自定义属性(必需),风格组件的一部分,供外部运行时自定义主题风格
       │   ├── _variables.scss        # Sass 变量(必需),风格组件的一部分,供外部编辑时/编译时自定义主题风格
       │   ├── _mixins.scss           # Sass 混入(可选)
       │   └── _rules.scss            # CSS 规则(必需),视觉组件,具有约束结构的作用
       ├── typing                     # 类型相关文件
       │   ├── custom-properties.ts   # CSS 自定义属性配置项(必需),用于运行时生成 CSS 自定义属性
       │   ├── aliases.ts             # 类型别名(可选)
       │   ├── interfaces.ts          # 结构组件接口(必需)
       │   └── index.ts               # 类型统一导出
       ├── HeadlessComponent.ts       # 无头组件,UI 组件与结构无关的逻辑
       ├── Component.vue              # 结构组件,受生成 HTML 的 JS 库/框架的源码、平台限定的视图结构描述语言影响
       ├── index.ts                   # 模块统一导出
       ├── changelog.md               # 组件变更记录
       ├── readme.md                  # 组件说明文档
       ├── metadata.yml
       └── package.json
    

    命名约定

    HTML & CSS class

    在基于组件开发(Component-based Development),即大家所说的「组件化」,在 web 前端领域普及之前,流行过一种神奇的 class 命名方式,可以说是一种方法论了——原子类(atomic classes)。

    估计一入行就是 React、Vue 横行的前端,压根儿就没听过更没见过「原子类」是个什么东西——

    <style>
    .w-100 { width: 100px; }
    .w-150 { width: 150px; }
    .h-100 { height: 100px; }
    .h-150 { height: 150px; }
    
    .m-10 { margin: 10px; }
    .m-20 { margin: 20px; }
    .mt-10 { margin-top: 10px; }
    .ml-15 { margin-left: 15px; }
    
    .bgc-red { background-color: red; }
    .bgc-greed { background-color: green; }
    
    .c-fff { color: #fff; }
    .c-000 { color: #000; }
    
    .f-l { float: left; }
    .f-r { float: right; }
    </style>
    
    <div class="w-150 h-150 f-l mt-10 ml-15 bgc-red c-000">
      <div class="w-100 h-100 f-r m-20 bgc-green c-fff">Atomic classes</div>
    </div>
    

    看到了吧,这种方法论强调的就是尽可能将 CSS 的每个属性和值的组合拆成 class,命名方式也基本是「属性名 + 属性值」的形式,并且属性名和属性值是否进行「简写」以及中间有没有 -_ 等分隔符就看编写的人的素养和心情了。

    原子类的「优点」是,它把 class 拆分到足够细,很好很「原子」;原子化带来的特点就是可组合性很强,这样任何页面都可以通过原子类的有机组合去实现,只有想不到,没有做不到!哪天设计师说要把按钮距离左边的 15 像素改为 10 像素——没问题!把 <button>.ml-15 换成 .ml-10 就好!小菜一碟!

    为什么上面说的「优点」是加了引号的?我就想知道,原子类除了写的时候字符数可能会稍微少些,跟写内联样式(inline style)有什么区别?有更语义化吗?可读性有变更好吗?人脑负担有降低吗?中、大型项目维护起来更方便吗?

    随着基于组件开发在 web 前端领域的普及,原子类的身影逐渐消失;但最近因为某个 CSS 框架人气走高的原因,原子类再度死灰复燃……

    那么,原子类或者说样式原子化是错的吗?不是,都是时臣的错!啊,不!都是 utility-first 思想的错!

    class 应该是语义化的,尤其是在基于组件开发时,让在视图结构中一眼看到 class 后,就知道它是个什么东西,而不是它长什么样。

    另外,基于组件开发的特点之一就是封装,对外屏蔽内部细节;而 utility-first 思想恰恰是暴露细节,这与基于组件开发的理念「三观不合」。

    在基于组件开发的体系下,class 理应是 component-first,即应用 CSS 组件(CSS component),那些 utility class 作为辅助存在。也就是说,当 CSS 组件自带样式与实际需求有些许不符时,利用 utility class 进行「微调」,而不是在外部重写 CSS 组件的样式——这也是一种组合方式。

    比如,按钮 CSS 组件本身是不会在水平方向撑满容器的,但设计师想让它占满一行——

    <style>
    .Button {
      display: inline-block;
      text-align: center;
    }
    
    .u-block {
      display: block !important;
    }
    </style>
    
    <div>
      <button class="Button u-block">CSS component</button>
    </div>
    

    CSS 组件在本系列文章所阐述的 UI 组件体系中,叫做「视觉组件」,class 的命名遵循 BEM 的变体——SUIT CSS 命名约定。

    SUIT CSS 是 Normalize.css 的作者 Nicolas Gallagher 于 2013 年左右时创立,虽然现在已经处于基本不维护的状态了,但它基于组件开发的思想仍发挥着余热。

    SUIT CSS 命名约定我从 2014 年用到现在,并且会继续用下去。本系列文章 CSS 相关的示例代码中 class 的命名皆遵循此命名约定。在基于组件开发的体系下,强烈建议 class 命名遵循 SUIT CSS 命名约定——

    /* 组件 */
    .ComponentName {}
    
    /* 组件修饰符 */
    .ComponentName--modifierName {}
    
    /* 组件后代 */
    .ComponentName-descendentName {}
    
    /* 组件状态 */
    .ComponentName.is-stateOfComponent {}
    
    /* 辅助工具 */
    .u-utilityName {}
    

    组件基类 .ComponentName 及其后代 .ComponentName-descendentName 很好理解,它们天然具有层级关系,共同描述了一个 UI 组件的结构——

    <!-- 用语义化 HTML 标签 -->
    <article class="Article">
      <header class="Article-header">
        <h1 class="Article-title">文章标题</h1>
      </header>
      <section class="Article-section">
        <h2>章节标题</h2>
        <p>章节段落</p>
      </section>
      <footer class="Article-footer">一些其他信息</footer>
    </article>
    
    <!-- 用非语义化 HTML 标签,更能凸显出 class 命名语义化的作用 -->
    <div class="Article">
      <div class="Article-header">
        <h1 class="Article-title">文章标题</h1>
      </div>
      <div class="Article-section">
        <h2>章节标题</h2>
        <p>章节段落</p>
      </div>
      <div class="Article-footer">一些其他信息</div>
    </div>
    

    而组件修饰符 .ComponentName--modifierName 和组件状态 .ComponentName.is-stateOfComponent 有时就不能很好地区分何时该用哪个了。就拿按钮 CSS 组件来说,它的颜色、是否可用与尺寸,哪个该用修饰符?哪个算是状态?

    我给出一个比较简单的判断标准:如果是 UI 组件的特性,即不会因为什么条件而改变的,用修饰符;倘若会因某个条件满足与否而变化,那就是状态——

    <!-- 用语义化 HTML 标签,大号(尺寸)的主要(功能色)操作按钮 -->
    <button class="Button Button--primary Button--large">新增</button>
    
    <!-- 用非语义化 HTML 标签,不可用(状态)的危险(功能色)操作按钮 -->
    <span class="Button Button--danger is-disabled">批量删除</span>
    

    应该注意的是,组件修饰符和组件状态都是直接加在 UI 组件的根节点上的,也就是要跟在组件基类的后面,不能用于组件后代上。假如一个组件后代需要程序化地改变它本身的样式,要用辅助工具类而不是状态类。当一个组件后代的结构、功能等变得复杂时,要将其封装成一个新的组件。

    Sass 变量与 CSS 自定义属性

    在本系列文章所阐述的 UI 组件体系中,Sass 变量和 CSS 自定义属性合称为「风格组件」,它们负责主题风格的定制,是与设计体系(Design System)的结合点。其中,Sass 变量是在编辑时/编译时,CSS 自定义属性则是在运行时。

    在这里,Sass 变量与 CSS 自定义属性的命名方式比较类似,它们大概都是 <namespace>-<component-name>[-descendent-name|-modifier-name][-state]-(variable-name|property-name) 的形式。

    由于我在基于本系列文章所阐述的思想做一套叫做「Petals」的半成品 UI 组件,因此之后的示例代码中涉及到的 <namespace> 部分基本都会用 petals

    Sass 变量是以 $__petals$petals 开头,与组件名之间用 -- 连接,前者是内部使用(私有)的,上层开发者无需关心,后者是供外部在编辑时/编译时定制用;CSS 自定义属性则用 --petals 开头,以 - 与组件名相连——

    /* 实际形式:<namespace>-<component-name>-(variable-name|property-name) */
    $__petals--button-font-size: --petals-button-font-size;
    $__petals--button-line-height: --petals-button-line-height;
    
    /* 实际形式:<namespace>-<component-name>-<modifier-name>-<state>-(variable-name|property-name) */
    $petals--button-primary-focus-color: var($__petals--primary-active-color, $petals--primary-active-color) !default;
    $petals--button-primary-focus-bg: var($__petals--primary-active-bg, $petals--primary-active-bg) !default;
    

    上文所说的 CSS 组件,即视觉组件,它是将样式进行封装,对外屏蔽细节;而风格组件相反,通过将视觉组件所用到的 CSS 属性值动态化的方式达到样式可定制化的目的,这就变得像 utility-first 的原子类一样暴露了样式细节。

    但与 utility-first 的 CSS 框架不同的是,风格组件只给进行主题风格定制的人带来了心智负担,对其他的上层开发者并无影响。

    业务无关

    本系列文章主要讨论的对象是业务无关的 UI 组件,在单说「UI 组件」或「组件」时也是指这个;而业务相关的 UI 组件,在本系列文章所阐述的 UI 组件体系中叫做「部件」。

    上面提到的「通用组件」和「专用组件」都是业务无关的 UI 组件。

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

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

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

    UI 组件的属性只应与其本身的特性有关,与业务意义无关——自身特性是自然特性,业务意义是附加特性。

    比如,一个按钮组件通常会有「主要」、「次要」和「危险」这几种多少与业务沾边的语义,那么组件的属性该如何设计来满足这种需求呢?

    Ant Design 和 Element 的做法是将其作为 type 属性的值或独立成一个属性——

    <Button type="primary">Ant Design 中的主要按钮</Button>
    <Button>Ant Design 中的次要(默认)按钮</Button>
    <Button danger>Ant Design 中的危险按钮</Button>
    
    <el-button type="primary">Element 中的主要按钮</el-button>
    <el-button>Element 中的次要(默认)按钮</el-button>
    <el-button type="danger">Element 中的危险按钮</el-button>
    

    按照上面说的 UI 组件属性设计原则来看,「主要」、「次要」和「危险」作用到按钮组件上的表现主要是颜色发生了变化,所以应该去用表示按钮的自然特性「颜色」的 color 属性来满足同样的需求——

    <button color="primary">主要按钮</button>
    <button>次要(默认)按钮</button>
    <button color="danger">危险按钮</button>
    
    <!-- 还可以扩展出其他任意多颜色的按钮 -->
    <button color="f00">红色按钮</button>
    <button color="yellow">黄色按钮</button>
    <button color="blue">蓝色按钮</button>
    

    若 UI 组件的某组特性是二元对立的,如「禁用」与「启用」,则选择默认不生效的那个作为属性,且属性值是布尔型,默认值为 false

    还是拿按钮组件来举例:如果默认是「禁用」,那就设计一个代表「启用」的 enabled 属性,其默认值是 false,只要组件在被使用时传入了 enabled,就变成了「启用」状态;反之亦然。

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

    业务相关

    业务相关的 UI 组件,即上文所说的「部件」,因其关注点与业务无关的 UI 组件不同,所以在设计时所遵守的原则和考虑的事情也不尽相同,甚至会大相径庭。一般来说,会用到上下文与依赖注入等技术。

    由于业务相关的 UI 组件不是本系列文章主要讨论的对象,在此就不展开说了。

    总结

    前几天在朋友圈立了个 flag——

    聊聊前端 UI 组件:组件设计

    本文就是该 flag 的「引子」。


    欢迎关注微信公众号【Coding as Hobby】(微信中搜「coding-as-hobby」)以及时阅读最新的技术文章~ ;-)


    起源地下载网 » 聊聊前端 UI 组件:组件设计

    常见问题FAQ

    免费下载或者VIP会员专享资源能否直接商用?
    本站所有资源版权均属于原作者所有,这里所提供资源均只能用于参考学习用,请勿直接商用。若由于商用引起版权纠纷,一切责任均由使用者承担。更多说明请参考 VIP介绍。
    提示下载完但解压或打开不了?
    最常见的情况是下载不完整: 可对比下载完压缩包的与网盘上的容量,若小于网盘提示的容量则是这个原因。这是浏览器下载的bug,建议用百度网盘软件或迅雷下载。若排除这种情况,可在对应资源底部留言,或 联络我们.。
    找不到素材资源介绍文章里的示例图片?
    对于PPT,KEY,Mockups,APP,网页模版等类型的素材,文章内用于介绍的图片通常并不包含在对应可供下载素材包内。这些相关商业图片需另外购买,且本站不负责(也没有办法)找到出处。 同样地一些字体文件也是这种情况,但部分素材会在素材包内有一份字体下载链接清单。
    模板不会安装或需要功能定制以及二次开发?
    请QQ联系我们

    发表评论

    还没有评论,快来抢沙发吧!

    如需帝国cms功能定制以及二次开发请联系我们

    联系作者

    请选择支付方式

    ×
    迅虎支付宝
    迅虎微信
    支付宝当面付
    余额支付
    ×
    微信扫码支付 0 元