一張頁面引起的項目架構思考(Rax+Typescript+hooks)


一張頁面引起的項目架構思考(Rax+Typescript+hooks)


前言

好的書本分章節、好的代碼分模塊,那麼好的架構該如何定義呢?

咳咳,不要意思,題目起大了~~ 小生之輩,豈敢以架構而論。

不過話說來,很多人都認為前端無非就是 HTML+CSS+JS,一個目錄一類文件,有何架構可言。但是我想說。。。。你說的都對!


一張頁面引起的項目架構思考(Rax+Typescript+hooks)


但是,筆者一直在探索不同的頁面架構組織形式,鄙人愚見,好的架構,能夠方便拓展和開發以及後期的項目維護。

在筆者剛開始接觸前端的時候,就一直在思考怎麼樣的架構比較舒服易於擴展,且能裝 B。React-Full-Dianping-Demo裡面就有寫到對於react+react-redux+soga的一些列代碼組織的思考:react技術棧項目結構探究

一直還在學習,本文也只是拿來探討下本次我開發一個頁面時,我個人的一些代碼組織方式。拋個磚~

望各位大佬不嗇賜教。


一張頁面引起的項目架構思考(Rax+Typescript+hooks)


項目架構


一張頁面引起的項目架構思考(Rax+Typescript+hooks)


<code>src
├─ action-log
│ ├─ constants.ts
│ └─ index.ts
├─ app.js
├─ app.json
├─ common

│ ├─ animation-utils.ts
│ ├─ business-utils.ts
│ ├─ constants.ts
│ ├─ detail-utils.ts
│ ├─ mtop-utils.ts
│ ├─ net-utils.ts
│ ├─ price-utils.ts
│ ├─ storage-utils.ts
│ ├─ string-utils.ts
│ ├─ time-utils.ts
│ ├─ type.ts
│ ├─ url-utils.ts
│ └─ utils.ts
├─ components
│ ├─ loading-page
│ │ ├─ index.css
│ │ └─ index.tsx
│ └─ pm-bottom
│ ├─ index.css
│ └─ index.tsx
├─ document
│ └─ index.jsx
├─ event
│ └─ EVENTS.ts
├─ modules
│ ├─ bottom-action
│ │ ├─ index.css
│ │ └─ index.tsx
│ └─ page-container
│ ├─ base
│ ├─ decorator
│ ├─ index.tsx
│ └─ libs
└─ pages
├─ buyer-identity
│ ├─ components
│ ├─ constants
│ ├─ customized-hooks
│ ├─ index.tsx
│ ├─ types
│ └─ utils
複製代碼/<code>

或許上面看起來並不是很直觀,截圖解釋下


一張頁面引起的項目架構思考(Rax+Typescript+hooks)


一張頁面引起的項目架構思考(Rax+Typescript+hooks)


大概的看下,腦海中有個大概的位置和每個文件的作用。下面我們再來細品

目錄職責

其實劃分了這麼多的目錄,無非就是為了最大可能的複用。其中也包括對於組件狀態的抽離、hooks 特性的利用

pages 層以外的公共邏輯

畢竟是MPA應用,所以一切還都是圍繞著 pages 展開。

action-log

首先這裡的action-log目錄就不多說了,因為沒有太多可借鑑性。大概就是返回一個 ActionLog對象,來進行一些業務上的埋點、信息收集等邏輯的處理。所以這裡如果大家有一些公共的基礎類封裝,都是可以放這裡的。

common

<code>common
├─ animation-utils.ts
├─ business-utils.ts
├─ constants.ts
├─ detail-utils.ts
├─ mtop-utils.ts

├─ net-utils.ts
├─ price-utils.ts
├─ storage-utils.ts
├─ string-utils.ts
├─ time-utils.ts
├─ type.ts
├─ url-utils.ts
└─ utils.ts
複製代碼/<code>

由於該項目的比較複雜,業務邏輯相對較多。所以這裡我將 utils按照類別,區分出來了以上幾種。方面後期開發中的維護和擴展,也便於查找。

除了一些從命名可以區分出來的utils 以外,這裡還放了一個 type.ts和constants.ts,用途自如其名。

components

相信框架使用者對於 components 的命名都不為陌生.是的,就是對於一些公共組件的封裝,比如我這裡放的兩個組件loading-page,pm-bottom等公共組件。components 相對來說是比較“小”的概念,劃分依據這這個項目中也比較簡單,就是是否為“木偶組件”(雖然 hooks 了以後,咱不太適合這麼說),

modules

<code>modules
├─ bottom-action
│ ├─ index.css
│ └─ index.tsx
└─ page-container
├─ base
│ ├─ base.tsx
│ ├─ error.tsx
│ └─ scrollBase.tsx
├─ decorator

│ └─ withError.tsx
├─ index.tsx
└─ libs
├─ displayName.ts
├─ navbarTransparent.ts
├─ spm.ts
└─ title.ts
複製代碼/<code>

更具有模塊的概念,這裡最典型的page-contaienr的模塊,作用就是每一個頁面的通用底層容器,早在之前的文章中其實有介紹到這個容器,如何用 Decorator 裝飾你的 Typescript,所以這裡就不再贅述了,其實就是一些基礎功能的封裝。所以也就是解釋了event的目錄存在。

