热门标签 | HotTags
当前位置:  开发笔记 > 编程语言 > 正文

如何设计组件还不会吗?简单谈论下组件化思想

点击上方程序员成长指北,关注公众号回复1,加入高级Node交流群作者:我想写文章啊来源:https:juejin.impos

点击上方 程序员成长指北,关注公众号

回复1,加入高级Node交流群

作者:我想写文章啊

来源:https://juejin.im/post/6878099828497186823

一、写在前面

现今的web开发通过前后端分离的技术拆分为了web后端开发与web前端开发,值得指出的是,web前端开发早已不是传统意义上的开发模式了,转而变成了web客户端开发,有过客户端开发经验的同学应该知道这两者间的差别,客户端开发关注的是:

  1. 应用的生命周期

  2. 组件化

  3. 开发模式与打包方法

组件化是客户端开发最重要的内容,设计一套复用度高、扩展性好的组件系统,可以显著提高开发效率,并且可以减少后期的维护成本

二、一个笔记组件的设计案例

就以我正在使用的笔记app为例,上图展示的笔记的阅读与书写区域,如何将这个区域抽象为一个组件呢?让我们一步一步来分析。

1. 最简api

我们为该组件取个名字(取名很重要),就叫Note吧。不管是在阅读状态还是在编辑状态,该组件都要展示笔记的内容,因为笔记对象应该通过组件的接口传入进来,因为我们为该组件设计第一个api:

属性说明类型是否必填
data笔记对象数据object

接下来,我们简单使用一下这个组件:

为了兼容vue与react的读者,本页面均使用JSX语法

const note = {title:  如何制作一个组件?.md ,content: 
}

这样,一个最简api的笔记组件就搞定了,它的接口非常简单,只需要提供一个data属性,就可以展示出笔记内容,并且可以点击编辑进入书写状态。

一般而言,如果没有更多的需求的话,我们的笔记组件设计到这里也就可以了。**在设计组件时,务必遵循最小化原则,即尽可能少地抛出接口。**因为使用组件的用户可能有很多,一旦组件作者不小心抛出了一个不合理的接口,以后想要修改就几乎不可能了(只能通过标记过时的方法提醒用户,但这种做法往往是无奈之举)。

2. 满足数据获取的多种情况

现在,组件的使用者已经可以通过很简洁的api使用这个笔记组件了,但是现在问题来了:有的组件使用者只拿到了笔记的id,想要通过直接传入id的方式使用组件。

此时,作为组件作者,我们评估了这个需求是合理的,于是,我们扩展了笔记组件的api:

属性说明类型是否必填默认值
data笔记对象objectnull
dataId笔记对象idstringnull

现在可以通过传入id的方式来使用组件了:

const noteId =  123

请注意,api中的两个属性都是非必填的,因为不知道用户会传入哪个属性,为了程序的严谨性,组件内部应当校验两个参数都不传的情况,并通过抛出错误告诉调用者。

这是组件设计的一个技巧,通过支持多种数据源使得调用更加简单。但是这种设计也有其弊端所在,如果这种兼容性的扩展过多会使得组件的内部逻辑变得复杂,也会使得api变得难于理解,因此,对于兼容性的api扩展要谨慎,不可过量。

3. 兼容不同模式

组件的使用一如既往的优雅、简单,但是现在又有用户提出新的需求了:因为该组件是支持阅读与编辑两种模式的,在使用时,对于他人的笔记是不可编辑的,能否在指定的场景下只支持一种阅读模式?

笔记组件由于内部支持了两种模式,既支持阅读,也支持编辑。因此调用者只想使用一种模式也是合理的。于是,我们继续扩展组件的api:

属性说明类型是否必填默认值
mode模式,数组的第一项作为初始模式,该参数不可为空数组array[ write ,  read ]

现在,对于只想使用阅读模式的用户,可以这么调用:

const note = {}
const mode = [ read ]

