做者:我想写文章啊
html
来源:https://juejin.im/post/6878099828497186823前端
1、写在前面
现今的web开发经过先后端分离的技术拆分为了web后端开发与web前端开发,值得指出的是,web前端开发早已不是传统意义上的开发模式了,转而变成了web客户端开发,有过客户端开发经验的同窗应该知道这二者间的差异,客户端开发关注的是:vue
-
应用的生命周期 -
组件化 -
开发模式与打包方法
组件化是客户端开发最重要的内容,设计一套复用度高、扩展性好的组件系统,能够显著提升开发效率,而且能够减小后期的维护成本。react
2、一个笔记组件的设计案例

就以我正在使用的笔记app为例,上图展现的笔记的阅读与书写区域,如何将这个区域抽象为一个组件呢?让咱们一步一步来分析。web
1. 最简api
咱们为该组件取个名字(取名很重要),就叫Note吧。无论是在阅读状态仍是在编辑状态,该组件都要展现笔记的内容,由于笔记对象应该经过组件的接口传入进来,由于咱们为该组件设计第一个api:后端
属性 | 说明 | 类型 | 是否必填 |
---|---|---|---|
data | 笔记对象数据 | object | 是 |
接下来,咱们简单使用一下这个组件:api
为了兼容vue与react的读者,本页面均使用JSX语法数组
const note = {
title: 如何制做一个组件?.md ,
content:
}
这样,一个最简api的笔记组件就搞定了,它的接口很是简单,只须要提供一个data
属性,就能够展现出笔记内容,而且能够点击编辑进入书写状态。微信
通常而言,若是没有更多的需求的话,咱们的笔记组件设计到这里也就能够了。**在设计组件时,务必遵循最小化原则,即尽量少地抛出接口。**由于使用组件的用户可能有不少,一旦组件做者不当心抛出了一个不合理的接口,之后想要修改就几乎不可能了(只能经过标记过期的方法提醒用户,但这种作法每每是无奈之举)。markdown
2. 知足数据获取的多种状况
如今,组件的使用者已经能够经过很简洁的api使用这个笔记组件了,可是如今问题来了:有的组件使用者只拿到了笔记的id,想要经过直接传入id的方式使用组件。
此时,做为组件做者,咱们评估了这个需求是合理的,因而,咱们扩展了笔记组件的api:
属性 | 说明 | 类型 | 是否必填 | 默认值 |
---|---|---|---|---|
data | 笔记对象 | object | 否 | null |
dataId | 笔记对象id | string | 否 | null |
如今能够经过传入id的方式来使用组件了:
const noteId = 123
<Note dataId={noteId} />
请注意,api中的两个属性都是非必填的,由于不知道用户会传入哪一个属性,为了程序的严谨性,组件内部应当校验两个参数都不传的状况,并经过抛出错误告诉调用者。
这是组件设计的一个技巧,经过支持多种数据源使得调用更加简单。可是这种设计也有其弊端所在,若是这种兼容性的扩展过多会使得组件的内部逻辑变得复杂,也会使得api变得难于理解,所以,对于兼容性的api扩展要谨慎,不可过量。
3. 兼容不一样模式
组件的使用一如既往的优雅、简单,可是如今又有用户提出新的需求了:由于该组件是支持阅读与编辑两种模式的,在使用时,对于他人的笔记是不可编辑的,可否在指定的场景下只支持一种阅读模式?
笔记组件因为内部支持了两种模式,既支持阅读,也支持编辑。所以调用者只想使用一种模式也是合理的。因而,咱们继续扩展组件的api:
属性 | 说明 | 类型 | 是否必填 | 默认值 |
---|---|---|---|---|
mode | 模式,数组的第一项做为初始模式,该参数不可为空数组 | array | 否 | [ write , read ] |
如今,对于只想使用阅读模式的用户,能够这么调用:
const note = {}
const mode = [ read ]
<Note data={note} mode={mode} />
在设计api时,咱们在知足需求的前提下,支持了更多状况。首先,使用者也可能只使用编辑模式,由于mode参数是支持随意组合各类模式的,所以这种状况也能知足。另外,若是组件之后扩展了更多模式,该api仍然能知足需求,只须要为mode数组增长更多的模式项便可。这里有一个更佳的设计是,当使用多个模式时,肯定哪一个模式做为初始模式也是有必要的,所以,将mode数组的第一项做为多模式下的初始模式,既知足了需求,又达到了api设计最小化的原则。
如今,咱们对用户的需求进行了扩展,不只支持只使用阅读模式,还支持各类模式任意组合和初始模式,可是这还不够,组件的设计者应当针对需求想到更长远的状况,针对这个例子,咱们还能够为组件扩展一个模式改变的事件,让调用者能够捕捉到笔记组件从阅读 -> 编辑或编辑 -> 阅读(随着模式的扩展,这种组合会更多)切换的时机:
事件 | 说明 | 回调参数 |
---|---|---|
modeChange | 模式切换时触发 | (from: string, to: string) from表示切换前的模式,to表示切换后的模式 |
调用者可能在捕捉到模式切换事件时,作一些特定的工做:
function handleModeChange(from, to) {
// ...
}
<Note onModeChange={handleModeChange} />
4. 更多的支持
在编辑器中编辑笔记是html或markdown类型的,笔记组件支持将笔记导出为一个PDF文档。所以,设计时咱们能够将组件的一些能力抽象为api,再次扩展组件的api:
方法 | 说明 | 参数 |
---|---|---|
exportPDF | 导出笔记为PDF文件 | - |
toggleFullscreen | 切换全屏显示 | (value: boolean) 是否全屏展现 |
组件设计时,咱们能够将可预见范围内的组件的能力设计为api,须要注意的,方法的参数与返回值也是api的一部分,应当谨慎设计。
除了扩展组件的能力外,咱们还能够扩展组件的视图。注意到阅读
按钮右侧的工具栏了吗?咱们假设这部分的视图不属于笔记组件,是经过api扩展而渲染出来的,这就是组件的子视图设计,在web前端的组件化中,称为插槽。咱们能够为笔记组件扩展一个工具栏的插槽:
插槽 | 说明 | 参数 |
---|---|---|
toolbar | 工具栏子视图 | { data } |
当调用者想要扩展笔记组件的工具栏时,能够这么使用:
const note = {}
<Note data={note}>
<MyToolbar />
</Note>
这样,调用者就能够根据本身的需求,在工具栏渲染本身想要的内容了。
3、组件设计四要素
上述案例讲述了组件设计的整个流程,经过分析用户的需求(或将来可能出现的需求),一步一步地设计出了一个复用度高、扩展性好的组件。若是你是一个组件设计的新手,你应该如何去思考、去设计一个优良的组件呢?
1. 先设计,后实现
咱们通篇在讨论组件的设计,可是实际操做时,不少朋友会经过边实现边设计的方式来完成一个组件的制做,这是不合理的,由于自身能力与眼界的限制,实现可能会干扰你的设计,对于如下两个经典矛盾,但愿读者能选择后者,以追求合理性为重。
这样实现比较方便,不如将这个参数抛出让用户传进来吧! 这样设计比较合理,虽然实现难度可能会比较高,但我能够经过文档学习、求问他人的方式来实现它,或者直接让他人来实现。
**提出问题比解决问题更难。**设计难于实现,你应当花70%的时间来设计而不是用来实现。有的设计者甚至不参与实现,设计者与实现者的身份也是随时在转换的,善于思考的实现者自己就是设计者。
2. 组件设计四要素
-
属性 -
方法 -
事件 -
子视图(插槽)
上述的案例基本涵盖了这四个要素,这四要素共同组成了组件的api。须要注意的,除了基本的四要素外,咱们还须要注意这些也是组件api的一部分:
-
属性的类型、是否必填、默认值(属性类型肯定后再也不变化) -
方法的参数、返回值(须要考虑变化的状况) -
事件回调函数的参数 -
插槽可获取到的局部参数
在设计时,应当当心谨慎面对每个api的要素,哪个环节出现了设计缺陷,对于调用者都是如鲠在喉。
4、终极思惟:面向对象
尽管咱们经过一系列的理论讲述了组件设计的方法,可是对于初学者而言,仍然难以设计出一个优良的组件,设计一个优良的组件须要大量的经验,初学者每每考虑不全面,或因对需求的不了解,没法预知将来的变化。
尽管如此,初学者仍然要耐心学习组件的设计,不积跬步无以致千里,通过一段时间的积累,我总结了一个设计组件的终极思惟,将面向对象的思想用于组件设计,将会事半功倍。
在开发领域,学会思考比埋头干活重要。咱们将这个理论用于组件设计中,如何经过面向对象的思惟来设计一个组件呢?
虽然咱们强调使用面向对象的思惟来设计组件,但仿佛面向对象思惟比组件设计更高深,咱们固然不会推荐你们用更加晦涩的理论来指导组件的设计,这里,咱们将面向对象拟人化,提取出一个天然世界联想法的思惟方法。
下面咱们就用这种方法,来设计一个快递小哥的组件:
首先,快递小哥有他的基本信息,这是该组件的属性,基本信息中包含了他的任职单位、工做年限、姓名、联系方式等等。此外,快递小哥有一些特有的行为,例如送快递、接收包裹等,咱们能够将这部分抽取为组件的方法,好比咱们调用快递小哥的接收包裹方法,该方法有两个参数,第一个参数是我要寄的东西即包裹,第二个参数是快递单,描述了寄送相关的信息。除了基本信息和一些行为外,快递小哥组件还有一些特有的事件,当咱们的包裹到了时,他会打电话给咱们,这里,组件抛出了一个快递到达的事件,事件的参数是快递单和包裹,快递单描述了包裹的送达信息,包裹是快递单中描述的接收人的东西。最后,快递小哥组件有没有子视图呢?有。快递小哥组件除了被咱们普通用户调用外,还会被快递公司所调用,不一样的快递公司会以不一样的方式来包装快递小哥(例如经过不一样服装不一样logo的方式),所以,快递公司在调用该组件时,会将快递小哥的服装传入一个名为装束的子视图中,这样,不一样公司的快递小哥就有不一样的装束了。
你可使用天然世界联想法来思考一切关于面向对象与组件化相关的问题,只要计算机世界仍然是人构建的,咱们就仍然能够按照天然世界的规则来感知计算机世界。
二进制世界历来不是冰冷、无情的,每个二进制串都融入了编码人的思惟模式、价值观。
5、最后
从新回到开篇的问题,为何说当今的web前端开发已变成web客户端开发呢?由于组件化是全部客户端开发的核心概念,只要这个端大部分的时间在作组件抽象的工做,咱们就能够认为本身在从事客户端开发。
最后,组件化不是银弹,不能为你解决任何实际问题,它只是一种思惟方式。
本文分享自微信公众号 - 前端迷社区(gh_c8466b051727)。
若有侵权,请联系 support@oschina.cn 删除。
本文参与“OSC源创计划”,欢迎正在阅读的你也加入,一块儿分享。