前端新手一定要清楚的组件设计原则

2023-06-05 0 601

我在最近的工作中开始使用 Vue 展开合作开发,但我在上一家公司累积了五年以上 React 合作开发经验。虽然在两种不同的后端架构之间展开转换确实须要学习许多,但两者之间在许多基础基本上概念、结构设计路子上是相连的。其中众所周知是组件结构设计,包括组件体系结构结构设计和组件各别的职能分割。

组件是绝大多数现代后端架构的基本上基本上概念众所周知,在 React 和 Vue 和 Ember 和 Mithril 等架构上均有所充分体现。组件通常是由记号词汇、方法论和式样组成的子集。它被建立的目的是作为可F83E43Se的组件去构筑他们的插件。

近似于传统 OOP 词汇中 class 的结构设计,在结构设计组件的时候须要考虑到许多各方面,以期它能较好的复用,女团,分立和低谐振,但功能能相对平稳的实现,即使是在远远超过实际单元测试范围的情况下。这样的结构设计说出来容易做出来却极难,因为现实生活中他们往往没有足够多的时间按照最优化的方式去做。

方式

在责任编辑中,我想介绍一些组件相关的结构设计基本上概念,在展开后端合作开发时应该考虑这些基本上概念。我指出最好的方式是给每一基本上概念一个简约研磨的英文名字,然后逐个解释每一基本上概念是什么和为什么重要,对比较抽象基本上概念的会举许多范例来协助认知

下列那个条目并并非不全面也不完备,但我注意到的只有 8 件事情值得称赞一提的是,对那些早已能撰写基本上组件但想提升他们的技术结构设计专业技能的人来说。所以这是条目:

下列列出的那个条目实际上是是我注意到的 8 个各方面,总之组件结构设计还有其他许多各方面。在此我只是列出出来我指出值得称赞一提的是的。

对早已掌控基本上的组件结构设计并且想提升另一方面的组件结构设计能力的合作开发人员,我指出下列 8 � 二项我指出值得称赞去注意的,总之这并并非组件结构设计的全部。

体系结构和 UML 类图

集中化、面向全国数据的 state/props

更加单纯的 State 变化

低谐振

辅助代码分立

提炼精华

及时组件化

集中/统一的状态管理

请注意,代码示例可能有许多小问题或有点人为结构设计。但它并不复杂,只是想通过这些范例来协助更好的认知基本上概念。

体系结构和类图

应用内的组件共同形成组件树, 而在结构设计过程中将组件树可视化展示能协助你全面了解插件的布局。一个比较好的展示这些的办法是组件图。

UML 中有一个在 OOP 类结构设计中经常使用的类型,称为 UML 类图。类图中显示了类属性、方式、访问修饰符、类与其他类的关系等。虽然 OOP 类结构设计和后端组件结构设计差异很大,但通过图解辅助结构设计的方式值得称赞参考。对后端组件,该图表能显示:

State

Props

Methods

与其他组件的关系( Relationship to other components )

因此,让他们看一下下面那个基础表组件的组件层次图,该组件的渲染对象是一个数组。该组件的功能包括显示总行数、标题行和许多数据行,和在单击其单元格标题格时对该列展开排序。在它的 props 中,它将传递列条目(具有属性名称和该属性的人类可读版本),然后传递数据数组。他们能添加一个可选的’on row click’功能来展开测试。

前端新手一定要清楚的组件设计原则

虽然这样的事情可能看出来有点多,但它具有许多优点,并且在大型插件合作开发结构设计中所须要的。这样会带来的一个比较重要的问题是它会须要你在开始 codeing 之前就须要考虑到具体细节的实现,例如每一组件须要什么类型的数据,须要实现哪些方式,所需的状态属性等等。

一旦你对如何构筑一个组件(或一组组件)的整体有大概的路子,就会很容易指出当自己真正开始编码实现时,它会如自己所期望的按部就班的完成,但事实上往往会出现许多预料之外的事情, 总之你肯定不希望因此去重构之前的某些部分,或者忍受初始设想中的缺点并因此扰乱你的代码路子。而这些类图的下列优点能协助你有效的规避以上问题,优点如下:

一个易于认知的组件组成和关联视图

一个易于认知的插件 UI 体系结构的概述

一个结构数据层次及其流动方式的视图

一个组件功能职能的快照

便于使用图表软件建立