在设计api时,我们在满足需求的前提下,支持了更多情况。首先,使用者也可能只使用编辑模式,因为mode参数是支持随意组合各种模式的,因此这种情况也能满足。另外,如果组件以后扩展了更多模式,该api仍然能满足需求,只需要为mode数组增加更多的模式项即可。这里有一个更佳的设计是,当使用多个模式时,确定哪个模式作为初始模式也是有必要的,因此,将mode数组的第一项作为多模式下的初始模式,既满足了需求,又达到了api设计最小化的原则。

现在,我们对用户的需求进行了扩展,不仅支持只使用阅读模式,还支持各种模式任意组合和初始模式,但是这还不够,组件的设计者应当针对需求想到更长远的情况,针对这个例子,我们还可以为组件扩展一个模式改变的事件,让调用者可以捕捉到笔记组件从阅读 -> 编辑或编辑 -> 阅读(随着模式的扩展,这种组合会更多)切换的时机:

事件说明回调参数
modeChange模式切换时触发(from: string, to: string) from表示切换前的模式,to表示切换后的模式

调用者可能在捕捉到模式切换事件时,做一些特定的工作:

function handleModeChange(from, to) {// ...
}

4. 更多的支持

在编辑器中编辑笔记是html或markdown类型的,笔记组件支持将笔记导出为一个PDF文档。因此,设计时我们可以将组件的一些能力抽象为api,再次扩展组件的api:

方法说明参数
exportPDF导出笔记为PDF文件-
toggleFullscreen切换全屏显示(value: boolean) 是否全屏展示

组件设计时,我们可以将可预见范围内的组件的能力设计为api,需要注意的,方法的参数与返回值也是api的一部分,应当谨慎设计。

除了扩展组件的能力外,我们还可以扩展组件的视图。注意到阅读按钮右侧的工具栏了吗?我们假设这部分的视图不属于笔记组件,是通过api扩展而渲染出来的,这就是组件的子视图设计,在web前端的组件化中,称为插槽。我们可以为笔记组件扩展一个工具栏的插槽:

插槽说明参数
toolbar工具栏子视图{ data }

当调用者想要扩展笔记组件的工具栏时,可以这么使用:

const note = {}

这样,调用者就可以根据自己的需求,在工具栏渲染自己想要的内容了。

三、组件设计四要素

上述案例讲述了组件设计的整个流程,通过分析用户的需求(或未来可能出现的需求),一步一步地设计出了一个复用度高、扩展性好的组件。如果你是一个组件设计的新手,你应该如何去思考、去设计一个优良的组件呢?

1. 先设计,后实现

我们通篇在讨论组件的设计,但是实际操作时,很多朋友会通过边实现边设计的方式来完成一个组件的制作,这是不合理的,因为自身能力与眼界的限制,实现可能会干扰你的设计,对于以下两个经典矛盾,希望读者能选择后者,以追求合理性为重。

  1. 这样实现比较方便,不如将这个参数抛出让用户传进来吧!

  2. 这样设计比较合理,虽然实现难度可能会比较高,但我可以通过文档学习、求问他人的方式来实现它,或者直接让他人来实现。

**提出问题比解决问题更难。**设计难于实现,你应当花70%的时间来设计而不是用来实现。有的设计者甚至不参与实现,设计者与实现者的身份也是随时在转换的,善于思考的实现者本身就是设计者。

2. 组件设计四要素

  • 属性

  • 方法

  • 事件

  • 子视图(插槽)

上述的案例基本涵盖了这四个要素,这四要素共同组成了组件的api。需要注意的,除了基本的四要素外,我们还需要注意这些也是组件api的一部分:

  • 属性的类型、是否必填、默认值(属性类型确定后不再变化)

  • 方法的参数、返回值(需要考虑变化的情况)

  • 事件回调函数的参数

  • 插槽可获取到的局部参数

在设计时,应当小心谨慎面对每一个api的要素,哪一个环节出现了设计缺陷,对于调用者都是如鲠在喉。

四、终极思维:面向对象

尽管我们通过一系列的理论讲述了组件设计的方法,但是对于初学者而言,仍然难以设计出一个优良的组件,设计一个优良的组件需要大量的经验,初学者往往考虑不全面,或因对需求的不了解,无法预知未来的变化。

