Leaa (project 1d1h)
Leaa is a monorepo CMS (Content Management System) built with Nest.js, Next.js, and Ant Design.
Monorepo-Packages
- leaa-api (demo) / backend (Nest.js + @nestjsx/crud + MySQL + Docker Compose)
- leaa-dashboard (demo) / dashboard (React.js + Antd + MobX)
-
leaa-www (demo) / website (Next.js) -
leaa-miniprogram (independence-repo) / wechat-miniprogram (Taro.js + Taro-ui + GraphQL) -
leaa-app (independence-repo) / iOS and Android (expo + GraphQL)
Install
View the README.md
of each sub-directory in packages
. You may need to look at yarn workspaces first.
demo)
Dashboard (dashboard-login
dashboard-user-edit
Legacy
TODOS
DEV SUMMARY
本来总结应该写在文末,但我感觉还是提上吧,起码不用看我唠唠叨叨一堆开发日志。
以前老想着自己写一个全栈项目试着打通 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
DEV LOG
2019-08-01 23:39
从 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 的人还是很多的,特别是国内。
2019-08-15 20:45
时间过得好快,转眼半个月,最近没给 leaa 写什么新东西。重点放在了阿里云 OSS 整合这块。想要实现这样一个功能:
- Local 上传
- OSS 上传
- OSS 上传后备份到 Local
- OSS 上传 @2x 图片后,生成 @1x 上传回 OSS
- OSS 上传 @2x 图片后,生成 @1x 上传回 OSS 并备份到 Local
- 删除 OSS 需要触发删除 @1x 和 @2x 文件,并删除 Local 中的 @1x 和 @2x
- Local 和 OSS 是否开启均可配置
- 如 OSS 开启,为保证用户上传速度,所有上传直接走 OSS
其中过程还蛮艰辛的,涉及到 Local 和 OSS 之间的一些交互,而且因为直接走 OSS,所有请求不经 API,变成了等待 OSS 的 Callback,必须保证任何一步没做完都不能动 DB,勉强达到了幂等。 其实如果上传都走 API,然后由 API 统一处理再 put 到 OSS 会简单非非非非非非非常多,我这么做主要是担心做某些活动的时候,如果涉及到上传文件,并发就会很大,服务器缓不过来。所以拿 OSS 先挡一下还是很有必要的。
基本上 www 和 api 以及 dashboard 就告一段落了。明天开始 miniprogram
。
2019-08-16 12:04
刚整理 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"
。
2019-08-16 15:07
做了一个 Leaa Stack 的 Banner 放到 README 顶部,用图片描述使用的技术比文字好不少。另外提一下 Leaa
这个名字,这其实是我喜欢的一个法国女演员 Léa Seydoux 的名字,避免重名率过高,我在 Lea 后面多加了个 a。不过 LEAA
在 Google 最多的指向是 Law Enforcement Assistance Administration
美国一司法机构(笑)。
2019-08-17 11:21
刚在用 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,目前只有一处是只有,标本不够就先不处理了。待日后这个问题多了再统一处理。
2019-08-17 14:16
虽然自己很注意 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'],
};
试了一下,速度还是蛮快的。要有更好的最佳实践可能还得用一段时间才知道效果了。
2019-08-18 11:42
试了大概一个晚上的 Taro
,感觉不是特别理想,为什么呢?首先我需要的是一个 React
to 小程序
的框架,而且想要的是 ONLY
小程序,至于为什么是 ONLY,后面我会展开详细说明。
初步使用下来,感觉 Taro
感觉是一个集大成者,他身上的责任还蛮重的,需要兼容太多的 类小程序
环境,比如 支付宝小程序
,今日头条小程序
等…… 而且还要考虑兼容 RN
那不友好的 yoga
CSS 引擎,团队还是非常不容易的,能做到这这个程度,我还是非常佩服的,这里必须先给个赞。接下来我讲一下我几小时下来大概的感触。
H5 端
完美!正常 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/**"]
}
React Native 端
看 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
处理就好,毕竟坑基本上在以往项目踩完了(笑)。
2019-08-18 22:18
记录一下今天白天用 Taro
的心得,真是满满的心酸啊……
-
首先,比较痛苦的是不支持
@apollo/react-hooks
和react-apollo
!也就是说,任何 Apollo 官方的包都不可以用了!不能useQuery
连<Query>
都不让,用就给你报 hooks 那经典的错误Invariant Violation: Invalid hook call.
结果是直接用写好 exportapolloClient
后apolloClient.query()
,这真是一夜回到解放前啊! -
本来想着方便,在
H5
模式下 debug,apolloClient
这种方式能跑起来已经很开心了,没想到……小程序
模式弹 error 了,说fetch is not found globally and no fetcher passed, to fix pass....
查了下资料说是 「微信小程序在某一次升级中, 移除了全局的 fetch」这……,还好,马上找到了前辈写的 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
的开发已是煎熬,但这次…… 哎,不说了,怪自己用的技术太新(啪)。