前端工程化之组件化

前端工程化是从软件工程衍生出的概念, 它是指以工程化的方法构建和维护有效、实用和高质量的软件。
前端从刀耕火种到现在社区空前繁荣的发展历史,也是工程化的演变历史。从演变的历史来看,前端工程化主要分模块化组件化工具化自动化。
本篇文章主要谈谈组件化以及组件化的实践方案。

组件化
组件并不是一个新的概念。c++ 中就提出过这样的概念,组件是对数据和方法的封装,即对象。
前端中的组件则是对一块视图区域的封装,多个组件组合成了一个复合组件,多个组件组合成了一个页面,所以页面也是组件。

一个组件中包含了完整视图结构,样式表以及交互逻辑。它是封闭的结构,对外提供属性的输入用来满足用户的多样化需求,对内自己管理内部状态来满足自己的交互需求,所以组件的封装也是对象的封装,同样要做到高内聚,低耦合。

组件化的目的是为了实现代码的更高层次的复用,提高开发效率,同样它也是前端工程化的基础。组件化的页面不仅仅利于开发者在进行单元测试,同样也有益于测试的有效进行。

组件化的实践
Web Components
Web Components 是W3C提出的组件化规范。允许开发者使用规范提供的API 以及HTML 模板来实现可重用的定制元素,与HTML 原本支持的标签元素相同,可以直接在页面中使用。
Web Components 主要有三种技术构成:

  • Custom elements(自定义元素):一组JavaScript API,允许您定义custom elements及其行为,然后可以在的用户界面中按照需要使用它们。
  • Shadow DOM(影子DOM):一组JavaScript API,用于将封装的“影子”DOM树附加到元素(与主文档DOM分开呈现)并控制其关联的功能。通过这种方式,可以保持元素的功能私有,这样它们就可以被脚本化和样式化,而不用担心与文档的其他部分发生冲突。
  • HTML templates(HTML模板): <template><slot> 元素使您可以编写不在呈现页面中显示的标记模板。然后它们可以作为自定义元素结构的基础被多次重用。
    相关的开发教程,请参见 Web Components

但是由于规范不是很完善,现只有Firefox(版本63)、Chrome和Opera都默认支持Web组件; Safari支持许多web组件特性,但比前面的浏览器少; Edge正在开发一个实现。

Stencil Js
Stencil 是由Ionic 团队开发出的前端视图库,类似react。它可以将开发者开发的组件编译成 Web Components ,可以直接运行在浏览器中。抹平了前端不同技术架构造成的隔离,比如采用Stencil 开发的组件,在Vue 和 React 中都可以使用,不需要再进行额外的兼容工作。

Angular Directives / VUE Components / React Components / Backbone Components / Anything... Components

社区的视图库或者框架提供的组件解决方案,是当前最具代表性的组件化的实践,但是不同的框架之间的隔离也同样是前端需要考虑的问题。

封装React 组件
封装组件最主要要遵循开闭原则和单一职责原则,这也是封装一个高内聚低耦合的组件的核心。
简而言之,就是首先要知道你的组件是做什么的,并且它只做这一件事;其次对你的组件的对外的属性输出和对内的状态管理有明确和清晰的认知,什么开放给用户,什么自己管理。

1.界定一个组价的Scope
这里推荐React 官方文档上拆分一个页面成多个组件的方法,React 设计哲学

thinking-in-react-components.png
  1. FilterableProductTable (橙色): 是整个示例应用的整体
  2. SearchBar (蓝色): 接受所有的用户输入
  3. ProductTable (绿色): 展示数据内容并根据用户输入筛选结果
  4. ProductCategoryRow (天蓝色): 为每一个产品类别展示标题
  5. ProductRow (红色): 每一行展示一个产品

对应的层级:
FilterableProductTable

  • SearchBar
  • ProductTable
    • ProductCategoryRow
    • ProductRow

2.区分Props 和 State
在React 中 UI = Component(Props, State)。
Props 是组件对外的接口,不可变。组件内可以引用其他组件,组件之间的引用形成了一个树状结构(组件树),如果下层组件需要使用上层组件的数据或方法,上层组件就可以通过下层组件的props属性进行传递,因此props是组件对外的接口。

