開源了我獨立開發的評論元件 SaaS 🔥 🚀

devrsi0n 發表於 2022-01-14

👋 大家好,在獨立開發一年後,今天開源了我的 Next.js App - Chirpy, 一個主打保護隱私、支援主題定製的評論元件 SaaS。

太長不看版 🙈

這是 GitHub Repo, 歡迎點亮 🌟 收藏,歡迎參與貢獻。對於初、中級工程師這裡有所有你需要知道的一個完整的 SaaS 怎麼運作的全部知識,非常適合學習。

官網目前正在 beta 測試,歡迎試用。如果想要資料完全由你控制,Chirp 也支援 docker 部署

預覽 👀

評論元件,支援富文字編輯和 markdown shortcuts (markdown 實時預覽,類似 typora 的書寫體驗)

Untitled.png

主題定製 🌈(更多定製項開發中)

Untitled 1.png

元件使用情況分析皮膚(Analytics) 📈

Untitled 2.png

初心 ❤️

我在構建我自己的部落格的時候想要一個類似 Disqus 一樣的功能豐富、接入快捷的評論元件,但 Disqus 本身這幾年因為不經使用者同意就私加廣告,出賣使用者隱私等問題廣為詬病。下面是我檢索到的一些報導:

Bloomberg LawDisqus Faces $3 Million Sanction Over Alleged GDPR Breachesfor multiple breaches of EU privacy law.
Disqus facing $3M fine in Norway for tracking users without consent

我也用過 gitalk utterances 之類的基於 GitHub API 的免費評論系統,它們有一個顯而易見的問題就是隻支援 GitHub 登陸,而且受限 GitHub 本身 API,很多功能並不好做,比如元件的使用情況分析/ Analytics。

市面的元件基本都缺乏主題定製能力,放在自己的部落格、網站有很大概率因為設計不協調導致的違和感,所以主題定製也是必要的。

基於以上種種問題所以我打算做一個完全開源同時也能解決以上痛點的評論元件系統。

技術選擇與演進 🕺

Next.js 🆚 Gatsby

時間倒回到2020年,當年 React SSR 框架 Next.js 推出了 SSG(Static Site Generation)和 ISR(Incremental Static Regeneration), 迅速變得炙手可熱,相比2019年大熱的靜態網站生成器 Gatsby 優勢明顯。(如果放在今天重新選擇,remix 也是一個非常好的選擇。😄)

GatsbyNext.js
SSR🔴✅ 
SSG✅ 
ISR🔴✅ 
BUILD SPEED🐌⚡️
HMR SPEED🐌⚡️

npm 趨勢圖

Untitled 3.png

Next.js 的 ISR(增量靜態更新) 特性特別適合評論元件的場景。想象有成千上萬個 iframe 評論元件,用 ISR 渲染既有了類似 SSR 的靈活性,也有 CDN 的加速加持(減少併發渲染)。下圖是 ISR 示意:

Untitled 4.png

一開始 Chirpy 選擇了 egoist 的 Next FullStack Starter 作為始點,主要技術棧是:Next.js + Prisma + GraphQL + TypeGraphQL + Tailwindcss。但深入開發過程中發現 ORM Prisma 並不支援 Subscription/real-time API,這是一個對使用者體驗比較重要的功能,在慎重考慮之後 backend 遷移到了 Hasura

Prisma ➡️ Hasura

嚴格來說 PrismaHasura 並不算是同一類東西。Hasura 是一個支援高併發的 GraphQL server,支援用 GraphQL 增刪改查資料庫(比如 PostgreSQL),同時也提供一套完整的許可權控制。Prisma 是一個 ORM,為了支援 GraphQL 還是需要手寫所對應的 resolver 以及相應的許可權控制,開發成本較高,但相應的比 Hasura 更加靈活。

PrismaHasura
語言TypeScripthaskell
TypeORMGraphQL Server
GraphQL API🟡 手動✅ 由資料庫 Schema 生成
高併發🟡 受限於  Node.js 效能和應用架構✅  (50M記憶體支援1000 q/sec)
Subscription🔴
靈活性🟡

Hasura 應用的架構

Untitled 5.png

passportjs ➡️ next-auth