尽管如此,初学者仍然要耐心学习组件的设计,不积跬步无以至千里,经过一段时间的积累,我总结了一个设计组件的终极思维,将面向对象的思想用于组件设计,将会事半功倍。

在开发领域,学会思考比埋头干活重要。我们将这个理论用于组件设计中,如何通过面向对象的思维来设计一个组件呢?

虽然我们强调使用面向对象的思维来设计组件,但仿佛面向对象思维比组件设计更高深,我们当然不会推荐大家用更加晦涩的理论来指导组件的设计,这里,我们将面向对象拟人化,提取出一个自然世界联想法的思维方法。

下面我们就用这种方法,来设计一个快递小哥的组件:

首先,快递小哥有他的基本信息,这是该组件的属性,基本信息中包含了他的任职单位、工作年限、姓名、联系方式等等。此外,快递小哥有一些特有的行为,例如送快递、接收包裹等,我们可以将这部分抽取为组件的方法,比如我们调用快递小哥的接收包裹方法,该方法有两个参数,第一个参数是我要寄的东西即包裹,第二个参数是快递单,描述了寄送相关的信息。除了基本信息和一些行为外,快递小哥组件还有一些特有的事件,当我们的包裹到了时,他会打电话给我们,这里,组件抛出了一个快递到达的事件,事件的参数是快递单和包裹,快递单描述了包裹的送达信息,包裹是快递单中描述的接收人的东西。最后,快递小哥组件有没有子视图呢?有。快递小哥组件除了被我们普通用户调用外,还会被快递公司所调用,不同的快递公司会以不同的方式来包装快递小哥(例如通过不同服装不同logo的方式),因此,快递公司在调用该组件时,会将快递小哥的服装传入一个名为装束的子视图中,这样,不同公司的快递小哥就有不同的装束了。

你可以使用自然世界联想法来思考一切关于面向对象与组件化相关的问题,只要计算机世界仍然是构建的,我们就仍然可以按照自然世界的规则来感知计算机世界。

二进制世界从来不是冰冷、无情的,每一个二进制串都融入了编码人的思维模式、价值观。

五、最后

重新回到开篇的问题,为什么说当今的web前端开发已变成web客户端开发呢?因为组件化是所有客户端开发的核心概念,只要这个端大部分的时间在做组件抽象的工作,我们就可以认为自己在从事客户端开发。

最后,组件化不是银弹,不能为你解决任何实际问题,它只是一种思维方式。

如果觉得这篇文章还不错

点击下面卡片关注我

来个【分享、点赞、在看】三连支持一下吧

   “分享、点赞、在看” 支持一波 


