背景
www.cloudbase.net 云开发网站是基于 nextjs 开发,里面的内容是写在 js 配置文件。每当更新网站内容,都需要提交 git,并且本地进行静态导出,再借助腾讯云云开发的 cli 工具,部署到云开发控制台的「静态网站」服务。
但是内容的更新,不应该涉及到 git 记录(只包括代码或者配置的改动),而且每次改动都要手动 pull/push/deploy 一遍,属实麻烦。因此,需要进行动态化。
系统设计
动态化获取数据
利用 nextjs 提供的 getInitialProps 钩子,从 cms 系统对应的云数据库中拉取动态内容。并将最新的内容,结合模板代码导出为静态 html 文件。
在 getInitialProps 钩子中,环境既不是 browser,也不是 nodejs,而是 ssr 的环境。所以无法使用 tcb-js-sdk 以及 tcb-admin-node 这两个库来获取云开发的数据。
这里使用了 axios.js 来进行网络请求,理由如下:
- 完美支持 ssr、node、browser 环境:直接用于 getInitialProps 钩子
- 支持一级代理转发:可以在内网环境下获取外部数据
http 触发调用云函数
由于无法使用 tcb-js-sdk 和 tcb-admin-node,所以没办法通过 sdk 提供的 api 来读取云数据库的数据。
所以只能“曲线救国”,借助云函数 + http 触发功能来获取云数据库的数据:
- 在 cloudbase 控制台编写用于读取数据库的云函数
- 开启云函数的 http 触发:调用者可以通过 http url 的方式调用云函数,传入参数,获取云函数运行结果
- 在 getInitialProps 钩子中,使用 axios 调用远程云函数,获取最新数据
部分代码实现
在需要动态化内容的页面组件中,添加 getInitialProps 钩子,里面通过 axios 触发云函数,获取云函数数据,并将其挂入组件的 props 中。
组件内部根据 props 的内容,生成对应的 jsx 结构。
代码实现如下:
const MainPage = ({activities, courses, articles}) => {
// ...
return ()
}
Main.getInitialProps = async () => {
const promises = [
getActivities(),
getCourses(),
getArticles()
]
const [
activities,
courses,
articles
] = await Promise.all(promises)
return {
activities,
courses,
articles
}
}
Q:为什么不选择 SSR,而是使用静态导出?静态导出怎么保证实效性?
理论上来说,SSR 是 SEO+获取最新数据的理论最优方案。但是考虑到云函数搭配 ssr 存在冷热启动问题,而静态导出后的文件直接部署到云开发静态网站服务上,本质上是对象存储,访问速度更快,并且节省费用。
除此之外,借助 CI 工具,设置了定时构建,以获取最新数据进行更新。如果有紧急情况,开发人员也可以在平台手动触发 CI,获取实时最新数据。
一句话,最合适的方案不一定是最优的。