使用者登陸系統(第三方登陸 + 傳統賬號密碼)也有2個比較成熟的選擇,一開始選擇的是 passportjs, 在深入開發中遇到很多 OAuth 和安全相關的問題,最後重構換到了 next-auth。相比之下 next-auth 更加現代化(提供 React Hooks,庫本身也是用 TypeScript 寫的),更安全,Next.js 整合方便。

passportjsnext-auth
第三方登陸
郵箱密碼登陸✅ 
無密碼登陸✅ 
安全性🟡
React Hooks🔴
Next.js 整合🔴

Tailwindcss & twin.macro & radix-colors

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 只需要在元件渲染時注入使用者自定義的變數值即可。

Slate ➡️ tiptap

評論元件體驗最核心的是富文字編輯器,一開始 Chirpy 用的是 slate,因為它和 React 結合比較好,但開發到後期也遇到一些問題,比如 markdown shortcuts 實現並不順利,後面遷移到了tiptap,功能更加完善,底層也基於更穩定的 ProseMirror

slatetiptap
基於ReactProseMirror
穩定性🔴 issue✅ 
Markdown shortcuts🔴
功能豐富度🔴✅ 

apollo-client ➡️ urql

apollo-client 毫無疑問是最流行的 GraphQL client,最初選擇的便是它。就在準備開源之前不久遇到了2個很詭異的 bug(某些狀態死活不更新)。搜尋相關問題也瞭解到 apollo 把開源當成一種營銷手段,加之我一直很頭痛它很大的 bundle size,隨後下定決心遷移到 urql 這個小巧的多的庫,事實證明這次重構超值,減少了近 45KB bundle size。

Untitled 6.png

apollo-clienturql
bundle size🟡 33kb✅ 7.1kb
Next.js 整合🔴 ✅ next-urql
Document Caching🔴
Stale while Revalidate✅ ✅ 

Plausible 📈

一開始 Chirpy 自己實現了一個的資料統計/ Analytics,但後面發現這裡面需要考慮很多東西(聚合資料、效能、圖表等),嚴重拖累了開發進度。最後遷移到了 Plausible,它也是一個主打保護隱私的開源 SaaS。為了更好地保護使用者隱私,Chirpy 用它跑在一個單獨的伺服器上而不是直接用它的服務。

Chirpy 複用了 Plausible 的前端程式碼以適配評論元件的場景。同時也把它發資料的指令碼直接放到 Chirpy 的 bundle + 重對映發資料的 API 介面,可以很好的解決瀏覽器廣告過濾器過濾掉請求導致資料不準確的問題。因此你可以把 Chirpy 當成一個免費且高精確度的 Analytics 工具來用 😉。

Plausible 的營收也是 Chirpy 努力的目標 💪

image.png

CI & CD ♻

專案的 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 幾部分:

  • Next.js 自然選擇部署到開發它的公司 → Vercel 是最佳選擇,相比同類服務(比如 netlify)構建效能、圖片之類的優化效果更好,部署體驗也屬一流。
  • Hasura 前置了一個 Caddy HTTP Server 可以自動給 HTTPS 域名簽名,同時也為未來支援彈性負載均衡作準備。
  • Plausible 例項單獨部署避免影響業務資料,同時也加上了 Caddy HTTP Server。
  • Hasura 和 Plausible 各自部署到了一個 DigitalOcean 虛擬機器,主要看中高價效比,使用方便。

後續支援圖片上傳考慮使用 Cloudflare Images, 功能齊全,價效比也不錯。

遠期規劃 🛸

評論元件只是我的第一步,在功能逐漸完善之後 Chirpy 也會考慮做類似 intercom 的聊天元件(如下圖),hasura + WebSocket 架構天然適合這種輕量聊天應用。Chirpy 目標是打造一套完整的開源使用者溝通的解決方案。

Untitled 7.png

社群/community 🏘

作為開源專案,社群是我很看重的一塊,鼓勵大家參與貢獻(issue,discussion,PR)。計劃有了一定收入也會定期給活躍的開發者發放一定的金錢支援,回饋社群。

非常感謝你看到這裡,歡迎到 Chirpy 社群玩耍 🙌。