想要搭建一个结构清晰,可维护性高的前端项目?学会这三招就够了

2 篇文章 0 订阅
1 篇文章 0 订阅

0.前言

作为一个持续搬砖练习时长五年半的前端工程师,经历了无数次项目重构,无数次从屎山代码中爬出又进入新一轮的屎山之中,为什么别人的代码写的跟诗一样,而我的代码写的跟屎一样?在无数个日夜的苦思冥想后,最终领悟到了一套通用的方法论。无论是在项目中,还是在组件库中,均可使用这套方法论。只要使用此套方法论,在codeRview中能让同事刮目相看,在项目中能让屎山不再,甚至在未来某一天,当你重新打开这个项目的时候,会情不自禁的说:卧槽,这真是我写的代码吗?怎么这么牛逼。

这些方法本应该是不传之密,在我数个日夜的苦思冥想后,最终做了一个违背祖宗的决定:公开这些方法论。当你看到这篇文章的时候,恭喜你,你获得了一次改变码生的机会,从此升职加薪不是梦,回家更早了,加班更少了,连熬夜掉的头发都长出来了。那么废话不多说,我们开始进入正题吧。

本方法论一共只有三招:分层化,配置化,粒子化,接下来容我一一进行讲解,本文代码部分均使用React进行说明,请根据自身技术栈进行脑补。

1. 分层化

分层化是指将代码的结构进行分离,把相同的部分组合在一起,形成习惯后,可大大降低寻找代码的时间,提高代码的可读性以及可维护性。

一般拿到一个项目时,你是不是直接迫不及待的开始撸代码了?先不要着急,根据需求先整理好页面结构。在此以一个大屏页面为例,一般做一个大屏页面要做的流程为:设置好一个布局,在对应的位置添加图表,修改图表样式,mock数据,跟后端联调,添加图表与页面的交互效果,权限控制等。

那么基于此我们怎么进行分层化呢?我习惯将代码初步拆成这几层:layout层,数据层,胶水层,组件层,配置层。

layout层:用于页面布局样式的控制,通过插槽的方式将组件插入页面对应位置。

数据层:控制整个页面的数据加载,一般使用一个hooks进行控制,若请求多则可拆分为多个hooks。

胶水层:胶水层是整个页面的枢纽,用于统合整个页面的数据与视图,一般找代码都是从此处开始找的,它将往layout中插入各个组件,从数据层中获取数据分发到对应组件中去。大屏项目中总会有一些控制项会修改数据及视图,如:选择日期修改大屏中的数据,切换tab修改图表内容等,这些控制项也可以直接放在胶水层中。

组件层:组件层接收数据及配置项,生成一个个组件,页面中所有的显示元素都可以看成组件,如:图表组件,统计信息组件,3d组件,地图组件等。页面特有的组件由该页面页面自己管理,其他页面的组件需抽离出来放到公用组件文件夹中,须符合就近管理原则。

配置层:将配置页面的数据放到此层中统一管理(参考后面配置化思想),如:菜单配置,表格配置,组件配置等,当修改一些文字及界面元素时会非常方便。

分层化的核心思想就是分离及组合,先把页面进行分离,然后对功能相同的部分进行组合,达到高内聚低耦合的目的。

