前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
工具
TVP
发布
社区首页 >专栏 >【云+社区年度征文】浅谈前端项目结构设计

【云+社区年度征文】浅谈前端项目结构设计

原创
作者头像
Nian糕
修改2020-12-24 09:29:15
7631
修改2020-12-24 09:29:15

原先想的标题是“浅谈前端架构”,但后来想想,前端固然有可以称为架构的部分,但架构是对整个系统进行通盘考虑的,并不仅局限于前端或者后端,“前端架构师”这个头衔虽然很酷,但这也仅仅是满足自己内心的虚荣感而已。所以我把标题改成了“浅谈前端项目结构设计”,虽然 Vue-Cli 等构建工具能够为我们生成基础的文件目录结构,但面对不同的业务场景和项目后期可维护性来看,还是远远不够的,所以我把今年重构的一个千万用户级别的前端应用里,自己获得的一些思考和感悟,分享给大家。

  • 如何阅读他人的代码?

每个人代码编写习惯都不尽相同,从大到有人用全局变量or本地缓存管理状态,小到是否在代码末尾加分号,在个人开发的项目中这并不是什么严重问题,但在团队协作开发时,我们往往需要阅读其他人所写的代码,此时没有一个统一完整的代码规范,无法有效的控制代码质量,进而影响团队的开发效率。

如果能够给项目引入ESLint + Prettier,我还是建议大家都引入,可能会提示上百个警告或者错误,但等你根据配置规则一条条规范代码之后,你就会发现代码变得清晰有条理了,还能帮助你避免在开发过程中,出现的一些低级错误。

若是引入的代码风格检测插件的风格过大,需要你自己去处理前人,甚至N个前人所遗留下来的代码,那该怎么办呢?我这里有些心得可以分享给你,我们都知道,项目越庞大复杂,任何一处对现有业务逻辑的改动都有可能引发我们无法想象的风险,运气好的话可能项目直接报错,能够伸根其源找到问题所在,运气不好可能就埋下一个我们意识不到严重bug。所以也有人会通过在原来的代码上“包”一层自己代码的方式,进行新的业务逻辑编写,写一个简单代码进行举例:

代码语言:txt
复制
// “包”一层进行新的业务逻辑处理 - 判断一键领取的优惠券是否过期
isCouponOver(couponIds) {
  if(couponIds) {
    ...
    this.getUserCouponList(couponIds)
  }
},
// 原有的业务逻辑 - 一键领取所有优惠券
getUserCouponList(couponIds) {
  ...
}

这个例子或许并没有这么贴切,只是给大家演示一下这种方式,而实际业务中要比这种情况复杂很多,“包”的代码层级甚至会更多,你永远无法想象有些人为了修复一个bug,写出多么天马行空的代码来,或许能够解决当前的某个bug,但也给了后期维护代码的成本增加了难度。

我的做法是,在原有的功能模块上进行修改,修改过程要注意所有引入该模块的地方是否有影响,同一段代码,在不同场景下会有不同的作用,从首页进入商品详情页和从分享链接进入的商品详情页,都需要我们做好区别。如果原有的功能模块代码质量太差、无法满足新需求迭代,修改需要花费的人力/时间成本太高,那我建议你自己重新编写该模块。不管你采用的是哪种方式,如果模块的耦合度很高,请记住要让测试的同学帮忙走一遍整个业务流程。

  • 为什么要写注释/readme?

上面我讲了怎么去改动别人的代码,其实讲的就是代码的可维护性,如果代码的可维护性很高,同时具有易扩展的特点,那我们更本不需要去进行改动。从整个项目可维护性考虑,我想补充说一下注释/readme的问题,这也是大家最容易忽视的一个地方。

