使用 Github Issues 作為後端,並透過 Next.js 作為網頁渲染框架。
- Next.js(React.js)
- Server Components
- Server Actions
- App Router
- Tailwindcss(daisyUI)
- GraphQL(apollo-client)
- eslint + prettier
去除掉常見的設定相關檔案,主要專案中有這幾個目錄。
.
├── actions
├── app
| ├── api
│ │ └── auth
│ │ └── [...nextauth]
│ │ └── route.ts
│ ├── post
│ └── sitemap.ts
├── components
│ ├── Footer.tsx
│ ├── Header.tsx
│ ├── UserButton.tsx
│ ├── auth/
│ ├── post/
│ └── ui/
├── config
│ └── auth.ts
├── graphql
│ └── github.ts
├── lib
│ ├── apollo.ts
│ └── utils.ts
└── types
actions/
檔案夾中存放對於不同操作的 Server Action,對於不同的 Server Action 會設計屬於他們的 DTO,用於資料傳遞到 Client Side。
例如 actions/edit-post.tsx
中就存放著對於文章資料有變動相關的動作,以 createPost
為例,他就會有 CreatePostReqest
和 CreatePostResponse
兩種 DTO,可以在前端判斷錯誤和接收資訊時更加方便。
app/
檔案夾中存放對於不同頁面的 React Page 並且透過 App Router
來設計不同的頁面。在這之中比較需要注意的是我是在最外層 app/sitemap.ts
就將 Sitemap 生成完成,並不是 nested 的方式。
components/
檔案夾中存放對於不同頁面的 React Component,並且對於某個 page 所需要的 Component,我不會直接寫在 App Router 中,而是會在 components/[page]
中設計好,因為對我來說這樣可以讓 App router 更清楚展現頁面的路徑而不至於混亂。
config/
檔案夾中存放對於不同設定的檔案,例如 auth.ts
就是對於 next-auth
的設定。
graphql/
檔案檔案中存放對於不同 GraphQL Query 的設定,例如 github.ts
就是對於 Github GraphQL API 的設定。會這樣做是因為 GraphQL 的長度通常都蠻長的,希望有個統一個地方去管理 graphql 的 query。
lib/
在這裡存放一些對於不同功能的設定,例如 apollo.ts
就是對於 Apollo Client 的設定,utils.ts
就是一些常用的 function,例如 cn(這個專案裡也只有用到這個)。雖然 apollo client 有對於 next.js 的試驗型套件 next-with-apollo ,目的應該也是希望在一個 Server Side 共享同一個 client,但我目前是自己實作單體式架構,確保他都會使用到同一個 client。
要啟動專案,首先要先安裝相依套件:
npm install
設定好相關環境變數,在 .env
中,可以參考 .env.example
:
// 部落格中的相關資訊,沒有設定了話預設就是我的 XD
BLOG_NAME=My Blog
LINKEDIN_URL=https://www.linkedin.com/in/宇傑-鄭-3941181a3
GITHUB_URL=https://github.com/YJack0000
// blog post 來源 repo
GITHUB_BLOG_POST_OWNER=YJack0000
GITHUB_BLOG_POST_REPO=github-issue-blog-post
// 參考 https://next-auth.js.org/configuration/options#secret 生成
NEXTAUTH_SECRET=<your-app-secret>
// Github App,用於給人登入並且檢查是否可以在線編輯
GITHUB_ID=<your-github-app-client-id>
GITHUB_SECRET=<your-github-app-client-secret>
// 個人 token 給沒有登入使用者也可以看文章
GITHUB_PAT=<your-github-personal-access-token>
透過以下指令來啟動專案:
npm run dev
透過以下指令來編譯專案:
npm run build
- GitHub Login
- 使用
next-auth
串接 Github 登入,並且在使用者為登入時使用 Github PAT 作為取得文章需的 Token - 登入時只取得使用者相關資訊和公開 repo 的訪問權限
- Post Management
- 將 GitHub Issue 作為 Post
- title 為文章標題
- labels 為文章的分類標籤
- body 為文章描述(顯示在文章預覽)
- 第一個 comment 為文章主體
- 列表頁
- 第一次會先在 Server Side 載入 10 筆
- 每當列表滾到底部時要自動發送 API 請求,並載入額外 10 筆,直到沒有更多文章
- 文章頁
- 顯示文章內容,並透過
react-markdown
渲染出 markdown 的內容,並透過rehypeRaw
防止有需 html 的東西渲染不出來 - 使用者可以在此「編輯」、「刪除」,透過
rehype-sanitize
防止在 Markdown 中的 XSS 攻擊 - 新增 / 編輯文章時,可以使用 Modal 或跳轉至新的頁面操作
- 表單驗證: title 為必填,body 至少需要 30 字
- 使用 TypeScript
- 使用 Next.js + App Router
- 調校 Web Vitals 評分(Lighthouse + Vercel)
- 有處理錯誤及例外狀況 (Error Handling),可能做得不太完全
- 有部署至 Vercel
透過動態生成 sitemap.xml
並丟給 Google Search Console 來同時增加準確度與被 cache 機會。app/sitemap.ts
待更新調整方案...
可以顯示出非預期錯誤或是預期的 validation error
參考
app/post/page.tsx
actions/edit-post.tsx
以前就做過的一些知識點搬過來:
- 使用 DaisyUI 來快速建立網頁,並且用 DaisyUI 內建的功能提供 Dark/Light mode 一定的支援。
- 使用 next/image 來優化圖片載入速度(LCP)
- 使用 sitmap 生成來優化 SEO。
- 動態生成不同篇文章的 header,例如 Open Graph, Twitter Card 等等。
- 目前動態生成圖片有 bug,待修正
做啥有趣的事情:
- 第一次用 Next.js
- 嘗試使用 Next.js 的 Server Components 作為渲染 blog post 的方式
- 希望 Blog Post 應該要有一些文字的描述,於是將 Issue 本身作為 description,第一個 comment 才作為文章內容
- 把之前會設計成 API Request 的地方交由 Server Action。
- 使用 Intersection Observer API 來觸發新的資料載入
- 處理使用者沒登入的情況 -> 理論上還是要在一定限度內給他讀東西啦(Github PAT 當作備援)
- 分類文章,並且可以透過分類來篩選文章
- 嘗試使用 GraphQL 來取得資料