State 是组件对内的状态,可变。state必须能代表一个组件UI呈现的完整状态集,即组件对应UI的任何改变,都可以从state的变化中反映出来;同时,state还必须是代表一个组件UI呈现的最小状态集,即state中的所有状态都是用于反映组件UI的变化,没有任何多余的状态,也不需要通过其他状态计算而来的中间状态。

组件中用到的一个变量是不是应该作为组件state,可以通过下面的4条依据进行判断:

  1. 这个变量是否是通过props从父组件中获取?如果是,那么它不是一个状态。
  2. 这个变量是否在组件的整个生命周期中都保持不变?如果是,那么它不是一个状态。
  3. 这个变量是否可以通过state 或props 中的已有数据计算得到?如果是,那么它不是一个状态。
  4. 这个变量是否在组件的render方法中使用?如果不是,那么它不是一个状态。这种情况下,这个变量更适合定义为组件的一个普通属性(除了props 和 state以外的组件属性 ),例如组件中用到的定时器,就应该直接定义为this.timer,而不是this.state.timer。

同时,并不是组件中用到的所有变量都是组件的State!当存在多个组件共同依赖同一个状态时,一般的做法是状态上移,将这个状态放到这几个组件的公共父组件中。

在项目中开发组件
在项目中开发组件,最重要的是明确组件的作用域。

  • 业务组件,它会包含你的业务逻辑,方便你对代码的分割。
  • UI组件(以上提出的关于组价的封装的方法论都是UI 组件), 即一个公共的组件,它所包含的应该只有该UI 组件的交互逻辑。如果当你觉得它会有一些公共的业务逻辑的时候,请再进行二次封装,即在该UI 组件的基础上在进行一层业务的包裹。

封装UI组件

社区中优秀的React 组件库有很多,但是不同的公司有不同的设计语言,如果单纯的采用某社区UI库的话,比如Ant-design, 势必不能满足业务的需求和风格的需求。

所以对于我们来说,我们现阶段采用的封装复合公司风格的UI 组件时, React-component,在这个基础库上进行二次开发。

  • React-component, 是Ant-design 的组件基础库,它提供了完善的API以及简单的样式,所以我们需要对它的样式进行定制,满足公司的设计语言。
import * as React from "react";
import styles from "./Switch.less";
import RcSwitch from "rc-switch";
import "rc-switch/assets/index.css";

type SwitchChangeEventHandler = (
    checked: boolean,
    event: React.MouseEvent<HTMLButtonElement> | React.KeyboardEvent<HTMLButtonElement>
) => void;

interface SwitchProps {
    className?: string;
    prefixCls?: string;
    disabled?: boolean;
    checkedChildren?: React.ReactNode;
    unCheckedChildren?: React.ReactNode;
    onChange?: SwitchChangeEventHandler;
    onKeyDown?: React.KeyboardEventHandler<HTMLButtonElement>;
    onClick?: SwitchChangeEventHandler;
    tabIndex?: number;
    checked?: boolean;
    defaultChecked?: boolean;
    loadingIcon?: React.ReactNode;
    style?: React.CSSProperties;
    title?: string;
    text?: string[];
}

interface SwitchState {
    checked: boolean;
}

export default class Switch extends React.PureComponent<SwitchProps, SwitchState> {
    constructor(props) {
        super(props);
        this.state = {
            checked: !!props.checked
        };
    }
    ...
    ...
    ...

    render() {
        const { text } = this.props;
        const { checked } = this.state;
        const label = checked ? text[0] : text[1];

        return (
            <div className={styles.switch}>
                {text && text.length && <span className={styles.label}>{label}</span>}
                <RcSwitch {...this.props} className={styles.bgColor} onChange={this.onChangeHandler} />
            </div>
        );
    }
}

参考:
React 深入系列3:Props 和 State

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 215,634评论 6 497
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 91,951评论 3 391
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 161,427评论 0 351
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 57,770评论 1 290
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 66,835评论 6 388
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 50,799评论 1 294
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 39,768评论 3 416
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 38,544评论 0 271
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 44,979评论 1 308
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 37,271评论 2 331
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 39,427评论 1 345
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 35,121评论 5 340
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 40,756评论 3 324
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 31,375评论 0 21
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 32,579评论 1 268
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 47,410评论 2 368
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 44,315评论 2 352