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

前言

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

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

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

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

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

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

望各位大佬不嗇賜教。redux

項目架構

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
複製代碼

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

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

目錄職責

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

pages 層之外的公共邏輯

畢竟是MPA應用,因此一切還都是圍繞着 pages 展開。

action-log

首先這裏的action-log目錄就很少說了,由於沒有太多可借鑑性。大概就是返回一個 ActionLog對象,來進行一些業務上的埋點、信息收集等邏輯的處理。因此這裏若是你們有一些公共的基礎類封裝,都是能夠放這裏的。

common

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
複製代碼

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

除了一些從命名能夠區分出來的utils 之外,這裏還放了一個 type.tsconstants.ts,用途自如其名。

components

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

modules

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
複製代碼

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

而這裏modulesconponents最大的區別就是,複雜度和內部狀態管理。若是內部狀態較爲複雜,且有不少的交互,那麼咱們就稱之爲 module.是的,這裏的界限,咱們劃分較爲模糊。

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

紅色框就能夠理解爲 module,綠色框能夠理解爲 components

page 的組織

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

目錄區分的並非不少,可是也都較爲清晰。簡單介紹下每一個區域的分工,須要展開的,咱們在後續展開介紹

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

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

頁面數據流向和管理規則

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

由於是業務代碼,因此這裏就不會粘貼太多代碼了

簡單的解釋下上面的流程

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

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

interface

首先咱們須要定義每個模塊的 props,畢竟是由於用的 ts註釋即文檔。因此咱們將每個 componentsprops 都定義到 type 目錄中,畢竟不少時候接口返回的數據,須要咱們作一次 format,而這個 format 的目的就是爲了 components 更好的使用。換句話說,這些接口,可複用! 那必然定義到外面

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

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

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

由於有 type 的定義和 components 之間的約束,因此不管是componemts 內部的數據使用仍是 index.tsx 裏面的模塊引入時 props 的注入,都有很好的約束

編寫時候的提醒

漏寫時候的報錯

組件通訊

因爲咱們使用了 hooks,且相對隔離的組件劃分,原則上,組件通訊其實並非不少。固然,也必然是有的。

其實這方面的約束主要歸結於業務的複雜度,若是數據邏輯比較複雜,且通訊較多。那麼能夠考慮使用 useContextuseReducer

說下此次需求中涉及到的通訊。

原則:組件儘量值管理本身的狀態。

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

約束

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

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

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

結束語

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

由於本文不方便粘貼太多代碼,因此可能說的有些雲裏霧裏,有任何疑問,歡迎公衆號內回覆【1】,加入全棧技術交流③羣,一塊兒交流

最後,本文只作一個拋轉,並不是定義一種規範。更多的約束和組織,但願你們多多交流,互相學習。

學習交流

  • 關注公衆號【全棧前端精選】,每日獲取好文推薦
  • 添加微信號:is_Nealyang(備註來源) ,入羣交流
公衆號【全棧前端精選】 我的微信【is_Nealyang】
相關文章
相關標籤/搜索