我是如何将 Notion 作为博客后端的(转载)

notion进阶教程 1年前 (2023) iowen
882 0

文章来源:v2ex社区  作者:ByteCat

我用 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/emojis@1.1.0/tw-emoji'
          ]}
          dark={isDarkMode}
          meta={['nick', 'mail']}
          requiredMeta={['nick', 'mail']}
          imageUploader={false}
          copyright={false}
        /> : null}
// 放在这个结束标记前面
    </>

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

版权声明:iowen 发表于 2023年4月24日 上午10:56。
转载请注明:我是如何将 Notion 作为博客后端的(转载) | 吾爱notion

相关文章

暂无评论

暂无评论...