👋 大家好,在独立开发一年后,今天开源了我的 Next.js App - Chirpy, 一个主打保护隐私、支持主题定制的评论组件 SaaS 。
这是 GitHub Repo, 欢迎点亮 🌟 收藏,欢迎参与贡献。对于初、中级工程师这里有所有你需要知道的一个完整的 SaaS 怎么运作的全部知识,非常适合学习。
官网目前正在 beta 测试,欢迎试用。如果想要数据完全由你控制,Chirp 也支持 docker 部署 。
评论组件,支持富文本编辑和 markdown shortcuts ( markdown 实时预览,类似 typora 的书写体验)
主题定制 🌈(更多定制项开发中)
组件使用情况分析面板(Analytics) 📈
我在构建我自己的博客的时候想要一个类似 Disqus 一样的功能丰富、接入快捷的评论组件,但 Disqus 本身这几年因为不经用户同意就私加广告,出卖用户隐私等问题广为诟病。下面是我检索到的一些报道:
Disqus facing $3M fine in Norway for tracking users without consent
我也用过 gitalk utterances 之类的基于 GitHub API 的免费评论系统,它们有一个显而易见的问题就是只支持 GitHub 登陆,而且受限 GitHub 本身 API ,很多功能并不好做,比如组件的使用情况分析 / Analytics 。
市面的组件基本都缺乏主题定制能力,放在自己的博客、网站有很大概率因为设计不协调导致的违和感,所以主题定制也是必要的。
基于以上种种问题所以我打算做一个完全开源同时也能解决以上痛点的评论组件系统。
时间倒回到 2020 年,当年 React SSR 框架 Next.js 推出了 SSG( Static Site Generation )和 ISR( Incremental Static Regeneration ), 迅速变得炙手可热,相比 2019 年大热的静态网站生成器 Gatsby 优势明显。(如果放在今天重新选择,remix 也是一个非常好的选择。😄)
Gatsby | Next.js | |
---|---|---|
SSR | 🔴 | ✅ |
SSG | ✅ | ✅ |
ISR | 🔴 | ✅ |
BUILD SPEED | 🐌 | ⚡️ |
HMR SPEED | 🐌 | ⚡️ |
Next.js 的 ISR(增量静态更新) 特性特别适合评论组件的场景。想象有成千上万个 iframe 评论组件,用 ISR 渲染既有了类似 SSR 的灵活性,也有 CDN 的加速加持(减少并发渲染)。下图是 ISR 示意:
一开始 Chirpy 选择了 egoist 的 Next FullStack Starter 作为始点,主要技术栈是:Next.js + Prisma + GraphQL + TypeGraphQL + Tailwindcss。但深入开发过程中发现 ORM Prisma 并不支持 Subscription/real-time API,这是一个对用户体验比较重要的功能,在慎重考虑之后 backend 迁移到了 Hasura。
严格来说 Prisma 和 Hasura 并不算是同一类东西。Hasura 是一个支持高并发的 GraphQL server ,支持用 GraphQL 增删改查数据库(比如 PostgreSQL),同时也提供一套完整的权限控制。Prisma 是一个 ORM ,为了支持 GraphQL 还是需要手写所对应的 resolver 以及相应的权限控制,开发成本较高,但相应的比 Hasura 更加灵活。
Prisma | Hasura | |
---|---|---|
语言 | TypeScript | haskell |
Type | ORM | GraphQL Server |
GraphQL API | 🟡 手动 | ✅ 由数据库 Schema 生成 |
高并发 | 🟡 受限于 Node.js 性能和应用架构 | ✅ (50M 内存支持 1000 q/sec)( https://hasura.io/blog/fast-graphql-execution-with-query-caching-prepared-statements/) |
Subscription | 🔴 | ✅ |
灵活性 | ✅ | 🟡 |
Hasura 应用的架构
用户登陆系统(第三方登陆 + 传统账号密码)也有 2 个比较成熟的选择,一开始选择的是 passportjs, 在深入开发中遇到很多 OAuth 和安全相关的问题,最后重构换到了 next-auth 。相比之下 next-auth 更加现代化(提供 React Hooks ,库本身也是用 TypeScript 写的),更安全,Next.js 集成方便。
passportjs | next-auth | |
---|---|---|
第三方登陆 | ✅ | ✅ |
邮箱密码登陆 | ✅ | ✅ |
无密码登陆 | ✅ | ✅ |
安全性 | 🟡 | ✅ |
React Hooks | 🔴 | ✅ |
Next.js 集成 | 🔴 | ✅ |
Tailwind 是一个原子化 CSS 开发框架,在熟悉它之后 CSS 开发效率能显著的提升 🚀;同时它提供了一套完备且经得住考验的默认主题配置,以及一套丰富的 SaaS UI 库(不完全免费,但学会 tailwind 之后手写类似的 UI 不会太难,更重要的是这里有常用 SaaS 的 UI 设计可供参考,对我这个设计苦手非常有帮助 🥳)。
实际上 Chirpy 是和 twin.macro 一起用,这是一个结合 tailwind 和 CSS-in-JS ( styled-component & emotion ) 的库,tailwind 早期版本在组件化开发中会遇到样式类不能被 overwrite 的情况, 因为 tailwind 本身输出原子类:
<p className="w-1 h-1">...</p>
// 输出 CSS 👇
.w-1 {
width: 0.25rem;
}
.h-1 {
height: 0.25rem;
}
twin.macro 版本, 不需要 CSS !important
就能覆盖样式:
<p tw="w-1 h-1">...</p>
// 输出 html & css 👇
<p class="random-name">...</p>
.random-name {
width: 0.25rem;
height: 0.25rem;
}
twin.macro 也支持 build 的时候校验,不合法的样式会报错(比如w-0.1
),可以避免写出无用样式。以及自由组合多个 variants ,例如:sm:(bg-black hover:(bg-white w-10))
。当然它也并不完美,tailwind 可以复用已有的 CSS 类,而它每个 tw 几乎都会生成新的样式, 最终输出的 bundle size twin 会更大一点。它也不支持一些 tailwind 的一些官方插件,比如:tailwindcss-typography(用于编辑器)。故两者是结合一起使用。
tailwind 的 Dark mode 主要靠用 dark:
variant (如下面的🌰), 几乎每个颜色值都要写两遍,我理想中的 dark mode 是自动的。
<div class="bg-white dark:bg-gray-900">...</div>
因此这里需要 light/dark 2 套颜色,radix-colors 正满足要求。但它要怎么和 tailwind 一起用呢?答案是:CSS Variable。
首先配置 tailwind 主题:
module.exports = {
theme: {
colors: {
bg: `var(--tw-colors-bg)`
},
},
}
然后给应用注入样式:
:root {
--tw-colors-bg: white;
}
:root.dark {
--tw-colors-bg: black;
}
配合 next-themes 自动在用户切换 mode 时切换网站 CSS 类:
// next-themes 在用户切换 mode 时自动切换 .dark 有无
<html class="dark">...</html>
这样就能在应用切换 mode 时自动刷新颜色。
这里也顺便解决了组件的主题定制功能,Chirpy 只需要在组件渲染时注入用户自定义的变量值即可。
评论组件体验最核心的是富文本编辑器,一开始 Chirpy 用的是 slate,因为它和 React 结合比较好,但开发到后期也遇到一些问题,比如 markdown shortcuts 实现并不顺利,后面迁移到了tiptap,功能更加完善,底层也基于更稳定的 ProseMirror。
slate | tiptap | |
---|---|---|
基于 | React | ProseMirror |
稳定性 | 🔴 issue( https://github.com/ianstormtaylor/slate/issues/3150) | ✅ |
Markdown shortcuts | 🔴 | ✅ |
功能丰富度 | 🔴 | ✅ |
apollo-client 毫无疑问是最流行的 GraphQL client ,最初选择的便是它。就在准备开源之前不久遇到了 2 个很诡异的 bug (某些状态死活不更新)。搜索相关问题也了解到 apollo 把开源当成一种营销手段,加之我一直很头痛它很大的 bundle size ,随后下定决心迁移到 urql 这个小巧的多的库,事实证明这次重构超值,减少了近 45KB bundle size 。
apollo-client | urql | |
---|---|---|
bundle size | 🟡 33kb | ✅ 7.1kb |
Next.js 集成 | 🔴 | ✅ next-urql |
Document Caching | 🔴 | ✅ |
Stale while Revalidate | ✅ | ✅ |
一开始 Chirpy 自己实现了一个的数据统计 / Analytics ,但后面发现这里面需要考虑很多东西(聚合数据、性能、图表等),严重拖累了开发进度。最后迁移到了 Plausible,它也是一个主打保护隐私的开源 SaaS 。为了更好地保护用户隐私,Chirpy 用它跑在一个单独的服务器上而不是直接用它的服务。
Chirpy 复用了 Plausible 的前端代码以适配评论组件的场景。同时也把它发数据的脚本直接放到 Chirpy 的 bundle + 重映射发数据的 API 接口,可以很好的解决浏览器广告过滤器过滤掉请求导致数据不准确的问题。因此你可以把 Chirpy 当成一个免费且高精确度的 Analytics 工具来用 😉。
Plausible 的营收也是 Chirpy 努力的目标 💪
项目的 CI/CD 主要依靠 GitHub Action 。 开发流程是基于 PR ,每个 PR check-in 之前会跑 Cypress( 端到端测试),jest (单元测试),以及输出 Next.js bundle size 变化(避免无意引入代码造成 bundle size 问题)。
后面会引入 hasura schema CI/CD,减少人肉升级 schema 出问题。
这里分为 Next.js, Hasura 和 Plausible 几部分:
后续支持图片上传考虑使用 Cloudflare Images, 功能齐全,性价比也不错。
评论组件只是我的第一步,在功能逐渐完善之后 Chirpy 也会考虑做类似 intercom 的聊天组件(如下图),hasura + WebSocket 架构天然适合这种轻量聊天应用。Chirpy 目标是打造一套完整的开源用户沟通的解决方案。
作为开源项目,社区是我很看重的一块,鼓励大家参与贡献( issue ,discussion ,PR )。计划有了一定收入也会定期给活跃的开发者发放一定的金钱支持,回馈社区。
非常感谢你看到这里,欢迎到 Chirpy 社区玩耍 🙌。
1
codespots 2022-01-14 13:52:03 +08:00 2
东西挺好的,但你知道为啥多说之类的现在不存在了吗?所以还是面向海外用户吧
|
2
ljinkai 2022-01-14 14:37:47 +08:00
好东西,Nuxt.js 的考虑集成下吗
|
3
enchilada2020 2022-01-14 16:23:02 +08:00 via Android
厉害 大佬 dddd (带带弟弟
|
4
wensonsmith 2022-01-14 16:37:16 +08:00
不错呀
|
5
yuyue001 2022-01-14 17:04:28 +08:00
star 了
|
10
devrsi0n OP @enchilada2020 欢迎参与贡献,免费帮带 😜
|
11
Jiki 2022-01-14 19:06:16 +08:00 4
赏心悦目,无论是 UI 还是技术栈
|
13
lyhiving 2022-01-14 19:25:01 +08:00 via Android
弄成 intercom 的样子才能看到钱
|
14
devrsi0n OP @lyhiving 不一定哈,你看 Plausible (文章里有介绍) 本身功能不多的,但是靠主打隐私牌一样月收入恐怖,这种模式是有成功先例的
|
15
Showfom 2022-01-14 19:34:38 +08:00
|
16
devrsi0n OP @Showfom 因为 blog 和 docs 主要突出文本本身,不过后面加 TOC 侧边栏的时候会再斟酌下设计,感谢
|
18
devrsi0n OP @lyhiving 要说单一的话 Plausible 因为要保护隐私功能可以说极少,而且就 2 个开发者,但一样不影响它赚钱。国外 Facebook 一系列事件下来让保护隐私的 SaaS 就是一个热门领域,况且 Chirpy 也不止这一个特点。产品也是慢慢摸索出来的,我会不断完善 Chirpy
|
19
shadeofgod 2022-01-17 12:39:15 +08:00
|
20
br_wang 2022-01-17 16:04:22 +08:00
整体方案和技术栈选择相关的说明好看
|
21
devrsi0n OP @shadeofgod 哈哈,原作者跟我是同一家公司(开源是我们的副业),在他公布 cusdis 的时候我已经开发了很久了,并且两个产品定位不太相同,大家喜欢哪个就用哪个 😉
|
23
xrr2016 2022-01-19 10:29:15 +08:00
棒,大佬牛
|
24
scyuns 2022-01-19 10:42:47 +08:00
感谢分享,可惜我英语不好。希望可以做本土化!祝越来越好
|
26
daolanfler 2022-01-19 11:31:18 +08:00
把技术栈的选型都介绍了一遍,学到了多。赞
|
27
devrsi0n OP @daolanfler 🥳
|
28
marcosteam 2022-01-19 13:23:09 +08:00 via Android
你的下一步目标是打算做成 Crisp 之类的客服系统?
|
29
Showfom 2022-01-19 13:57:20 +08:00
突然发现个 typo
https://chirpy.dev/docs/self-hosted We'll polish this document once we're Generally Available. polish > publish |
30
zhy0216 2022-01-19 14:20:41 +08:00 1
别的不说 技术栈选的真不错
|
31
devrsi0n OP @marcosteam 👏恭喜你,你已经猜对 chirpy 的计划!只是一个实时聊天 widget 还不足以吸引人,能有一些 ai 智能客服应用场景会广的多。
|
32
devrsi0n OP @Showfom 不是 typo 哦,意思是我们产品真正发布的时候会更新 self hosted 的文档。毕竟谁也不想 slef hosted 的时候天天更新吧 😆
|
36
rookiebulls 2022-01-19 18:43:21 +08:00 via iPhone
好奇这种靠什么收益呢
|
37
lanxyou 2022-01-21 19:42:55 +08:00 1
不管怎么说,这么认真的分享,一定要支持一下
|
38
marcosteam 2022-01-21 21:21:24 +08:00
@rookiebulls 一般就是提供自托管的服务并收取维护费用吧
|