鐵汁們聽好了哈——從本篇筆記開始,將進入實際的鏟💩演練啦!
這首先要被剷除的💩,是分佈在我接手官網項目後最先接觸的用户個人主頁裏。
在這系統中,用户分個人與項目方兩種,它們的個人主頁雖看起來一樣,但視覺細節和數據結構等還是有些差異的,佈局結構大致如圖所示:
個人主頁原先代碼中,其他部分還算有點封裝意識,但最重要的課程、活動等列表部分的代碼看起來就是初級水平,將它們全部糅合在一個 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,完成第一次的鏟💩演練!
本文其他閲讀地址:小紅書|微信公眾號