辛宝Otto

辛宝Otto 的玄酒清谈

北漂前端程序员儿 / 探索新事物 / Web Worker 主播之一/内向话痨
xiaoyuzhou
email

使用 Nitro + Serverless 函数实现动态头图 Banner

image.png

关联内部链接:

  • [[速通 - unjs 家族的 Web 框架 Nitro]]
  • [[速通 - 腾讯云云函数 scf]]
  • [[编写一个小工具方便快速为文章添加 Banner]]
  • [[速通 - 动态 SVG 图片生成方案 Satori]]

背景#

现在比较习惯在文章顶部增加一张包含当前标题的图片,在不同平台会默认第一张图片有用,比如认为这是文章 Banner,Cover, Hero Image 等说法。

之前在 [[编写一个小工具方便快速为文章添加 Banner]] 文章中就提到了我写了一个网站,可以在页面上操作生成 banner。

那有没有一种办法直接写一个服务,当我访问 xx.com/cover?title=xxx 时候自动返回一张图片,这样的话,我就不需要打开页面去创建了。后续我只需要查看日志就知道有多少人访问过啦!

而且不同于浏览器,在 node.js 里渲染图片要更复杂一点,不可以像浏览器那样把计算、渲染的能力丢给浏览器了,最直接的就是 html+css 生成截图不可以了,因为 node 环境里没有 css 渲染的能力,得靠 puppeteer 那一套,得使用 canvas/svg 等方案来画。

Canvas 的跨平台兼容就显现出来了。但 Canvas 没接触过不太会,得现学。后来发现 svg 也非常简单,而且返回的是字符串,体积更小。

鱼和熊掌不可兼得啊。

需求设计#

首先这是一个 node 服务,我们携带参数请求接口,返回的是一张图片。这就需要我们部署后端。

相通的请求参数应该出现一致的结果,主要是背景图,不要一刷新页面里的图片变了,产生不必要的误会。

后续可以增加不同的模板,展望可以做成服务对外提供。

参数我们希望:

  • width 宽度
  • height 可选,自动高度
  • 比例 4:3 16:9 21:9 2:1 1:1 等
  • title 文字内容
  • source 角标,比如 @ijust.cc
  • 指定色相 0-360
  • 默认开启渐变 linear-gradient

这样基本就和网页版 https://ijust.cc/tools/canvas 一致了。

技术设计#

交付托管#

node 服务托管,显然是可以使用 serverless,我们可以选择国内的云厂商服务,也可以使用 cloudflare/vercel 提供的服务。考虑服务为了正常需要迁移,可以选择 serverless 框架,之前提到过的 [[速通 - unjs 家族的 Web 框架 Nitro]] 就可以拍上用场了。Nitro 可以很容易构建出不同的产物。

云厂商这里我们使用腾讯云 Serverless 函数,后面补充 Cloudflare Workers。

主题色持久化#

相通的内容产生相通的结果,设计一个方案,根据 title 内容算出一个数字,表达主色调。问了 GPT 给了几个方案,比如每个字符串去算 charCodeAt ,得到的数字进行处理。

function stringToDegrees(inputString) {
    let hash = 0;
    for (let i = 0; i < inputString.length; i++) {
        let char = inputString.charCodeAt(i);
        hash = ((hash << 5) - hash) + char;
        hash |= 0; // 将哈希值转换为32位整数
    }
    return Math.abs(hash % 360);
}
stringToDegrees('中文') // 194

使用 Satori 制作动态 SVG#

画布因为考虑 Canvas,很多东西就不能复用了,经过实践发现 Vercel 的 Satori最近比较火热,在 [[12 - 速通 syntax.fm 659 OG Image]] 这一期其实也提到过。写 demo 成功了,以后能经常用得上。

image.svg

这么一张 SVG 图,才 4.5k 大小,要是 jpg 和 png 都不敢想。至少大 10 倍、20 倍。

技术实现#

这里我们准备一个新仓库用来解释这一切,后续也可以作为分享内容。

代码在这里 https://github.com/Otto-J/serverless-dynamic-banner

我先去写代码了,写完代码回来补充。

经过一些时间后,终于写完了,serverless 服务也上线可用了。大量吐槽来了,气人😡。

踩坑经验#