而這裡modules和conponents最大的區別就是,複雜度和內部狀態管理。如果內部狀態較為複雜,且有很多的交互,那麼我們就稱之為 module.是的,這裡的界限,我們劃分較為模糊。

但是當你拿到一份設計稿的時候,估計就能明白我的良苦用心了~


一張頁面引起的項目架構思考(Rax+Typescript+hooks)


紅色框就可以理解為 module,綠色框可以理解為 components

page 的組織

針對單個頁面裡面的組織,其實都大同小異。(突然發現前端架構沒有太多可言)


一張頁面引起的項目架構思考(Rax+Typescript+hooks)


目錄區分的並不是很多,但是也都較為清晰。簡單介紹下每個區域的分工,需要展開的,我們在後續展開介紹

  • index.tsx 頁面的入口文件,但是本身裡面不會編寫太多業務邏輯
  • utils 該頁面的工具函數,包括接口的請求、數據的 format等
  • customized-hooks 自定義hooks,這裡有兩個,初始化 UI 所需要的數據(邊距等),業務請求的數據。
  • constants 頁面的常量,包括請求的 api、spm 埋點、固定的一些該頁面業務數據等
  • components 該頁面的組件(注意這裡沒有 module,因為太多了真的容易混亂),頁面的 components,有簡單的,也有複雜的。


一張頁面引起的項目架構思考(Rax+Typescript+hooks)


以上就是一些目錄結構和代碼組織的交代。其實還是比較簡單清晰的。下面介紹下

頁面數據流向和管理規則

碎碎叨叨道不到個明明白白


一張頁面引起的項目架構思考(Rax+Typescript+hooks)


因為是業務代碼,所以這裡就不會粘貼太多代碼了

簡單的解釋下上面的流程

初始化 UI 的邏輯比較偏於業務,其實沒有太多可借鑑的。這裡我代碼裡面的工作也就是適配 iPhone X的一些UI。

重點說下初始化接口數據的過程吧。其實也就是各個頁面中的 components 的狀態初始化

interface

首先我們需要定義每一個模塊的 props,畢竟是因為用的 ts,註釋即文檔。所以我們將每一個 components 的 props 都定義到 type 目錄中,畢竟很多時候接口返回的數據,需要我們做一次 format,而這個 format 的目的就是為了 components 更好的使用。換句話說,這些接口,可複用! 那必然定義到外面


一張頁面引起的項目架構思考(Rax+Typescript+hooks)


注意接口上都要寫註釋啊!!!!理由如下:


一張頁面引起的項目架構思考(Rax+Typescript+hooks)


一張頁面引起的項目架構思考(Rax+Typescript+hooks)


將所有數據處理的方法,全部放到 utils 中(注意數據兜底的處理,這裡我所有的數據處理都寫好工具函數,並添加充分的單元測試)


一張頁面引起的項目架構思考(Rax+Typescript+hooks)


真正的做到對 components 而言,開箱即用

因為有 type 的定義和 components 之間的約束,所以無論是componemts 內部的數據使用還是 index.tsx 裡面的模塊引入時 props 的注入,都有很好的約束


一張頁面引起的項目架構思考(Rax+Typescript+hooks)


編寫時候的提醒


一張頁面引起的項目架構思考(Rax+Typescript+hooks)


漏寫時候的報錯

組件通信

由於我們使用了 hooks,且相對隔離的組件劃分,原則上,組件通信其實並不是很多。當然,也必然是有的。

其實這方面的約束主要歸結於業務的複雜度,如果數據邏輯比較複雜,且通信較多。那麼可以考慮使用 useContext 和 useReducer

說下這次需求中涉及到的通信。

原則:組件儘可能值管理自己的狀態。

遵循如上原則,最終的業務交互邏輯都是由組件內部管理,涉及到的同級通信則通過父組件操作。而父組件操作的原則就是只拿數據,不做任何業務處理。(儘可能的撇清關係)

約束

  • index儘可能不寫業務邏輯
  • UI 初始化和模塊數據初始化需自定義 hooks
  • 狀態儘可能抽離。component 過於複雜需額外抽離 component 、 utils 和 customized-hooks 等。參照上文
  • component 的 props 需抽離複用
  • 公共 utils 方法編寫充分的單元測試
  • 公共 utils 的方法導出需單獨導出(bundle 大小),且編寫註釋(調用時候的提醒)
  • 儘可能定義 interface,並且編寫註釋.畢竟註釋即文檔

以上約束後期應該都會編寫相應的 Eslint 來進行強約束(咳咳,程序猿基本素養不可靠)

最後看下我正在補充的單元測試,編寫單元測試過程中,的確發現了不少工具函數的邊緣情況處理的有問題


一張頁面引起的項目架構思考(Rax+Typescript+hooks)


一張頁面引起的項目架構思考(Rax+Typescript+hooks)


結束語

按照如上的 page 代碼組織後面又寫了一個頁面,感覺代碼的組織和狀態的管理還是較為清晰的。後續會編寫相應的 cli 來自動生成頁面基礎架構,比如 pmCli add page or pmCli add com

最後,本文只做一個拋轉,並非定義一種規範。更多的約束和組織,希望大家多多交流,互相學習。


一張頁面引起的項目架構思考(Rax+Typescript+hooks)


學習交流

  • 關注公眾號【全棧前端精選】,每日獲取好文推薦


原鏈接:https://juejin.im/post/5e705e4ee51d45270b7d7bb2


分享到:


相關文章: