迁移博客至 Next.js

博客之前使用的是 Hugo ,托管在 Netlify 上,配合腾讯云 CDN 和 Cloudflare,然后自己做的主题,很安逸。后来心血来潮,用 Next.js 重新弄了下,说下体验。

首要目标呢,能实现零成本部署,这里我用到了如下免费服务:

  • 腾讯云 CDN
  • 腾讯云 COS
  • ZEIT
  • Firebase Hosting
  • Azure DevOps
  • Backblaze B2

由于我的博客功能很简陋,加上之前已经为 Hugo 制作了一款主题,很快就搭建好了界面和路由。

习惯了之前 Hexo、Hugo 直接在项目目录下写文章的方式,我一开始也将文章(Markdown)源文件放在了 Next.js 项目中,然后 Webpack 直接全部引入打包,结果打包完的体积太大了。不行,Markdown 因该按需加载,根据不同文章 URL 动态引入,我想了想,为了实现这个功能,需要在运行 Next.js 前先生成包含文章 URL 和 Markdown 路径关系的索引,同时还要生成文章分页数据。这样操作下来,无论是更新文章还是 Next.js 都挺繁琐的,不如分开吧。

最终,我将 Next.js 和文章源文件分离开来。文章源文件经整理生成分页和文章 JSON 数据单独存放(这个过程我使用 Azure DevOps 来自动完成),再由 Next.js 使用 Axios 调用。

Sitemap.xml

为了动态生成 sitemap.xml ,我一开始使用 Express 作为默认 Server ,定义好 sitemap.xml 路由,直接由 Express 获取数据后生成 xml 返回,不经 Next.js 处理。只可惜,托管在 ZEIT 上不支持使用自定义 Server,只好在 pages 目录下建立文件 sitemap.xml.tsx ,拼接 xml 文本后直接返回

getInitialProps = async ({ res }) => {
  if (res) {
    res.setHeader("Content-Type", "application/xml");
    res.end(
      `<?xml version="1.0" encoding="UTF-8"?>
        <urlset xmlns="http://www.sitemaps.org/schemas/sitemap/0.9">
          <url>
              <loc>${""}</loc>
              <priority>1.0</priority>
          </url>
          ${""}
        </urlset>`
    );
  }
};

数据格式

之前数据格式一直使用 json ,这次我尝试了使用 Protocol buffers ,使用上大体差不多,就是定义格式的时候麻烦了些。也遇到了一个坑:我使用 Protocol Buffers 作为数据存储格式,并托管在 Firebase Hosting 上,但在 Firefox 下解析数据错误,Chrome 正常,观察 HTTP 响应后,发现 HEADER 中 Content-Encoding 值为 application/x-gzip ,需要修改为 application/protobuf

可用率

博客托管在 ZEIT,文章存放在 Firebase Hosting,图片存放在 Backblaze B2,这三者都使用 腾讯云 CDN 进行国内加速,但在特殊时期,还是会访问超时,最严重的就属 Firebase Hosting,没办法,只好将文章同步到 腾讯云 COS ,利用官方提供的 SDK,写了个脚本:

const path = require("path");
const COS = require("cos-nodejs-sdk-v5");
const glob = require("glob");
const yargs = require("yargs");

const argv = yargs.argv;
const options = {
  Bucket: argv.Bucket, // COS 桶
  Region: argv.Region, // COS 桶区域
};

const dist = path.resolve(__dirname, "../dist"); //需要上传的文件夹
const cos = new COS({
  SecretId: argv.SecretId, // COS 认证
  SecretKey: argv.SecretKey, // COS 认证
});

let uploadFilesHash = {};

glob(
  path.join(dist, "**/*"),
  {
    nodir: true,
  },
  function (er, files) {
    files.forEach((x) => {
      let key = path.relative(dist, x).split(path.sep).join("/");
      uploadFilesHash[key] = x;
    });

    cos.uploadFiles(
      {
        files: Object.keys(uploadFilesHash).map((key) => {
          return {
            Bucket: options.Bucket,
            Region: options.Region,
            Key: key,
            FilePath: uploadFilesHash[key],
          };
        }),
        SliceSize: 1024 * 1024,
        onProgress: function (info) {},
        onFileFinish: function (err, data, options) {
          console.log(options.Key + "上传" + (err ? "失败" : "完成"));
        },
      },
      function (err, data) {}
    );
  }
);

Backblaze B2 初次访问也慢,好在数据不会变,可以把 CDN 缓存时间调大些。