V2EX = way to explore
V2EX 是一个关于分享和探索的地方
现在注册
已注册用户请  登录
ByteCat
V2EX  ›  Notion

我是如何将 Notion 作为博客后端的

  •  2
     
  •   ByteCat · 2023-02-01 16:52:20 +08:00 · 2559 次点击
    这是一个创建于 440 天前的主题,其中的信息可能已经有所发展或是发生改变。

    我用 Notion 写博客已经一年了,主要是非常方便,想到什么可以先打个草稿,也不用像之前写 Markdown 一样要关心图片和附件的问题。

    最近花了一两天终于把小博客打理得比较像样了,便把这折腾过程记录下来。

    🎉 效果展示

    选择网站框架

    Notion 官方提供的页面,虽然效果是最好的,但给人的感觉还是笔记页面,而不是真正的「网站」,也不能添加自定义的功能,例如评论,要把 Notion 作为真正的 CMS 来用,可以通过 Notion API 获取数据(作为后端),而我们自己去实现一个前端。

    为了减少重复造轮子,我选择了 https://github.com/transitive-bullshit/nextjs-notion-starter-kit ,因为这是一个采用了 Next.js 的框架,而我本人对 React 也略有了解,修改起来比较容易。

    这个框架的使用也非常简单,只需要 fork 一份到自己的仓库,再修改 site.config.ts 中的网站设置,使用 Vercel 即可部署一个网站,如果你不怎么想折腾,到这里就差不多结束了。

    从 Vercel 迁移到自建服务器

    是的,虽然 Next.js 是 Vercel 所开源,对 Next.js 的构建部署也非常方便,为什么我还要选择自建呢?

    自从去年 nextjs-notion-starter-kit 的作者加入了图片优化之后,我常常能收到 Vercel 的滥用警告。

    而事实是,我的图片数量远没有那么多,我也很少重新构建项目(后来知道是因为每次请求了签名的文件链接,导致图片无法被缓存,下文有解决办法),但一直超过用量不是个好兆头,我也承担不起 Vercel 高昂的费用(相比起很多服务器来说),于是决定把博客迁移到自建服务器上。

    因为之前没有接触过 Next.js ,查询了一下资料发现部署是比较简单的,nextjs-notion-starter-kit 使用的是 SSR ( Server Side Render ,服务端渲染),只要把服务器用 Node.js 跑起来就可以了。

    然而,我对这个框架做了较多的修改,如果用之前的「 fork-sync-修改-部署」流程,很容易产生冲突,于是我想到了一种比较方便的办法:用 GitHub Actions 拉取最新代码,用我自己修改的文件覆盖原作者的代码,再打包成 Docker image 。我已经在 GitHub 开源: https://github.com/imbytecat/nextjs-notion-starter-kit-docker ,需要的可以自己 fork 修改。

    自定义功能的超能力

    由于前端都由我们自己掌控,要做一些功能的更改非常容易。

    移除 GitHub 分享按钮

    原版框架的页面右上角会添加一个指向开源项目的按钮,而且不能简单通过配置文件设置不显示。

    在阅读代码之后发现,这个按钮封装成了一个组件,而且确实没有相关的设置来移除,只能通过删除代码解决:

    1. 删除 components/NotionPage.tsx#L23 处的导入
    2. 删除 components/NotionPage.tsx#L284 处的组件

    这样重新构建后就不会显示分享按钮了。

    更改图片为永久链接

    参考了 WeijunDeng 的修改建议,不过因为时间较久,但目前其代码依然可以使用。

    只需要在 lib/notion.ts#L42 处加入以下代码:

      if (recordMap && recordMap["signed_urls"]) {
        const signed_urls = recordMap["signed_urls"]
        const new_signed_urls = {} 
        for (const p in signed_urls) {
          if (signed_urls[p] && signed_urls[p].includes(".amazonaws.com/")) {
            console.log("skip : " + signed_urls[p])
            continue
          }
          new_signed_urls[p] = signed_urls[p]
        }
        recordMap["signed_urls"] = new_signed_urls
      }
    

    这样图片就会变成永久链接,不会每次都带着签名请求 Notion API ,也能正确触发图片缓存,但不知道为什么原作者迟迟不合并这个请求。

    更改文章 URL 路径为 UUID

    使用 UUID 作为路径的原因很简单:

    1. 使用评论系统,UUID 有唯一确定性
    2. Notion 页面本来就有唯一对应的 UUID ,获取起来比较方便
    3. 不用自己想 slug ,减轻心智负担

    完成后的效果:

    https://www.imbytecat.com/2f3456133af0425da87539dd6a8b2379
    

    方法也很简单,只需要将 lib/get-canonical-page-id.ts#L23 替换成:

        return getCanonicalPageIdImpl(pageId, recordMap, { uuid: true }).split('-').slice(-1).join('')
    

    另外不要忘了在 lib/get-canonical-page-id.ts#L12 上方加一句 // eslint-disable-next-line @typescript-eslint/no-unused-vars ,否则过不了 ESLint 检查,不能构建成功。

    添加评论系统

    评论系统我用的是 Waline ,以前用过 Valine 还不错,但是有纯前端实现有安全问题,并且非常依赖 LeanCloud ,而 Waline 可以自建。

    评论的实现比较粗糙,还没来得及调整样式,所以还存在一些问题,不过大概的思路可以讲一下。

    如果使用 Waline ,需要先添加依赖:

    yarn add @waline/client
    yarn add sass
    

    首先可以新建一个 components/Comment.tsx 作为我们的评论框组件,内容大概是这样:

    import { init } from '@waline/client';
    import '@waline/client/dist/waline-meta.css';
    import '@waline/client/dist/waline.css';
    import React, { useEffect, useRef } from 'react';
    
    import type { WalineInitOptions, WalineInstance } from '@waline/client';
    
    export type WalineOptions = Omit<WalineInitOptions, 'el'> & { path: string };
    
    export const Waline = (props: WalineOptions) => {
      const walineInstanceRef = useRef<WalineInstance | null>(null);
      const containerRef = React.createRef<HTMLDivElement>();
    
      useEffect(() => {
        walineInstanceRef.current = init({
          ...props,
          el: containerRef.current,
        });
    
        return () => walineInstanceRef.current?.destroy();
      });
    
      useEffect(() => {
        walineInstanceRef.current?.update(props);
      }, [props]);
    
      return <div ref={containerRef} />;
    };
    

    然后在 components/NotionPage.tsx 使用就可以了:

    import { Waline } from './Comment'
    // ...
          {block.id.replace(/-/g, '') !== site.rootNotionPageId ?
            <Waline
              serverURL='https://waline.imbytecat.com'
              path={'/' + block.id.replace(/-/g, '')}
              emoji={[
                '//cdn.jsdelivr.net/gh/walinejs/[email protected]/tw-emoji'
              ]}
              dark={isDarkMode}
              meta={['nick', 'mail']}
              requiredMeta={['nick', 'mail']}
              imageUploader={false}
              copyright={false}
            /> : null}
    // 放在这个结束标记前面
        </>
    

    最终应该可以得到和 🎉 效果展示 差不多的样子。

    CrazyCoolL
        1
    CrazyCoolL  
       2023-02-02 11:56:46 +08:00 via iPhone
    直接使用 Notion 本身其实就挺好的,我的个人主页: https://imllf.com
    lifesimple
        2
    lifesimple  
       2023-02-09 10:41:09 +08:00
    @CrazyCoolL 强啊 老哥能写篇文章分享下是怎么搭建的么,纯 notion 搭建?改过样式么 另外就是留言板输入中文容易失焦
    CrazyCoolL
        3
    CrazyCoolL  
       2023-02-09 17:30:11 +08:00
    @lifesimple 对,纯 Notion 搭建,只有留言部分是嵌入了其他页面。(好像是有失焦问题,怪)
    Laussan
        4
    Laussan  
       2023-02-19 02:58:43 +08:00
    马一个,小白想问几个问题:

    CMS 具体是指什么?

    Notion API 获取的是什么类型的数据?是会完整拉取到服务器端么?

    跑这个的 vps 大概需要什么配置?

    提前感谢!
    ByteCat
        5
    ByteCat  
    OP
       2023-02-20 20:49:54 +08:00
    @Laussan 可以看下 [Wikipedia 的说明]( https://zh.wikipedia.org/zh-cn/%E5%86%85%E5%AE%B9%E7%AE%A1%E7%90%86%E7%B3%BB%E7%BB%9F),Notion API 可以获取 Notion 的所有数据,所以可以据此复刻一个 Notion 出来,也可以用 API 来实现一个 CMS ,我用的方案就是 [nextjs-notion-starter-kit]( https://github.com/transitive-bullshit/nextjs-notion-starter-kit),是一个基于 Next.js 的框架。
    跑的话,配置要求不高,随便一个服务器应该都可以,嫌麻烦可以直接用 Vercel 的,甚至不需要服务器。
    foyo
        6
    foyo  
       2023-02-26 01:38:46 +08:00
    可以不购买 VPS ,直接用 GitHub Pages 部署,具体可以参考 https://xchb.fun/%E7%94%A8notiongithub-pages%E6%90%AD%E5%BB%BA%E4%B8%80%E4%B8%AA%E5%8D%9A%E5%AE%A2
    ByteCat
        7
    ByteCat  
    OP
       2023-02-26 19:24:23 +08:00
    @foyo 静态生成也不错,不过我图片都用 Cloudflare 缓存了,所以很快,Notion API 请求多了好像有限制。
    Anjhon
        8
    Anjhon  
       343 天前
    这个我必须来推荐 NotionNext 一波,完美契合你的需求,博客功能完善,而且作者还在持续更新; GitHub 地址: https://github.com/tangly1024/NotionNext ;打个广告(我的博客地址😂):anjhon.top
    关于   ·   帮助文档   ·   博客   ·   API   ·   FAQ   ·   我们的愿景   ·   实用小工具   ·   1049 人在线   最高记录 6543   ·     Select Language
    创意工作者们的社区
    World is powered by solitude
    VERSION: 3.9.8.5 · 27ms · UTC 22:41 · PVG 06:41 · LAX 15:41 · JFK 18:41
    Developed with CodeLauncher
    ♥ Do have faith in what you're doing.