推荐阅读
  • 使用 ListView 浏览安卓系统中的回收站文件 ... [详细]
  • C++ 异步编程中获取线程执行结果的方法与技巧及其在前端开发中的应用探讨
    本文探讨了C++异步编程中获取线程执行结果的方法与技巧,并深入分析了这些技术在前端开发中的应用。通过对比不同的异步编程模型,本文详细介绍了如何高效地处理多线程任务,确保程序的稳定性和性能。同时,文章还结合实际案例,展示了这些方法在前端异步编程中的具体实现和优化策略。 ... [详细]
  • 在最近的学习过程中,我对Vue.js中的Prop属性有了更深入的理解,并认为这一知识点至关重要,因此在此记录一些心得体会。Prop属性用于在组件之间传递数据。由于每个组件实例的作用域都是独立的,无法直接引用父组件的数据。通过使用Prop,可以将数据从父组件安全地传递到子组件,确保数据的隔离性和可维护性。 ... [详细]
  • 本指南介绍了如何在ASP.NET Web应用程序中利用C#和JavaScript实现基于指纹识别的登录系统。通过集成指纹识别技术,用户无需输入传统的登录ID即可完成身份验证,从而提升用户体验和安全性。我们将详细探讨如何配置和部署这一功能,确保系统的稳定性和可靠性。 ... [详细]
  • 在本文中,我们将详细介绍如何构建一个用于自动回复消息的XML类。当微信服务器接收到用户消息时,该类将生成相应的自动回复消息。以下是具体的代码实现:```phpclass We_Xml { // 代码内容}```通过这个类,开发者可以轻松地处理各种消息类型,并实现高效的自动回复功能。我们将深入探讨类的各个方法和属性,帮助读者更好地理解和应用这一技术。 ... [详细]
  • Swoole加密机制的安全性分析与破解可能性探讨
    本文深入分析了Swoole框架的加密机制,探讨了其在实际应用中的安全性,并评估了潜在的破解可能性。研究结果表明,尽管Swoole的加密算法在大多数情况下能够提供有效的安全保护,但在特定场景下仍存在被攻击的风险。文章还提出了一些改进措施,以增强系统的整体安全性。 ... [详细]
  • PHP预处理常量详解:如何定义与使用常量 ... [详细]
  • 深入探索HTTP协议的学习与实践
    在初次访问某个网站时,由于本地没有缓存,服务器会返回一个200状态码的响应,并在响应头中设置Etag和Last-Modified等缓存控制字段。这些字段用于后续请求时验证资源是否已更新,从而提高页面加载速度和减少带宽消耗。本文将深入探讨HTTP缓存机制及其在实际应用中的优化策略,帮助读者更好地理解和运用HTTP协议。 ... [详细]
  • 本文探讨了使用JavaScript在不同页面间传递参数的技术方法。具体而言,从a.html页面跳转至b.html时,如何携带参数并使b.html替代当前页面显示,而非新开窗口。文中详细介绍了实现这一功能的代码及注释,帮助开发者更好地理解和应用该技术。 ... [详细]
  • 在当前的软件开发领域,Lua 作为一种轻量级脚本语言,在 .NET 生态系统中的应用逐渐受到关注。本文探讨了 Lua 在 .NET 环境下的集成方法及其面临的挑战,包括性能优化、互操作性和生态支持等方面。尽管存在一定的技术障碍,但通过不断的学习和实践,开发者能够克服这些困难,拓展 Lua 在 .NET 中的应用场景。 ... [详细]
  • 在 Linux 环境下,多线程编程是实现高效并发处理的重要技术。本文通过具体的实战案例,详细分析了多线程编程的关键技术和常见问题。文章首先介绍了多线程的基本概念和创建方法,然后通过实例代码展示了如何使用 pthreads 库进行线程同步和通信。此外,还探讨了多线程程序中的性能优化技巧和调试方法,为开发者提供了宝贵的实践经验。 ... [详细]
  • 在 Vue 应用开发中,页面状态管理和跨页面数据传递是常见需求。本文将详细介绍 Vue Router 提供的两种有效方式,帮助开发者高效地实现页面间的数据交互与状态同步,同时分享一些最佳实践和注意事项。 ... [详细]
  • 本文详细探讨了JavaScript中数组去重的各种方法,并通过实际代码示例进行了深入解析。文章首先介绍了几种常见的去重技术,包括使用Set对象、过滤方法和双重循环等。每种方法都附有具体的实现代码,帮助读者更好地理解和应用这些技术。此外,文中还讨论了不同方法在性能上的优劣,为开发者提供了实用的参考。 ... [详细]
  • 使用 Vuex 管理表单状态:当输入框失去焦点时自动恢复初始值 ... [详细]
  • 本指南从零开始介绍Scala编程语言的基础知识,重点讲解了Scala解释器REPL(读取-求值-打印-循环)的使用方法。REPL是Scala开发中的重要工具,能够帮助初学者快速理解和实践Scala的基本语法和特性。通过详细的示例和练习,读者将能够熟练掌握Scala的基础概念和编程技巧。 ... [详细]
author-avatar
瓷娃娃2502929883
这个家伙很懒,什么也没留下!
PHP1.CN | 中国最专业的PHP中文社区 | DevBox开发工具箱 | json解析格式化 |PHP资讯 | PHP教程 | 数据库技术 | 服务器技术 | 前端开发技术 | PHP框架 | 开发工具 | 在线工具
Copyright © 1998 - 2020 PHP1.CN. All Rights Reserved | 京公网安备 11010802041100号 | 京ICP备19059560号-4 | PHP1.CN 第一PHP社区 版权所有