组件化

模块化与组件化

本章主要介绍模块化与组件化的设计原则在Web开发中的实际应用,其承接了软件架构设计/组件化一章中的相关内容。

在静态类型语言Java与动态类型语言Python中都有内建的模块机制,模块机制能够让我们更好地去

import java.awt.*;
import java.awt.color.*;
$.browser.msie = function detectIE() {};

模块一般指能够独立拆分且通用的代码单元。

Mixing and matching generic components is what modern web development is all about.

The terms are similar. I generally think of a “module” as being larger than a “component”. A component is a single part, usually relatively small in scope, possibly general-purpose. Examples include UI controls and “background components” such as timers, threading assistants etc. A “module” is a larger piece of the whole, usually something that performs a complex primary function without outside interference. It could be the class library of an application that provides integration with e-mail or the database. It may be as large as a single application of a suite, such as the “Accounts Receivable module” of an ERP/accounting platform.

I also think of “modules” as being more interchangeable. Components can be replicated, with new ones looking like old ones but being “better” in some way, but typically the design of the system is more strictly dependent upon a component (or a replacement designed to conform to that component’s very specific behavior). In non-computer terms, a “component” may be the engine block of a car; you can tinker within the engine, even replace it entirely, but the car must have an engine, and it must conform to very rigid specifications such as dimensions, weight, mounting points, etc in order to replace the “stock” engine which the car was originally designed to have. A “module”, on the other hand, implies “plug-in”-type functionality; whatever that module is, it can be communicated with in such a lightweight way that the module can be removed and/or replaced with minimal effect on other parts of the system. The electrical system of a house is highly modular; you can plug anything with a 120V15A plug into any 120V15A receptacle and expect the thing you’re plugging in to work. The house wiring couldn’t care less what’s plugged in where, provided the power demands in any single branch of the system don’t exceed safe limits.

模块化无疑是ES6中最令人激动的特性,保证了大型应用程序的健壮性、可扩展性与可重构性

模块化CSS

命名规范

.block{} // the ‘thing’ like .list
.block__element{} // a child of the block like .list__item
.block--modifier{} // a variation of the ‘thing’ like .list-—vertical

预处理器

为了继续遵循BEM规范,我们需要在SCSS中添加对于父元素的引用,大概是如下语法形式:

.block {
  @at-root #{&}__element {
  }
  @at-root #{&}--modifier {
  }
}

最终编译生成的结果为:

.block {
}
.block__element {
}
.block--modifier {
}

而在SASS 3.3之后,我们可以使用如下的快捷写法:

.block {
  &__element {
  }
  &--modifier {
  }
}

在真实的开发环境中,我们以一个典型的导航栏为例描述下应该如何使用BEM规范:

<nav role="navigation" aria-label="primary">
  <ul class="nav__list">
    <li class="nav__list__item">
      <a href="" class="nav__link"></a>
    </li>
    <li class="nav__list__item">
      <a href="" class="nav__link--active"></a>
    </li>
    <li class="nav__list__item">
      <a href="" class="nav__link"></a>
    </li>
  </ul>
</nav>

我们编写的CSS样式如下所示:

nav[role="navigation"] {
}
.nav {
  &__list {
    &__item {
    }
  }
  &__link {
    &--active {
    }
  }
}

最终的输出结果大概是这个样子:

nav[role="navigation"] {
}
.nav {
}
.nav__list {
}
.nav__list__item {
}
.nav__link {
}
.nav__link--active {
}

CSS-in-JS

广义上说,目标格式为CSS的 预处理器 是CSS预处理器,但本文 特指 以最终生成CSS为目的的 领域特定语言。Sass、LESS、Stylus是目前最主流的CSS预处理器。

.opacity(@opacity: 100) {
  opacity: @opacity / 100;
  filter: ~"alpha(opacity=@{opacity})";
}

.sidebar {
  .opacity(50);
}

将以上DSL源代码(LESS),编译成CSS

.sidebar {
  opacity: 0.5;
  filter: alpha(opacity=50);
}

取到DSL源代码 的 分析树将含有 动态生成 相关节点的 分析树 转换为 静态分析树将 静态分析树 转换为CSS的 静态分析树将CSS的 静态分析树 转换为CSS代码

.foo {
  transition: width 0.3s;
}

自动按需生成前缀

.foo {
  -webkit-transition: width 0.3s;
  -moz-transition: width 0.3s;
  -o-transition: width 0.3s;
  transition: width 0.3s;
}
/* 变量 */
:root {
  --fontSize: 14px;
  --mainColor: #333;
  --highlightColor: hwb(190, 35%, 20%);
}
/* 自定义 media queries */
@custom-media --viewport-medium (min-width: 760px) and (max-width: 990px);
/* 变量引用 以及使用 calc() 运算*/
body {
  color: var(--mainColor);
  font-size: var(--fontSize);
  line-height: calc(var(--fontSize) * 1.5);
  padding: calc((var(--fontSize) / 2) + 1px);
}
/* 颜色处理函数 */
a {
  color: color(var(--highlightColor) blackness(+20%));
  background: color(red a(80%));
}
/* 使用自定义 media queries */
@media (--viewport-medium) {
  .foo {
    display: flex;
    font-size: calc(var(--fontSize) * 2 + 6px);
  }
}
/* 变量 */
/* 自定义 media queries */
/* 变量引用 以及使用 calc() 运算*/
body {
  color: #333;
  font-size: 14px;
  line-height: 21px;
  padding: 8px;
}
/* 颜色处理函数 */
a {
  color: rgb(89, 142, 153);
  background: rgba(255, 0, 0, 0.8);
}
/* 使用自定义 media queries */
@media (min-width: 760px) and (max-width: 990px) {
  .foo {
    display: -webkit-box;
    display: -webkit-flex;
    display: -ms-flexbox;
    display: flex;
    font-size: 34px;
  }
}

组件化

在以DOM操作为核心的时代,我们的业务逻辑就是一系列对于DOM元素(元素)的操作序列,绝大部分的状态数据也都直接存放在元素中。而React中同样存在有元素与Component两个概念,React元素直译为元素,其用于描述屏幕所见内容,即是DOM元素的对象表示,也就是Virtual DOM。而组件则是某个能够接受输入并且返回某个元素的函数或者类。譬如在下面这个简单的Button组件中,其返回值就是某个元素,这里的函数参数onLogin就是所谓的Prop,我们会在以后的章节中进行详细讲解。

function Button({ onLogin }) {
  return React.createElement(
    "div",
    { id: "login-btn", onClick: onLogin },
    "Login"
  );
}

Virtual DOM初探的章节中我们也有提及,元素是可以递归嵌套的,换言之,组件是对于一或多个元素的封装,其能够根据不同的输入返回不同的元素。

function User({ name, addFriend }) {
  return React.createElement(
    "div",
    null,
    React.createElement("p", null, name),
    React.createElement(Button, { addFriend })
  );
}

组件化的意义

组件化要点

何谓好的组件

Web Components

Links