写注释是为了让团队的其他人能够明白你写的代码实现了什么功能,判断条件是什么等等,如果你起的变量名方法名能够让人一眼就知道你的代码功能,如const couponStyle = {} getCouponList(){},那不写也没关系;但并不是所有人都能按照NEC或JS命名规则进行变量命名,所以还是建议大家能写一下注释,这不仅是方便别人阅读你的代码,更是方便你自己review自己写的代码。我曾问过一个同事他所写的某个方法的作用是什么,他翻来覆去看了半天,结果问了一句——“这个代码是我写的吗?”,我相信这个场景并不止有我一个人遇到过。

和注释不同,readme更多的是对整个项目的一些记录,github上有很多优秀的readme范本,大家可以多找来学习,我自己会习惯在readme记录一些开发过程中需要注意的问题,比如各端兼容性问题,头条小程序不支持将rpx写入background简写中,统一将背景图片尺寸分开写:

代码语言:txt
复制
// 不支持
background: red url('xxx.jpg') 97% center / 10rpx 6rpx  no-repeat;

// 修改成
background: red url('xxx.jpg') 97% center no-repeat;
background-size: 10rpx 6rpx;

还有就是统一方法,比如路由跳转使用绝对路径,避免自定义组件引入时造成文件层级不一致;甚至还可以把todo列在其中,以免在后续开发过程中遗忘。

  • UI组件库的引入

现在的项目基本上都会引入开源的UI组件库,企业有自己组件库的就排除在外了,而至于选哪个UI组件库,是根据自己项目贴合程度进行选取的,不要人云亦云的选取热门框架。需要注意的是,选取稳定版的组件库,如果不影响业务需求,尽量不要进行升级。可以根据项目具体需求,对组件进行了源码修改,修改原则同样也是以易维护、可扩展,所涉及的组件和相应的API描述可以写到readme中。

接下来谈谈自定义组件部分,我习惯将外部引入的组件和自己编写的组件存放到不同的文件夹中,不要求能把注释和API写成开源组件那样详细,但至少把参数含义、事件、调用方法写清楚。

代码语言:txt
复制
┌─components          符合vue组件规范的uni-app组件目录
│  ├─address
│  │  └─address.vue   地址联动组件
│  └─heade
│     └─header.vue    导航栏组件
├─uview-ui            uView UI框架
└─README.md           readme文档
  • API抽离

大多数同学都知道把公共方法进行抽离,放到public文件夹中,但能做到把API抽离进行统一管理的就很少。在某些页面或许只需要请求很少的接口,有些同学可能就直接把请求写到页面中,但等到新的迭代或需求,涉及到更多接口,再把请求写到页面中就不太明智了。将所有API统一抽离,根据模块进行划分,这样能方便我们后期管理,若是API变更,我们也仅仅只需要修改API层,而并不会动到业务逻辑层面,更新的render.js也会很小

代码语言:javascript
复制
const baseUrl = 'https://cloud.tencent.com'

export function getCouponList(userId) {
  return axios.post(`${baseUrl}/api/getCouponList?userId=${userId}`).then(res => {
    return Promise.resolve(res.data)
  })
}

以上就是我自己的一些收获,在大部分项目中,往往是没有足够的时间让前端开发去设计目录结构的,我们需要从需求出发,抽象业务特性、预判业务发展方向,能够引导后续业务按照一定的规范迭代,​希望大家都够把应对业务中频繁的业务变更能力,在项目目录结构设计中得以实现。

原创声明:本文系作者授权腾讯云开发者社区发表,未经许可,不得转载。

如有侵权,请联系 cloudcommunity@tencent.com 删除。

原创声明:本文系作者授权腾讯云开发者社区发表,未经许可,不得转载。

如有侵权,请联系 cloudcommunity@tencent.com 删除。

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
相关产品与服务
Prowork 团队协同
ProWork 团队协同(以下简称 ProWork )是便捷高效的协同平台,为团队中的不同角色提供支持。团队成员可以通过日历、清单来规划每⽇的工作,同时管理者也可以通过统计报表随时掌握团队状况。ProWork 摒弃了僵化的流程,通过灵活轻量的任务管理体系,满足不同团队的实际情况,目前 ProWork 所有功能均可免费使用。
领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档