之前讲到本次技术选择是:nitro + 腾讯云 serverless,目标是上线 serverless 服务,最终我们手里有一个网址,一打开是一张图片。

Nitro 文档稀烂#

我选择 nitro 有两个原因:

  • 不用我学习新语法,比如 next.js 的那些概念,轻松上手,比 express/koa 相比不是玩具,大量内置了 ts、路由、中间件、常用方法,更快更容易写出真实业务代码
  • Nitro 是 nuxt 的组成部分,学会了 nitro 一鱼两吃,写后端服务、写全栈服务都可以用得上,经验直接复用

问题来了,如果我想获取路由的信息,设置返回的信息等技术细节,我应该怎么学会呢?问 GPT,GPT 不知道这么新的东西。只能看文档和教程。

Nitro 文档就几页,没中文,功能介绍跟 ppt 似的,也没有 playground,看的眼睛都花了,也没找到 api 索引。生气,guide+api 索引 + playground + 项目实战不应该标配嘛!

image.png

不过我是高手,好在我知道 Nitro ([[速通 - unjs 家族的 Web 框架 Nitro]]) 是 unjs/h3 ([[unjs_h3]]) 的封装,h3 提供低级 api,nitro 提供高级封装开箱即用。所以我应该去看 h3 的文档,聪明如我。

github 搜索 unjs h3,打开 https://github.com/unjs/h3 结果...

image.png

算了,告诉你吧,api 索引在这里,在 h3 长长文档的最下方

image.png

如何设定返回值时候,指定 content-type 为 svg?自己试。结论是使用 send 方法。

对了,如果你是 Nuxt 用户,那么你需要按照这个路线学习 Nuxt 文档 - Nitro 文档 - h3 文档。

腾讯 Serverless 函数体验贼差#

之前文档也提到过 ([[速通 - 腾讯云云函数 scf]]),作为国内大厂,值得信赖,而且做了也不是一年两年了。

结果体验贼差,我以为的云函数上线过程:

  • 授权 github 信息,读取项目
  • 选择项目源码,自动或者手动选择基本信息,帮我做好构建
  • 拿到交付产物,上传到云端
  • 给我一个地址,我一点击就访问通了
  • 告诉我你可以自定义网址,为了安全和推广其他服务,建议你开通网关鉴权、监控、日志之类的
  • 用吧,额度不够可以买套餐

挺合理的,服务好用户,也能带一带其他商业服务。

实际上不是,每一步都有大量的错误,大量的延伸技术概念,大量的问号帮助文档。生怕我觉得前端上线个服务挺简单,没有技术难度。

无力吐槽,算了,不写了,好在走过来了。

image.png

image.png

image.png

后面单独开个文档介绍吧。

DNSPOD 扭扭捏捏不融入腾讯云#

DNSPOD 被腾讯云收购了,现在属于腾讯云。即便我域名、备案、解析都在腾讯云和 DNSPOD,我开通 DNS 解析,还是要重复开页面、登录、扫码、编写。

麻烦死了。

自己愚蠢文件放错了位置#

这个看我源码就知道,为了目录好看,我包了一层,结果脑抽忘了,一些文件放外层了,怎么都拿不到,嘿嘿,后面不同项目开不同的窗口,乱了就不好了。

satori 不支持 css 和图片的特性#

因为 svg 技术限制,satori 不支持 z-index,不支持 transfrom,必须全程使用 flex 布局,字体必须指定,不支持 webp/avif 等图片格式,不支持 hsl 颜色格式。

怎么说呢,好在东西不复杂,样式改改也就能用了。

我 banner 不是有一个小人头像么,svg 是要通过 base64 内嵌进去的,体积会变大。为了减少图片体积,我尝试转 svg,转 avif/webp 等格式,都不是很满意。

最后用的 png 颜色减少,感觉 24 色就足够了,轻松让 png 缩小了五六倍,肉眼看不出差异,挺好的。

总结展望#

东西做完了,感受如何,昨天都气疯了,今天算写完了。

下次还来,希望轻车熟路,能快一点。

做国内的云服务虽然有价格战,但还是有位置的,业务还是要专注、做好。服务使用流畅应该是标配的。

加载中...
此文章数据所有权由区块链加密技术和智能合约保障仅归创作者所有。