如何开发一个人人爱的组件?

这篇具有很好参考价值的文章主要介绍了如何开发一个人人爱的组件?。希望对大家有所帮助。如果存在错误或未考虑完全的地方,请大家不吝赐教,您也可以点击"举报违法"按钮提交疑问。

组件,是前端最常打交道的东西,对于 React、Vue 等应用来说,万物皆组件毫不为过。

有些工作经验的同学都知道,组件其实也分等级的,有的组件可以被上万开发者复用,有些组件就只能在项目中运行,甚至挪动到自己的另外一个项目都不行。

如何考察一个前端的水平,首先可以看看他有没有对团队提供过可复用的组件,一个前端如果一直只能用自己写的东西,或者从没有对外提供过可复用的技术,那么他对于一个团队的贡献一定是有限的。

所以开始写一个能开放的组件应该考虑些什么呢?🤔

本篇文章类似一个菜谱,比较零碎的记录一些组件设计的内容,我分别按照 1~5 ⭐️ 区分其重要性。

意识

首先在意识层面,我们需要站在使用组件的开发者角度来观察这个组件,所以下面几点需要在组件开发过程中种在意识里面:

1.我应该注重 TypeScript API 定义,好用的组件API都应该看上去 理所应当 且 绝不多余。

2.我应该注重 README 和 Mock ,一个没有文档的组件 = 没有,最好不要使用 link 模式去开发组件。

3.我不应引入任何副作用依赖,比如全局状态(Vuex、Redux),除非他们能自我收敛。

4.我在开发一个开放组件,以后很有可能会有人来看我的代码,我得写好点。

接口设计

好的 Interface 是开发者最快能搞清楚组件入参的途径,也是让你后续开发能够有更好代码提示的前提。

type Size = any; // 😖 ❌type Size = string; // 🤷🏻♀️type Size = "small" | "medium" | "large"; // ✅

DOM属性(⭐️⭐️⭐️⭐️⭐️)

组件最终需要变成页面DOM,所以如果你的组件不是那种一次性的,请默认顺手定义基础的DOM属性类型。className 可以使用 classnames[1]或者 clsx[2]处理,别再用手动方式处理 className 啦!

export interface IProps {className?: string;  style?: React.CSSProperties;}

对于内部业务来说,还会有 data-spm 这类 dom 属性,主要用于埋点上报内容,所以可以直接对你的 Props 类型做一个基础封装:

export type CommonDomProps = {className?: string;  style?: React.CSSProperties;} & Record<`data-${string}`, string>// component.tsxexport interface IComponentProps extends CommonDomProps {  ...}// orexport type IComponentProps = CommonDomProps & {  ...}

类型注释(⭐️⭐️⭐️)

1.export 组件 props 类型定义

2.为组件暴露的类型添加 规范的注释

export type IListProps{/**   * Custom suffix element.   * Used to append element after list   */  suffix?: React.ReactNode;/**   * List column definition.   * This makes List acts like a Table, header depends on this property   * @default []   */  columns?: IColumn[];/**   * List dataSource.   * Used with renderRow   * @default []   */  dataSource?: Array<Record<string, any>>;}

上面的类型注释就是一个规范的类型注释,清晰的类型注释可以让消费者,直接点击进入你的类型定义中查看到关于这个参数的清晰解释。

同时这类符合 jsdoc[3]规范的类型注释,也是一个标准的社区规范。利用 vitdoc[4]这类组件DEMO生成工具也可以帮你快速生成美观的 API 说明文档。

小技巧:如果你非常厌倦写这些注释,不如试试著名的AI代码插件:Copilot[5],它可以帮你快速生成你想要表达的文字。

以下是 ❌ 错误示范:

toolbar?: React.ReactNode; // List toolbar.// 👇🏻 Columns // defaultValue is "[]"  columns?: IColumns[];

组件插槽(⭐️⭐️⭐️)

对于一个组件开发新手来说,往往会犯 string 类型替代 ReactNode 的错误。

比如要对一个 Input 组件定义一个 label 的 props ,许多新手开发者会使用 string 作为 label 类型,但这是错误的。