下面是我的一个大屏页面中的目录结构,由于此项目大屏页面有多个,因此layout层抽离到公共组件中去了。

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-Qrq38dBx-1668700814866)(https://p6-juejin.byteimg.com/tos-cn-i-k3u1fbpfcp/de75d3e9c6364d01a027db36060ffb48~tplv-k3u1fbpfcp-watermark.image?)]

2.配置化

配置化是指将所有组件的动态部分都抽象为一个个配置,使用一个json去描述,然后将整个配置文件注入到组件中,配置化有如下几个好处:

  1. 配置化的思维能大大降低项目的维护成本,当修改需求时,往往只需要改动某一个配置就能满足需求,而且维护项目的人不需要了解你的源代码,只需要读懂配置项即可进行一些日常功能的维护。

  2. 实现了配置化的组件可复用性强,当项目量达到一定程度时,积累的配置化组件可从项目中抽离,单独形成一套自己的组件库。有自己的代码积累,不至于深陷业务沦为苦逼的码农,面试时也可以成为一个亮点。

配置化的核心是利用typescript定义好json配置文件的参数,需要详细写出对应配置项的注释。实际开发中可能很多人都潜意识的对组件设置成可配置的,但很少有刻意的去把能配置化的组件都做成可配置化组件。万物皆可配置化,抱有这样的想法你才能写出可复用性高的代码。

那么组件配置化该怎么做呢?其实完全可以参考antd库的组织形式,在这里不讲源码,只讲使用。我们可以发现antd近期的版本很多的优化都是把原来通过jsx进行拼接的方式改成注入配置的方式,组件内部自行进行遍历,这其实就是配置化的一个方向。
图片

antd中menu组件版本优化

antd所有组件的动态部分都是使用配置的方式进行的,可以看到有很多配置项,通过修改这些配置项能基本实现你想要实现的功能。

图片

antd中table组件的配置项,自己写组件时,

可定义成一个type进行描述

3.粒子化

粒子化是指将一个个组件拆散,让其符合单一职责原则,再想拼积木一样,将其组装成完整的组件,以达到可插拔的状态。粒子化在小型项目中不需要关注,但当项目规模较大或编写组件库时,此方法往往拥有奇效。

大家在项目中是不是经常会遇到如下问题:

  1. 要写的页面非常多,但每个页面功能又很像。

  2. 客户需求变来变去,但每次变动又不是很大。

  3. 很多组件功能都差不多,但又有很多细节性的地方不一样。

如果经常遇到如上问题,就需要考虑进行粒子化拆分了,粒子化的项目前期可能会花较多时间,后期你会发现页面大部分内容都可以通过已有的碎片拼出来,开发速度杠杠的,需求改起来也很快,无非是把不要的拔出来,需要的插进去,再也不怕客户乱改需求了。

放出一个我自己写的渐变文字的卡片组件,让大家直观的了解一下使用粒子化以及配置化思想写出的代码。

// 渐变文字组件(GradText)
export type GradTextType = {
  children: React.ReactNode;
  color?: string;
  size?: number;
  styles?: React.CSSProperties;
};

const GradText = (props: GradTextType) => {
  return (
    <div
      className={styles.text}
      style={{
        ...props.styles,
        fontSize: props.size,
        background: `linear-gradient(0deg, ${props.color || 'rgba(111, 196, 255, 0.75)'
          } 0%,#fff 70%)`,
        WebkitBackgroundClip: 'text',
      }}>
      {
        typeof props.children === 'number' ? <CountUp duration={2} end={props.children} separator=','></CountUp> : props.children
      }
    </div>
  );
};

// 组合后的描述组件(Describe)
import GradText from '@/components/GradText';
export type DescribeType = {
  front: string;
  middle: string | number;
  end?: string;
  color?: string;
};

const Describe = (props: DescribeType) => {
  return (
    <div className={styles.gradText}>
      <span>{props.front}</span>
      <GradText color={props.color} size={18}>
        {props.middle}
      </GradText>
      <span>{props.end}</span>
    </div>
  );
};

export default Describe;

// 卡片组件(BsCard)
import Describe, { DescribeType } from '@/components/Describe';
export type CardType = {
  title: string;
  color?: string;
  item: {
    front: string;
    middle: number;
    end?: string;
    color?: string;
  }[];
  progress?: number;
};
type CardProps = {
  data?: CardType[];
  describe?: DescribeType[];
};

function BsCard(props: CardProps) {
  return (
    <div className={styles.card}>
      {props.describe?.map((item) => (
        <Describe
          color={item.color}
          front={item.front}
          middle={item.middle}
          end={item.end}
          key={item.front + item.middle + item.end}></Describe>
      ))}
      {props.data &&
        props.data.map((content) => (
          <div className={styles.cardItem} key={content.title}>
            <div className={styles.trigle}></div>
            <div className={styles.title}>{content.title}</div>
            <div className={styles.content}>
              {content.item.map((item) => (
                <div className={styles.item} key={item.front}>
                  <Describe
                    color={item.color}
                    front={item.front}
                    middle={item.middle}
                    end={item.end}></Describe>
                </div>
              ))}
            </div>
            <div style={{ width: (content.progress || 0) + '%' }} className={styles.progress}></div>
            <div className={styles.progressOut}></div>
          </div>
        ))}
    </div>
  );
}

export default BsCard;

在页面中引用上述代码

// 卡片组件配置
const cardData:CardProps = {
  data:listData,
  describe:describeData
}
// 卡片组件
<BsCard
  {...cardData}
>
... 省略其他两个组件

效果如下图,每一个组件块都很小,但是可以有多种组合方式,达到不同页面效果。

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-gZctfWrq-1668700814868)(https://p3-juejin.byteimg.com/tos-cn-i-k3u1fbpfcp/70966e0bbeac4c619f0b48c47b2f03ce~tplv-k3u1fbpfcp-zoom-1.image)]

4.总结

根据以上方法论,我们开发项目时的实现思路就很清晰了:

  1. 开始做一个页面时,先进行分层,提前建立好基本的结构。

  2. .在编码过程中,将各个模块拆分组件,然后将配置项定义好,不断优化这些配置项,以适应更多情景下的使用。

  3. 如果项目规模较大时,将组件进行更加细致化的拆分,拼拼凑凑,一个项目就完成啦。

掌握这三招,融会贯通,相信再复杂的项目大家也有办法搭建一个还不错的项目结构,屎山少一点,快乐多一点。

本文仅代表个人观点,欢迎大家在评论区探讨,共同努力,共同进步!


欢迎关注我的公众号获取前端学习资料及最新文章,您也可以添加我的微信进行沟通交流:

本人公众号:web前端可视化
本人微信号:voidjay

  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值