抱歉,用 Serverless 作这个标题主要是借势炒做。本文内容跟 Serverless 其实没半毛钱关系。前端
可是2X做者为啥要起这个名字博眼球呢?node
众所周知,Serverless 的概念并不像字面意义上那样的 “无服务”,而是将中心化的服务端应用打散成为一个个函数式的服务,节约了前端编码到产品上线中间服务部署的操做成本。本质上是一种 云计算执行模型(Cloud Computing Execution Model)。ios
聪明的童鞋应该看完标题和开篇第一张图就猜到了我药里究竟卖的什么葫芦 —— 本文内容实际上是关于 SSG(Static Site Generation,静态站点生成) 的一个解决方案。介绍一下鄙人最近是如何将一个 Mongo + Egg + React + Node 架构的动态站点(内部非开源版本图表库官网)降(mo)级(gai)为纯静态 SPA (开源图表库 Cloud Charts 的官网站点)并部署在 github pages 上的(注:考虑国内访问问题,目前托管在 gitee 和 netlify)。git
为了凸显方案的独特之处,顺便为开源项目引引流,难免要先说明一下我所遇到的需求场景的复杂度和非典型性:github
稍微介绍一下咱们目前正在折(guan)腾(shui)的一个 Github 开源项目:阿里云 Cloud Charts —— 又一个开源的信息可视化图表库。npm
众所周知,这绝壁又双叒叕是一群无良技工在重复造轮子!(这里是一段辛辣的讽刺)。json
一段不正经的补充说明:axios
- Cloud Charts 的前身是一套长期服务、应用于阿里云混合云场景的数据可视化解决方案(非开源版本),在平常工做中切实方便了不少不熟悉前端工做的研发童鞋进行业务实践
- 咱们推出开源版本的其中一个目的,在于寻找更多适合沉淀的业务场景和更多志同道合的优秀的小伙伴 -(^_^)/ 没错~就是你!
在维护 Cloud Charts 开源项目的同时,咱们也须要将其内网版本的文档、示例、教程等资料同步搬运到公网,以方便公网用户对项目的了解和使用。后端
而对于 Cloud Charts 的非开源版本(TXD Widgets),咱们已经实现了一套用于托管相关文档和数据的官网站点(集团内网)。这个内部站点是基于 MongoDB + Egg.js + React.js 形式开发和维护的,最重要的一个特色是依靠一个后台管理系统,方便开发图表库的童鞋可以及时维护和发布图表库项目的功能特性和对应的教程、示例、API文档等内容。api
为了不手动搬运内容、文档到外网所产生的费力且繁琐的工做,更为了能将站点的原生功能、特色尽量透出给外网用户,咱们但愿将这套站点(前台页面功能和内容)部署到外网。
通过一番讨论和思虑,咱们决定将原站点经过一系列工做流构建为一个 SPA 应用,方便快速发布和更新到 github pages 类型的静态页面服务中(真-Serverless 🐶🐶🐶)。
针对两种主流的 SSG 模型进行分析,咱们能够看到其各自的优缺点:
(1)基于构建时的 SSR(无头浏览器渲染后爬取静态内容)
优点
不足
(2)XPress 类型(如 VuePress Next 等框架)
优点
不足
内网站点的功能、复杂度以下图所示。
结合前面的相关分析,咱们须要另辟蹊径,找到一条符合国情特点的发展道路。通过必定的业务抽象和一段时间的技术尝试,咱们最终造成了一整套站点迁移的技术方案。
咱们发现,动态站点要构建静态站点,最主要也是最重要的一环就是数据服务的静态化(即:数据接口的内容构建为可静态托管的前端资源)。
如上图所示,整个过程能够分为:数据静态化、静态数据加载、图片资源本地化、SPA 构建打包 共 4 个环节。下面的篇幅咱们将逐个环节展开介绍,展示每一个环节的具体内容、遇到的问题和相应解法。
因为官网形态是比较结构化的内容页面,咱们针对不一样页面的特色、不一样数据的关联关系,撰写了相应的浏览器脚原本模拟用户的点击、切换和跳转等行为。同时将 exceed 的数据请求记录下来(注:exceed 是一个 http 请求工具库,实质上是对 axios 的业务包装 - npm地址),通过必定处理后转存为本地数据(对敏感数据记录和字段进行过滤,将 “请求参数-数据返回” 以对应关系存储为本地 JSON 文件)。
静态化的数据格式形如:
// demo.json
[
// 单个请求的静态化数据
{
// 查询的条件(请求的参数)
params: {
api: 'fetchAll',
params: { module: 'studio' },
data: { page: 1, size: 0 },
},
// 查询的结果(请求的返回)
response: {
status: 0,
message: 'success',
data: [...],
}
},
...
]
复制代码
(1)页面挂载静态数据
SPA 页面加载时,先将静态化的数据加载到页面的 js 做用域中,便于数据查询方法的消费:
// 将静态数据解析为字典形式以方便查询(字典键值对的查询效率远高于列表)
function parseGlobalData(list) {
var globalData = {};
list.forEach(function (x) {
var key = objectToKey(x.params);
if (!globalData[key]) {
globalData[key] = x.response;
}
});
return globalData;
}
// 将请求参数(一个obj)转为字符串(base64),便于做为字典键值对中的键名
function objectToKey(obj) {
return btoa(JSON.stringify(Object.entries(obj).sort()));
}
// 将静态数据挂载到页面的 js 全局变量
function appendGlobalData(data) {
if (!window.staticGlobalData) {
window.staticGlobalData = {};
}
window.staticGlobalData = Object.assign(
{},
window.staticGlobalData,
parseGlobalData(data)
);
}
复制代码
(2)改造 exceed.fetch 方法以消费全局数据
内网站点的数据是使用 exceed 类库请求后端接口进行异步加载的。在外网版本中,咱们使用 Proxy 的方式重写了 exceed.fetch 方法以从本地读取数据(前面环节产生的静态化数据)。原理示意以下:
// 判断是否为外网站点
if (isStaticSite()) {
const handler = {
apply: (target, thisCtx, [params]) => loadLocalData(params),
};
// 代理 exceed.fetch 方法以消费全局变量中的数据
exceed.fetch = new Proxy(eceed.fetch, handler);
}
// 从全局变量中查询数据的方法
function loadLocalData(params) {
const key = objectToKey(params);
return window.staticGlobalData[key];
}
复制代码
在完成数据的本地化后,经过 nodejs 脚本实现能够匹配出官网所用到的所有图片资源(CDN 地址),对公网不能访问的图片资源(如内网语雀、OSS 服务上的资源)进行本地化转存。
内、外版本的站点比较效果以下(仅方便展现部分首页内容):
能够看到,内网版本中图表库须要的重要功能模块都被原滋原味地还原、构建到外网站点中了(不可对外输出的部分在 SPA 构建时已经作了有效的过滤处理)。
写在后面:本文主要介绍 SSG 的技术方案和一些关键环节的奇技淫巧。SSG 的过程当中其实还有不少细节问题,如:如何在一套代码中维护内、外网不一样版本的内容差别等。感兴趣的童鞋欢迎留言讨论。
码字不易,既然看到这里,但愿您能给个点赞再走~ Orz 感激不尽!