顺带一提,上图并并非基于某些官方标准,比如 UML 类图,它是我基本上上建立的一套表达规则。例如,在 props 、方式的参数和返回值的数据类型定义声明

扁平的,面向全国数据的 state/props

在 state 和 props 频繁被 watch 和 update 的情况下,如果你有使用嵌套数据,那么你的性能可能会受到影响,尤其是在下列场景中,例如许多因为浅对而触发的重新渲染;在涉及 immutability 的库中,比如 React,你必须建立状态的副本而并非像在 Vue 中那样直接更改它,并且使用嵌套数据这样做可能会建立笨拙,丑陋的代码。

//Flat, data-oriented state/props

const state = {

  clients: {

    allClients,

    firstClient,

    lastClient: {

name: John,

      phone: Doe,

      address: {

        number: 5,

        street: Estin,

        suburb: Parrama,

        city: Sydney

      }

    }

  }

}

// 倘若他们须要去修改 address number时须要怎么办?

const test = {

  clients: {

    …state.clients,

    lastClient: {

      …state.clients.lastClient,

address: {

        …state.clients.lastClient.address,

        number: 10

      }

    }

  }

}

即使使用展开运算符,这种写法也并不够优雅。扁平 props 也能较好地清除组件正在使用的数据值。如果你传给组件一个对象但你并不能清楚的知道对象内部的属性值,所以找出实际须要的数据值是来自组件具体的属性值则是额外的工作。但如果 props 足够多集中化,那么起码会方便使用和维护。

// 他们无法得知 customer 那个对象里面拥有什么属性

// 那个组件须要使用那个对象所有的属性值或者只是须要其中的一部分?

// 如果我想将那个组件在别处使用,我应该传入什么样的对象

// 下面的那个组件接收的属性就一目了然

state / props 还应该只包含组件渲染所需的数据。You shouldn’t store entire components in the state/props and render straight from there.

(此外,对数据繁重的插件,数据规范化能带来巨大的好处,除了集中化之外,你可能还须要考虑许多别的优化方式)。

更加单纯的 State 变化

对 state 的更改通常应该响应某种事件,例如用户单击按钮或 API 的响应。此外它不应该因为别的 state 的变化而做出响应,因为 state 之间这种关联可能会导致难以认知和维护的组件行为。state 变化应该没有副作用。

现给表的组件,其中排序,过滤等功能都是后端完成的,因此后端须要做的是 watch 所有搜索参数,并在其变化时触发 API 调用。其中一个须要 watch 的值是“zone”,这是一个过滤器。当更改时

//State change purity

zone:{

  handler() {

    // 重置页码

if(this.pagination.page > 1){

        this.pagination.page = 1

        return;

    }

    this.getDataFromApi()

  }

}

你会发现许多奇怪的东西。如果他们远远超过了结果的第一页,他们重置页码然后结束?这

watch: {

pagination() {

    this.getDataFromApi()

  }

},

zone: {

  handler() {

    // 重置页码

    if(this.pagination.page > 1) {

this.pagination.page = 1

        return;

    }

    this.getDataFromApi()

  }

}

据更新这段方法论。

让他们一下来考虑下列流程:如果当前页面远远超过了第 1 页并且更改了 zone,而那个变化会触发另一个状态(pagination)发生变化,进而触发 pagination 的观察者重新请求数据。这样并并非预料之中的行为,而且产生的代码也不够直观。

解决方案是改变页码那个行为的事件处理函数(并非观察者,用户更改页面的实际处理函数)应该更改页面值并触发 API 调用请求数据。这也将消除对观察

虽然那个范例非常简单,但不难看出将更复杂的状态更改关联在一起会产生令人难以认知的代码,这些代码不仅不可扩展并且是调试的噩梦。

松谐振

组件的核心思想是它是可F83E43Se的,为此要求它必须具有功能性和完备性。“谐振”是指实体彼此依赖的术语。松散谐振的实体应该能够独立运行,而不依赖于其他组件。就后端组件而言,谐振的主要部分是组件的功能依赖于其父级及其传递的 props 的多少,和内部使用的子组件(总之还有引用的部分,如第三方组件或用户脚本)。

紧密谐振的组件往往更不容易被F83E43Se,当它作为特定父组件的子项时,就极难正常工作,当父组件的一个子组件或一系列子组件只能在该父组件才能够正常发挥作用时,就会使得代码写的很冗余。因为父子组件别过度的关联在一起了。

