Leaa is a monorepo CMS (Content Management System) built with Nest.js, Next.js, and Ant Design.
View the README.md of each sub-directory in packages . You may need to look at yarn workspaces first.

TODOS
本來總結應該寫在文末,但我感覺還是提上吧,起碼不用看我嘮嘮叨叨一堆開發日誌。
以前老想著自己寫一個全棧項目試著打通5 端,苦於沒時間,一直拖著,寫的時候還以為需要大半年,但沒想到現在只花了一個半月就做好了,而且很多地方我還最求了最佳實踐,總體而言還算比較滿意。
項目初衷是想用React或者說主要是JSX的語法來做更多的事情,比如寫小程序或者App,而且現有的技術框架也支持我這種想法,拿著以前的已有經驗配上一些較新的技術比如GraphQL就開始上路了。
在api , dashboard , www這幾個端上碰到問題不算多,但miniprogram (小程序,下文简称mp)和app上就沒那麼幸運了,因為他們都不是標準的web 语言,類似HTML 富文本渲染這種在web上天然支持的功能,到了他們上面就變成fuckingSelf需要自己解析了,比如a鏈接,因為在mp和app中並沒有a鏈接的說法,用戶點擊a後會發生什麼完全由開發者自己決定,這和我以前開發的「 web應用」完全是兩種概念。倘若以前有過App開發經驗,相信要躺的坑會變少許多。
說到坑,我覺得我這一手坑挖技能真是了得。 RN以坑多而紅想必已人盡皆知,好,我選了。 monorepo的坑大家可能不了解,但的確也是能把人坑得死去活來的,好,我選了。用TS開發RN的坑不多,但也不少,好,我也選了。然後就變成了選擇了這個RN + monorepo + TS超級大坑(哭),不過後面我還是一點點的躺了過來,實在佩服自己的耐心(攤手)。
為什麼會選monorepo這種方式開發呢?我的初衷是5 端共享TS 的interface和一些可複用的配置,但是後來寫mp和app的時候發現,由於他們的一些特殊機制,我沒辦法給他們共享。實際上mp和app算是和monorepo完全隔離的,如果後面我重構代碼,我會把這些「非标准web 应用」單獨放一個repo,因為他們真的很難伺候, node_modules也是自有一份無法共享,每份體積都很大。大到不是關鍵,關鍵是每次yarn install的時候非常非常滿,CPU 狂飆感覺電腦都要起飛了。本來我是傾向於能用yarn workspaces解決的mono就不用lerna ,但因為這個問題我嘗試著上了lerna ,可問題似乎沒得到好轉,只好作罷。這一次用monorepo真真切切的給到了經驗,算得上是拳拳到肉的疼,也讓我知道如何取捨mono和multi 。
好,如果現在讓我寫一個5 端難度排行,我認為會是這樣mp > app > www > api > dashboard 。
為什麼會把mp列為最難的部分?因為mp不單有很多私貨,而且devtools 也bug 多得出奇,有時候我修一個bug 修半天沒好,結果重啟一下devtools 就好了,這個真的是要氣吐血。而且因為我用了Taro ,很多新的功能比如custom-tab-bar沒跟上,文檔都沒有,我自行摸索弄是弄出來了,但也花了不少時間。當然,如果你用Taro同時有custom-tab-bar這個需求, leaa可能是目前全Github 已有方案的最優解。
另外關於www ( Next.js v9) 我本來也有很多想說的,但隨著時間流逝,這些想說的慢慢變消失了,而這種「不想說」並不是那種「難者不會會者不難」的不想說,而是因為Next.js坑太多,解決一個坑必定會引發另外幾個坑,而且官方都沒有什麼最佳實踐給你參考,都是一些簡單的example ,一旦想要做一些複雜的功能,這種前後端都要處理的「SSR」的確讓人有種「難言之隱」的感覺。隨著每一次Next.js大版本的變動如8to9,都會有很多斷崖式的改動,沒辦法啦,zeit 的文化就是這樣,只能用「一切的不如意都源自於自己不夠強大」來安慰自己。
出於monorepo的原因,有非常多「文件名相似」的文件在一個項目裡,很多時候有種被文件淹沒的感覺,在找文件的時候很容易被干擾,即便是我放棄了用Components/Filter/index.tsx改為用Components/Filter/Filter.tsx去給文件命名,以求cmd +。 p ,能快速定位到文件本身而不是目錄,但也難以擺脫這種「文件地獄」的感覺。
本來說好寫總結就不要不抱怨了,但現在看來多多少少還是有一些吐槽,Anywhere,從Docker到Api再到UI/UX ,寫leaa過程的確讓我學到很多,對軟件架構、開閉原則有了更深的了解,以前寫項目覺得「編碼」與「建築」其實是做著同一件事情, 這次算是更深刻的體會到了。
目前leaa還有很多很多很多bug,但這似乎不妨礙有需要的人通過Github 上檢索到leaa中對他們有用的代碼,這也是我寫leaa的初衷,以上。 2019-09-17 17:01 @ Guangxi Hezhou
從git commit 可以看出,這篇DEV LOG(開發日誌)是現在才開始寫的,項目本來叫做1d1h,也就是一天一小時的意思,想著業餘時間把之前寫前後端的經驗匯集起來,做個Blog -> CMS -> Sohp 的開源項目,包括API / Dashboard / Website / Wechat Weapp / React Native (iOS / Android),因為是一套monorepo,類似interface / entry 這些都是共用的所以感覺做成全平台也是一件很順手的事情。
其實本來想早點寫這個開發日誌,但早期一大堆需要解決的問題,時間都用在開發上了,實在抽不出時間寫記錄,現在想想還真不應該這樣,畢竟之前的一大堆問題如果記錄下來了,其實就是隱形財富,雖然再次遇到了自己肯定懂如何解決,但就沒辦法share 給其他人了。不過接下來的日誌我會慢慢回顧就對了。
這裡說一下我對Dashboard 的理解吧,我覺得一個最小可用的的Dashboard 應該包括。
這幾個模塊寫完基本上就可以拿來當Blog 用了,特別是角色權限這塊,如果有業務需求,基於這樣的最小化的Dashboard 開發基本上可以說也很簡單了。我在以往的項目里處理權限已經很多次了,不過這次因為是graphql,和之前的restful 稍有區別,還是花了一些時間折騰的。
用Nest.js 寫了那麼多的代碼,其實算不上舒服,選用的原因其實還是看中了他的一整套範式以及武裝到牙齒的Typescript 支持。作者@kamilmysliwiec 還是非常厲害的,Nest.js 的一些封裝實現非常精妙,最重要的還與各種技術相結合,落地了很多業務場景,這點真的非常讚的。
dashboard上技術選型時常見的React + Antd,不過這次因為全面上了hooks ,包括Apollo 都是最新的hooks beta 版本,整個項目幾乎見不到Class,但在大規模使用hooks 後,感覺代碼長得實在難看,如果以前Class 代碼清晰度打10 分的話,hooks 只能打5 分。當然,最明顯的應該是賺了一個代碼Fn 共享,換做是Class,想要share Class 的Fn,還是挺麻煩的。
www部分沒得選,只能是Next.js 了,其實之前我有自研過一套較為完備的React-SSR,但為了順應浪潮,加上@Guillermo 神在推上天天天吹,忍不住還是入手了Next.js。我開始寫www 的時候剛好趕上Next.js v9 發布,這是一個從core 就開始用TS 重寫的船新版本。本以為用起來會很順利,但沒想到還是坑了……
畢竟需要集成Antd,即意味著,Client 自己的pages 代碼需要對less 用cssModule,Antd 則不用,Server 那邊則是看到less 就扔。所以官方提供的withLess 插件最多只能管60%,剩下40% 支持不到位。本來像Next.js,CRA 這種就是把webpack 包起來,前端毒瘤真不想你碰,配一下都是炒雞麻煩。
但,我想說一個框架在項目初期給你幾倍便利,那麼它便會在項目後期給你帶來幾倍麻煩。 CRA 如此,expo 如此,Next.js 也不例外,都是黑盒。那麼我必須在兩個小時內寫一個100% 符合我預期的withPlugin 來,不然項目就卡了。翻了翻Github 想看看有沒有解決方案,但很不幸, v9 剛出根本找不到相關代碼,看起來,只能fuckingself 了。我雖對webpack 很熟,但這Next.js 在webpack 上加了薄薄一層黑盒,寫withPlugin 有種被淹沒在未知的context 海洋中,是種非常憋屈的趕腳,不過還好,最終半小時搞定。提了個自帶resolve 的issue 趁沒被人發現趕緊close 掉。希望給碰到同樣問題的伙計在搜issue 的時候帶點幫助,畢竟需要Next.js + antd withLess 的人還是很多的,特別是國內。
時間過得好快,轉眼半個月,最近沒給leaa 寫什麼新東西。重點放在了阿里雲OSS 整合這塊。想要實現這樣一個功能:
其中過程還蠻艱辛的,涉及到Local 和OSS 之間的一些交互,而且因為直接走OSS,所有請求不經API,變成了等待OSS 的Callback,必須保證任何一步沒做完都不能動DB,勉強達到了冪等。 其實如果上傳都走API,然後由API 統一處理再put 到OSS 會簡單非非非非非非非常多,我這麼做主要是擔心做某些活動的時候,如果涉及到上傳文件,並發就會很大,服務器緩不過來。所以拿OSS 先擋一下還是很有必要的。
基本上www 和api 以及dashboard 就告一段落了。明天開始miniprogram 。
剛整理package 的時候發現React 升級到了16.9.0,console 下一堆類似的Warning: componentWillMount... ,看了一下React CHANGELOG 發現的確是大改,未來版本要廢棄幾個lifecycle 。由於leaa-dashboard 依賴antd ,所以還是等antd發版消除了這些warning,再升上去。目前React 是鎖在"react": "16.8.6", "react-dom": "16.8.6" 。
做了一個Leaa Stack 的Banner 放到README 頂部,用圖片描述使用的技術比文字好不少。另外提一下Leaa這個名字,這其實是我喜歡的一個法國女演員Léa Seydoux 的名字,避免重名率過高,我在Lea 後面多加了個a。不過LEAA在Google 最多的指向是Law Enforcement Assistance Administration美國一司法機構(笑)。
剛在用lint 在給項目做全面檢測發現了幾個error,比較有趣的是packages/leaa-dashboard/src/pages/Permission/PermissionList/PermissionList.tsx L159 這裡,項目.prettierrc的printWidth和.eslintrc.js的max-len都設置成了120 ,但這裡prettier 不報錯,也不自動格式化,但是eslint 和我說這裡超120 了。
我只好加了個eslint-disable-next-line max-len ,感覺很有可能他們其中一個是用了>一個是>= ,但是我去修改了兩者的屬性後發現不是這個問題,算了,先加個max-len,目前只有一處是只有,標本不夠就先不處理了。待日後這個問題多了再統一處理。
雖然自己很注意style code,也會用IDE 配合keymap 寫marco 套用prettier和eslint規則做format。但項目public 之後可能會有contributors 進來(不,不會的hhh),覺得還是在git commit卡一下code style 會比較好。
通常項目上一個husky就夠了,但是monorepo 文件那麼多,每次git commit全packages 所有文件都eslint必然會卡到爆,所以肯定是要配合lint-staged做最小化eslint 處理的,只讓此次git stage 中文件去跑eslint。
可是貌似官方沒有給出太多針對monorepo 的建議和範例。摸索了一番,發現其實也不麻煩,只是和non-monorepo 不大一樣而已。為了和pacakge.json解耦我還特意寫成配置文件,大致長這樣:
module . exports = {
'packages/**/*.ts?(x)' : [ 'prettier --write' , 'eslint' , 'git add' ] ,
'packages/**/*.(css|less)' : [ 'prettier --write' , 'stylelint' , 'git add' ] ,
} ;試了一下,速度還是蠻快的。要有更好的最佳實踐可能還得用一段時間才知道效果了。
試了大概一個晚上的Taro ,感覺不是特別理想,為什麼呢?首先我需要的是一個React to小程序的框架,而且想要的是ONLY小程序,至於為什麼是ONLY,後面我會展開詳細說明。
初步使用下來,感覺Taro感覺是一個集大成者,他身上的責任還蠻重的,需要兼容太多的类小程序環境,比如支付宝小程序,今日头条小程序等…… 而且還要考慮兼容RN那不友好的yoga CSS 引擎,團隊還是非常不容易的,能做到這這個程度,我還是非常佩服的,這裡必須先給個贊。接下來我講一下我幾小時下來大概的感觸。
完美!正常Web 開發一樣,沒什麼好說的。支持HRM,支持css module。不用關心webpack ,上來就能run。不過有一點值得注意,就是如果想要兼容RN ,那就不能用taro-ui或是別的什麼第三方UI lib,只能使用內置的@tarojs/component ,這個限制感覺卡得比較厲害,期待taro-ui早日支持RN 。
也非常完美,說不上沒什麼不好的地方,run 起來後,打開官方微信debug tools 順利走起。唯一坑點是對monorepo支持不友好,當然這點也無可厚非,國內本來用monorepo的就少,用了肯定要自定義為「自己有能力解決monorepo上的任問題」的態度。我在monorepo下run,遇到的是這個問題:
can't find module : ../../../node_modules/@tarojs/taro-weapp/
社區上也有一些人在提issues 比如需要monorepo 支持,我的做法和他差不多,都是用yarn wokespaces 的nohoist 去做處理,只不過我的方案是只讓Taro相關的模塊保留在sub-package 下,別的該提升還是提升,最大化share 了modules:
{
"nohoist" : [ " **/@tarojs/** " ]
}看package.json裡的有dev:rn ,我就run 了,結果是好的,看到提示編譯成功,但就沒有下文了…… 然後去官方docs 看了下,感覺略複雜,那這和單獨折騰一套原生RN 開發有什麼區別?而且依賴Taro的話, RN版本鎖在0.55.4 ,天啊!這和官方目前0.60.x的版本號相距甚遠,要知道RN每一個版本迭代都是質的飛躍,如果用上0.60.x還能在Android 上賺一個Hermes,效率也是大幅提升。另外還有一個讓我顧慮的是,用上RN@Taro ,意味著只能使用@tarojs/component這個UI lib,也就是意味著要放棄掉NativeBase和Shoutem這兩個在RN上相對優質的UI lib。
嗯…… 綜上考慮,如不是一心想為了節約成本和時間,想著一套代码多处运行,目前還是建議放棄RN@Taro ,如果要說一個最佳的切入時機,我認為是至少taro-ui支持了RN ,當然,這個代價實在太高,官方永遠不去做支持也是非常有可能的。
好啦回到正題,我使用Taro的初衷一開始就是用來ONLY for 小程序的,所以對於目前的情形我覺得「一切OK」。 leaa-app那邊還是RN或者expo處理就好,畢竟坑基本上在以往項目踩完了(笑)。
記錄一下今天白天用Taro的心得,真是滿滿的心酸啊……
首先,比較痛苦的是不支持@apollo/react-hooks和react-apollo !也就是說,任何Apollo 官方的包都不可以用了!不能useQuery連<Query>都不讓,用就給你報hooks 那經典的錯誤Invariant Violation: Invalid hook call.結果是直接用寫好export apolloClient後apolloClient.query() ,這真是一夜回到解放前啊!
本來想著方便,在H5模式下debug, apolloClient這種方式能跑起來已經很開心了,沒想到……小程序模式彈error 了,說fetch is not found globally and no fetcher passed, to fix pass.... 」這……,還好,馬上找到了前輩寫的lib wx-apollo-fetcher。整個庫就幾行:
return new Promise(resolve =>
wx.request({
...
complete: ({ data, statusCode, errMsg }) => resolve({...})
}))
然後在HttpLink那邊替換一下變成fetch: wxApolloFetcher就好了。萬萬沒想到微信還會做這種斷崖式更新,真是騷操作。
再就是路徑alias的問題,官方issues 這貼討論得最激烈,我看完後試了,依然無解。這裡的無解是小程序端無解, H5端是好的。這…… 我這好歹是monorepo ,要是不能share @leaa/common包裡的代碼,那會變得很尷尬。行吧,我先不復用,忍忍。
本以為經歷過RN的開發已是煎熬,但這次…… 哎,不說了,怪自己用的技術太新(啪)。
? READING MORE...