export type IInputProps = {  label?: string; // ❌}export type IInputProps = {  label?: React.ReactNode; // ✅}

遇到这种类型时,需要意识到我们其实是在提供一个 React 插槽类型,如果在组件消费中仅仅是让他展示出来,而不做其他处理的话,就应当使用 ReactNode 类型作为类型定义。

如何开发一个人人爱的组件?

受控 与 非受控(⭐️⭐️⭐️⭐️⭐️)

如果要封装的组件类型是 数据输入 的用途,也就是存在双向绑定的组件。请务必提供以下类型定义:

export type IFormProps<T = string> = {  value?: T;  defaultValue?: T;  onChange?: (value: T, ...args) => void;};

并且,这类接口定义不一定是针对 value, 其实对于所有有 受控需求 的组件都需要,比如:

export type IVisibleProps = {/**   * The visible state of the component.   * If you want to control the visible state of the component, you can use this property.   * @default false   */  visible?: boolean;/**   * The default visible state of the component.   * If you want to set the default visible state of the component, you can use this property.   * The component will be controlled by the visible property if it is set.   * @default false   */  defaultVisible?: boolean;/**   * Callback when the visible state of the component changes.   */  onVisibleChange?: (visible: boolean, ...args) => void;};

具体原因请查看:《受控组件和非受控组件》[6]

消费方式推荐使用:ahooks useControllableValue[7]

表单类常用属性(⭐️⭐️⭐️⭐️)

如果你正在封装一个表单类型的组件,未来可能会配合 antd[8]/ fusion[9]等 Form 组件来消费,以下这些类型定义你可能会需要到:

export type IFormProps = {/**   * Field name   */  name?: string;/**   * Field label   */  label?: ReactNode;/**   * The status of the field   */  state?: 'loading' | 'success' | 'error' | 'warning';/**   * Whether the field is disabled   * @default false   */  disabled?: boolean;/**   * Size of the field   */  size?: 'small' | 'medium' | 'large';/**   * The min value of the field   */  min?: number;/**   * The max value of the field   */  max?: number;};

选择类型(⭐️⭐️⭐️⭐️)

如果你正在开发一个需要选择的组件,可能以下类型你会用到:

export interface ISelection<T extends object = Record<string, any>> {/**   * The mode of selection   * @default 'multiple'   */  mode?: 'single' | 'multiple';/**   * The selected keys   */  selectedRowKeys?: string[];/**   * The default selected keys   */  defaultSelectedRowKeys?: string[];/**   * Max count of selected keys   */  maxSelection?: number;/**   * Whether take a snapshot of the selected records   * If true, the selected records will be stored in the state   */  keepSelected?: boolean;/**   * You can get the selected records by this function   */  getProps?: (record: T, index: number) => { disabled?: boolean; [key: string]: any };/**   * The callback when the selected keys changed   */  onChange?: (selectedRowKeys: string[], records?: Array<T>, ...args: any[]) => void;/**   * The callback when the selected records changed   * The difference between `onChange` is that this function will return the single record   */  onSelect?: (selected: boolean, record: T, records: Array<T>, ...args: any[]) => void;/**   * The callback when the selected all records   */  onSelectAll?: (selected: boolean, keys: string[], records: Array<T>, ...args: any[]) => void;}

上述参数定义,你可以参照 Merlion UI - useSelection[10]查看并消费。

另外,单选与多选存在时,组件的 value 可能会需要根据下传的 mode 自动变化数据类型。

比如,在 Select 组件中就会有以下区别:

mode="single" -> value: string | numbermode="multiple" -> value: string[] | number[]

所以对于需要 多选、单选 的组件来说,value 的类型定义会有更多区别。

对于这类场景可以使用 Merlion UI - useCheckControllableValue[11]进行抹平。

组件设计

服务请求(⭐️⭐️⭐️⭐️⭐️)

这是一个在业务组件设计中经常会遇到的组件设计,对于很多场景来说,或许我们只是需要替换一下请求的 url ,于是便有了类似下面这样的API设计:

export type IAsyncProps {  requestUrl?: string;  extParams?: any;}

后面接入方增多后,出现了后端的 API 结果不符合组件解析逻辑,或者出现了需要请求多个API组合后才能得到组件所需的数据,于是一个简单的请求就出现了以下这些参数:

export type IAsyncProps {  requestUrl?: string;  extParams?: any;  beforeUpload?: (res: any) => any  format?: (res: any) => any}

这还只是其中一个请求,如果你的业务组件需要 2个、3个呢?组件的API就会变得越来越多,越来越复杂,这个组件慢慢的也就变得没有易用性 ,也慢慢没有了生气。

对于异步接口的API设计最佳实践应该是:提供一个 Promise 方法,并且详细定义其入参、出参类型。

export type ProductList = {  total: number;  list: Array<{    id: string;    name: string;    image: string;    ...  }>}export type AsyncGetProductList = (  pageInfo: { current: number; pageSize: number },  searchParams: { name: string; id: string; },) => Promise<ProductList>;export type IComponentProps = {/**   * The service to get product list   */  loadProduct?: AsyncGetProductList;}

通过这样的参数定义后,对外只暴露了 1 个参数,该参数类型为一个 async 的方法。开发者需要下传一个符合上述入参和出参类型定义的函数。

在使用时组件内部并不关心请求是如何发生的,使用什么方式在请求,组件只关系返回的结果是符合类型定义的即可。

这对于使用组件的开发者来说是完全白盒的,可以清晰的看到需要下传什么,以及友好的错误提示等等。

Hooks(⭐️⭐️⭐️⭐️⭐️)

很多时候,或许你不需要组件!

对于很多业务组件来说,很多情况我们只是在原有的组件基础上封装一层浅浅的业务服务特性,比如:

  • Lazada Uploader:Upload + Lazada Upload Service

  • Address Selector: Select + Address Service

  • Brand Selector: Select + Brand Service

  • ...

而对于这种浅浅的胶水组件,实际上组件封装是十分脆弱的。因为业务会对UI有各种调整,对于这种重写成本极低的组件,很容易导致组件的垃圾参数激增。

实际上,对于这类对服务逻辑的状态封装,更好的办法是将其封装为 React Hooks ,比如上传:

export function Page() {const lzdUploadProps = useLzdUpload({ bu: 'seller' });return <Upload {...lzdUploadProps} />}

这样的封装既能保证逻辑的高度可复用性,又能保证 UI 的灵活性。

Consumer(⭐️⭐️⭐️)

对于插槽中需要使用到组件上下文的情况,我们可以考虑使用 Consumer 的设计进行组件入参设计。

比如 Expand 这个组件,就是为了让部分内容在收起时不展示。

如何开发一个人人爱的组件?

对于这种类型的组件,明显容器内的内容需要拿到 isExpand 这个关键属性,从而决定索要渲染的内容,所以我们在组件设计时,可以考虑将其设计成可接受一个回调函数的插槽:

export type IExpandProps = {  children?: (ctx: { isExpand: boolean }) => React.ReactNode;}

而在消费侧,则可以通过以下方式轻松消费:

export function Page() {return (<Expand>      {({ isExpand }) => {        return isExpand ? <Table /> : <AnotherTable />;      }}</Expand>  );}

文档设计

package.json(⭐️⭐️⭐️⭐️⭐️)

请确保你的 repository 是正确的仓库地址,因为这里的配置是很多平台溯源的唯一途径,比如: npmjs.com\npm.alibaba-inc.com\mc.lazada.com

如何开发一个人人爱的组件?

请确保 package.json 中存在常见的入口定义,比如 main\module\types\exports,以下是一个 package.json 的示范:

{"name": "xxx-ui","version": "1.0.0","description": "Out-of-box UI solution for enterprise applications from B-side.","author": "yee.wang@xxx.com","exports": {".": {"import": "./dist/esm/index.js","require": "./dist/cjs/index.js"    }  },"main": "./dist/cjs/index.js","module": "./dist/esm/index.js","types": "./dist/cjs/index.d.ts","repository": {"type": "git","url": "git@github.com:yee94/xxx.git"  }}

README.md(⭐️⭐️⭐️⭐️)

如果你在做一个库,并希望有人来使用它,请至少为你的库提供一段描述,在我们的脚手架模板中已经为你生成了一份模板,并且会在编译过程中自动加入在线 DEMO 地址,但如果可以请至少为它添加一段描述。

这里的办法有很多,如果你实在不知道该如何写,可以找一些知名的开源库来参考,比如 `antd` \ `react` \ `vue` 等。

还有一个办法,或许你可以寻求 `ChatGPT` 的帮助,屡试不爽😄。文章来源地址https://www.toymoban.com/news/detail-459667.html

到了这里,关于如何开发一个人人爱的组件?的文章就介绍完了。如果您还想了解更多内容,请在右上角搜索TOY模板网以前的文章或继续浏览下面的相关文章,希望大家以后多多支持TOY模板网!

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处: 如若内容造成侵权/违法违规/事实不符,请点击违法举报进行投诉反馈,一经查实,立即删除!

领支付宝红包 赞助服务器费用

相关文章

  • 人人都可用chatgpt开发小项目

    chatgpt4.0,开发一个h5数独游戏。以下是沟通过程,全程菜鸟模式,提问者没有使用任何专业技术术语。 You: 用h5写个数独小游戏 ChatGPT: 创建一个数独小游戏需要使用HTML、CSS和JavaScript。这里提供一个简单的数独游戏示例,你可以根据自己的需求进行修改和扩展。 创建一个HTM

    2023年04月18日
    浏览(45)
  • vue3中如何编写一个组件---demo

    当使用 Vue 3 来开发组件时,可以使用 Composition API 来定义组件逻辑和实现组件传值。下面是一个详细的 Vue 3 组件示例和讲解: 假设我们要开发一个简单的待办事项列表组件,包括添加待办事项、显示待办事项列表以及删除待办事项的功能。 在 TodoList.vue 文件中: 在上面的示

    2024年02月11日
    浏览(38)
  • 人人商城前端小程序如何配置使用教程

    WQ小程序如何配置使用,人人商城小程序如何配置实用,今天编写一篇关于人人商城小程序配置对接的教程,方便小白快速配置人人商城小程序。 1、在阿三源码站下载人人商城源码,根据自己的需要下载对应版本。 2、下载人人上商城源码,然后解压,将后端文件夹ewei_shop

    2024年02月09日
    浏览(78)
  • 我决定把一个收费视频课全免费公开了,今天起,慢慢放出“人人都需要的产品思维课”...

    人人都需要的产品思维课 Version 2017 为什么这会儿公开,单纯因为版权到期了,可以公开了。咱也不靠这个挣钱,干脆免费给需要的人吧。 这套课是2017年和馒头商学院合作录制的,所以内容主要基于2006~2014我在阿里做产品/管产品大学,以及2014~2017创业/孵化的经历所成。 曾经

    2023年04月08日
    浏览(45)
  • Kendo UI,一个加速Web应用界面开发的JavaScript组件库!

    Kendo UI是什么? 首先,Kendo UI是一个由四个JavaScript UI库组成的包,这些库是专为jQuery、Angular、React和Vue原生构建的,每一个都是用一致的API和主题构建的。所以无论开发者怎么选择,所开发的Web应用始终保持了现代的应用界面,响应迅速、可访问且速度快! 为什么要选择Ke

    2024年02月15日
    浏览(57)
  • 教你如何写一个符合自己需求的小程序日历组件

    很多时候,我们生活中会有各种打卡的情况,比如 keep 的运动打卡、单词的学习打卡和各种签到打卡或者酒店的入住时间选择,这时候就需要我们书写一个日历组件来处理我们这种需求。 但是更多时候,我们都是网上找一个插件直接套用了,有没有想过自己实现一下呢?如果

    2024年02月10日
    浏览(48)
  • datePicker一个或多个日期组件,如何快捷选择多个日期(时间段)

    elementUI的组件文档中没有详细说明type=\\\"dates\\\"如何快捷选择一个时间段的日期,我们可以通过picker-options参数来设置快捷选择: 通过设置变量pickerOption的值可设置快捷选择: 其中dateFormat方法是将日期格式化,getAllDate方法是获取开始和结束日期之间的全部日期,在data中定义:

    2024年02月13日
    浏览(37)
  • 组件化开发之如何封装组件-react

    组件是构建用户界面的基本单元,它是一个独立的、可重用的、可组合的代码单元,用于表示UI的一部分。 人话:当谈论组件时,就像在搭积木一样,每个组件都是一个 独立的、可以重复使用 的代码块,用来构建网页或应用的各个部分。比如界面的布局,像按钮、文本输入

    2024年02月11日
    浏览(63)
  • 手势交互!人人都会用的交互方式应该如何设计?

    手势交互是指通过手部动作来进行人机交互的一种方式。随着移动设备和触摸屏技术的普及,手势交互成为了一种重要的交互方式。它可以使用户更加直观地操作设备,提升用户体验,同时也可以拓展设备的交互维度,使得用户可以通过更加丰富的手势来完成各种操作。 手势

    2024年01月21日
    浏览(36)
  • 人人都能升薪?深扒Android开发中的通天神器Framework

    近年来,客户端领域的竞争日益激烈,很多安卓开发者抱怨内卷现象普遍存在。在这种背景下,开发者们面对的压力越来越大,很难掌握足够的技能去顺利发展。解决内卷,突破迷茫,是许多开发者面临的挑战。小米的高管在脉脉上提供了一条不错的建议,即如下: 但对于大

    2024年02月02日
    浏览(42)

觉得文章有用就打赏一下文章作者

支付宝扫一扫打赏

博客赞助

微信扫一扫打赏

请作者喝杯咖啡吧~博客赞助

支付宝扫一扫领取红包,优惠每天领

二维码1

领取红包

二维码2

领红包