在结构设计组件时,你应该考虑到更加通用的使用场景,而不实际上只是为了满足最开始某个特定场景的需求。虽然一般来说组件最初都是出于特定目的展开结构设计,但没关系,如果在结构设计它站在更高的角度去看待,那么许多组件将具有更好的适用性。

让他们看一个简单的 React 示例,你想在写出一个带有一个 logo 的链接条目,通过连接能访问特定的网站。最开始的结构设计可能是并没有跟内容合理的展开解耦。下面是最初的版本:

const Links = ()=>(

        Home

        Products

        Help

前端新手一定要清楚的组件设计原则

)

虽然这这样会满足预期的使用场景,但却极难被F83E43Se。如果你想更改链接地址该怎么办?你必须重新复制一份相同代码,并且手动去替换链接地址。而且, 如果你要去实现一个用户能更改连接的功能,那么意味着不可能将代码写“死”,也不能期望用户去手动修改代码,那么让他们来看一下F83E43Se性更高的组件应该如何结构设计:

const DEFAULT_LINKS = [

  {route: “/”, text: “Home”},

{route: “/shop”, text: “Products”},

  {route: “/help”, text: “Help”}

]

const DEFAULT_LOGO = “/default/logo.png”

const Links = ({links = DEFAULT_LINKS,logoPath = DEFAULT_LOGO }) => (

       // 将数组依次渲染为超链接

links.map((link) =>  {link.text})

前端新手一定要清楚的组件设计原则

)

在这里他们能看到,虽然它的原始链接和 logo 具有默认值,但他们能通过 props 传入的值去覆盖掉默认值。让他们来看一下它在实际中的使用:

const adminLinks = {

  links: [

    {route: “/”, text: “Home”},

    {route: “/metrics”, text: “Site metrics”},

{route: “/admin”, text: “Admin panel”}

  ],

  logoPath: “/admin/logo.png”

}

并不须要重新撰写新的组件!如果他们解决上文中用户能自定义链接的使用场景,能考虑动态构筑链接数组。此外,虽然在那个具体的范例中没有解决,但他们仍然能注意到那个组件没有与任何特定的父/子组件建立密切关联。它能在任何须要的地方呈现。改进后的组件明显比最初版本具有更好的F83E43Se性。

如果并非要结构设计需要服务于特定的一次性场景的组件,那么结构设计组件的最终目标是让它与父组件松散谐振,呈现更好的F83E43Se性,而并非受限于特定的上下文环境。

辅助代码分立

那个可能不那么的偏理论,但我仍然指出这很重要。与你的代码库打交道是软件工程的一部分,有时许多基本上的组织原则能使事情变得更加顺畅。在长时间与代码相处的过程中,即使改变一个很小的习惯也能产生很大的不同。其中一个有效的原则是将辅助代码分立出来放在特定的地方,这样你在处理组件时就不必考虑这些。下列列出许多各方面:

配置代码

假数据

大量非技术说明文档

因为在尝试处理组件的核心代码时,你不希望看到与技术无关的许多说明(因为会多滚动几下鼠标滚轮甚至打断路子)。在处理组件时,你希望它尽可能通用且可重用。查看与组件当前上下文相关的特定信息可能会使得结构设计出来的组件不易与具体业务解耦。

提炼精华

虽然这样做出来可能具有挑战性,但合作开发组件的一个好方式是使它包含渲染它所需的最小

单独从组件分的“视图”部分来看,即你看到的内容(html 和 式样)。其中的 Javascript 仅用于协助渲染视图,可能还有许多针对特定组件的方法论(例如在其他地方使用时)。除此之外的任何事情,例如 API 调用,数值的格式化(例如货币或时间)或跨组件F83E43Se的数据,都能移动外部的 js 文件中。让他们看一下 Vue 中的一个简单示例,使用嵌套条目组件。他们能先看下下面那个有问题的版本。

这是第一个层级:

// 组件父级

前端新手一定要清楚的组件设计原则

这是嵌套条目组件:

// nestedList 组件

{{secondLevelItem.text}}

而新的第二级:

// nestedList 组件

{{secondLevelItem.text}}

相关文章

发表评论
暂无评论
官方客服团队

为您解决烦忧 - 24小时在线 专业服务