头图

铁汁们听好了哈——从本篇笔记开始,将进入实际的铲💩演练啦!

这首先要被铲除的💩,是分布在我接手官网项目后最先接触的用户个人主页里。

在这系统中,用户分个人与项目方两种,它们的个人主页虽看起来一样,但视觉细节和数据结构等还是有些差异的,布局结构大致如图所示:

「个人主页」页面布局结构

个人主页原先代码中,其他部分还算有点封装意识,但最重要的课程、活动等列表部分的代码看起来就是初级水平,将它们全部糅合在一个 React 组件里:

暗中埋「雷(💩)」的代码

这一眼望去十分杂乱,看不出来一二三,无法直接识别出哪块代码是干啥的,因而改造的思路也十分清晰——拆分,封装。

根据个人页面的展示与整个网站的功能,我先在 domain 下划分出四个模块:

  • bounty——赏金任务;
  • challenge——线上、线下活动;
  • course——在线课程;
  • quiz——答题。

再将个人主页代码中的每个列表渲染代码封装成列表视图组件放到上述相应模块中,数据请求也封装成异步函数放进各模块的 repository.js 中:

重构后的目录结构

因每个列表的交互逻辑一致,都是点击「More」加载下一页,进一步封装了个通用的 LoadableList 组件放进 shared/components 文件夹,使得列表视图代码更为简洁清晰:

import LoadableList from '@/components/loadable-list'

import { fetchEnrolledCourseList } from '../../repository'
import EnrolledCourseItem from './EnrolledCourseItem'

function EnrolledCourseList({ list }) {
  return (
    <div>
      {list.map(({ series }, idx) => (
        <EnrolledCourseItem key={`course-${idx}`} data={series} />
      ))}
    </div>
  )
}

function EnrolledCourseListView({ params }) {
  return (
    <LoadableList
      params={params}
      fetch={fetchEnrolledCourseList}
      resolveResponse={res => ({ list: res.data.list, total: res.data.count })}
      renderList={list => <EnrolledCourseList list={list} />}
    />
  )
}

export default EnrolledCourseListView

接着,把切换列表视图的控制等逻辑封装进 ActivityTabList,并把列表视图数组声明式传给它,个人主页代码改造后大概是这样:

import EnrolledCourseListView from '../../../course/views/enrolled-course-list'
import EnrolledChallengeListView from '../../../challenge/views/enrolled-challenge-list'
import AppliedBountyListView from '../../../bounty/views/applied-bounty-list'
import AnsweredQuizListView from '../../../quiz/views/answered-quiz-list'

import ActivityTabList from '../../widgets/activity-tab-list'

// 列表视图声明
const tabs = [
  { text: 'Course', view: EnrolledCourseListView },
  { text: 'Challenge', view: EnrolledChallengeListView },
  { text: 'Bounty', view: AppliedBountyListView },
  { text: 'Quiz', view: AnsweredQuizListView },
]

function IndividualProfileView({ user }) {
  return (
    <div className="pl-[410px] pb-14 pr-14">
      <ActivityTabList userId={user.user_id} tabs={tabs} />
    </div>
  )
}

export default IndividualProfileView

可以看到都是相对路径引用,是因为在 domain 下新增了个 profile 模块专门用来处理个人主页相关功能,且同为 domain 下的文件约束为只能用相对路径引用。

最后,在 app 下的原文件中代码改为:

'use client'

export { default } from '#/domain/profile/views/individual-profile'

如此一来,就将个人主页相关功能的核心代码从 app 转移到了 domain,完成第一次的铲💩演练!


本文其他阅读地址:小红书微信公众号


欧雷
119 声望15 粉丝

致力于前端标准化、工业化,让前端开发更加有序且可快速装配,为新型开发方式及协作模式做支撑。