本框架为Serverless场景下的服务端渲染规范的实现,具有以下特点。
- 小:实现方式简洁使用方式优雅,构建生成的bundle文件少且小
- 全:支持SPA/MPA两种应用类型的开发,SSR/CSR两种渲染模式无缝切换,支持HMR,支持定制组件的渲染模式
- 美:基于Midway-faas框架,拥有强大的生态,可以发布到多个不同的Serverless平台
Serverless 解放了端开发者(不仅仅是 Web 开发者)的生产力,让端开发者可以更快、更好、更灵活地开发各种端上应用,不需要投入太多精力关注于后端服务的实现。”
传统应用开发流程
Serverless 应用开发流程
使用本框架开发Serverless SSR 应用开发流程
相比于传统服务端应用开发,我们将细节在底层统一抹平。前端开发者只需要关注业务逻辑,无需感知服务器的运行状况。成本和心智负担大大降低,只需要申请一个域名即可将应用发布到公网让所有用户可以访问。
开发者只需5分钟就可以快速的创建并发布一个SSR应用上线
$ node -v # 建议版本>=v10.15.0
v12.16.1
$ yarn -v # 建议使用yarn代替npm
1.21.1
$ yarn global add ssr # 全局安装ssr脚手架。等同于npm i -g ssr
$ ssr init # 创建example,支持SPA/MPA(开发中)两种类型的应用创建
$ yarn
$ ssr start
$ open https://localhost:3000
$ ssr build
$ ssr build index # 对指定函数进行构建(支持中)
发布命令
$ ssr deploy # 默认发布到阿里云函数计算服务,腾讯云支持中
首次发布需要输入阿里云账户信息,并且在阿里云控制台开通函数计算服务。账户信息在函数计算控制台查看。
将AccountId以及Key Secret在下面输入,只需要输入一次信息会储存在本地,之后deploy无需做该操作。
发布成功后得到一个http地址https://1812856288776972.cn-shanghai.fc.aliyuncs.com/***
。由于阿里云安全限制,无法直接在浏览器查看。需要手动配置域名转发过去。阿里云控制台域名服务
-> 域名解析设置
-> 函数计算控制台
-> 自定义域名
。之后打开域名便能够访问到发布的函数。
$ open https://ssr-fc.com/
$ open https://ssr-fc.com?csr=true # 以csr模式运行
结合阿里云云开发平台,全程上云开发Serverless SSR应用可参考该教程。
注:本规范适用于绝大多数的业务场景,如需额外定制请先想清楚是否必要!
- 前端框架: React
- 开发语言: TypeScript
- 代码风格: Standard
- 样式处理: less + css modules
- UI组件: 默认已对antd的使用做打包配置无需额外配置
- 前端路由: 约定式路由
- 数据管理: 待支持,暂定使用hooks
我们支持单页面应用(SPA)和多页面应用(MPA)两种常见的应用类型的开发。 关于SPA与MPA的区别如下(本表格转载自网络,如有侵权请提issue联系)
单页面应用(SinglePage Web Application,SPA) | 多页面应用(MultiPage Application,MPA) | |
---|---|---|
组成 | 一个外壳页面和多个页面片段组成 | 多个完整页面构成 |
资源共用(css,js) | 共用,只需在外壳部分加载 | 不共用,每个页面都需要加载 |
刷新方式 | 页面局部刷新或更改 | 整页刷新 |
url 模式 | a.com/pageone a.com/pagetwo |
a.com/pageone.html a.com/pagetwo.html |
用户体验 | 页面片段间的切换快,用户体验良好 | 页面切换加载缓慢,流畅度不够,用户体验比较差 |
转场动画 | 容易实现 | 无法实现 |
数据传递 | 容易 | 依赖 url传参、或者cookie 、localStorage等 |
搜索引擎优化(SEO) | 需要单独方案、实现较为困难、不利于SEO检索 可利用服务器端渲染(SSR)优化 | 实现方法简易 |
试用范围 | 高要求的体验度、追求界面流畅的应用 | 适用于追求高度支持搜索引擎的应用 |
开发成本 | 较高,常需借助专业的框架 | 较低 ,但页面重复代码多 |
维护成本 | 相对容易 | 相对复杂 |
单页面应用一个函数对应一个页面。一个页面对应多个path(即前端路由)。
这里我们使用约定式路由。无需手动编写路由配置文件,会根据文件夹名称及路径自动生成路由配置。
.
├── build # web目录构建产物
│ └── index
│ ├── client
│ └── server
├── config.js # 定义应用的配置
├── f.yml
├── package.json
├── src # 存放faas函数的handler
│ └── index.ts
├── tsconfig.json
├── web
│ ├── components # 存放公共组件
│ │ └── header
│ │ │ ├── index.less
│ │ │ └── index.tsx
│ │ └── layout # SPA应用只需要一个默认的layout
│ │ ├── index.less
│ │ └── index.tsx
│ ├── pages # pages目录下的文件夹会映射为前端路由
│ │ ├── index # index文件夹映射为根路由
│ │ │ ├── fetch.ts # 定义fetch文件用来获取数据,会自动注入到组件的props中
│ │ │ ├── index.less
│ │ │ └── render.tsx # 定义render文件用来定义页面渲染逻辑
│ │ └── detail
│ │ ├── fetch.ts
│ │ ├── index.less
│ │ └── render$id.tsx # 映射为/detail/:id
│ │ └── render$id$.tsx # 映射为/detail/:id?
│ ├── tsconfig.json # 仅用于编辑器ts语法检测
│ └── typings.d.ts
service:
name: serverless-ssr
provider:
name: aliyun
functions:
index:
handler: index.handler
render: # 定义页面渲染服务
mode: ssr
events:
- http:
path: /
method: get
- http:
path: /detail/*
method: get
api-index: # 定义api接口服务
handler: api.handler
events:
- http:
path: /api/index
method: get
api-detail:
handler: api.detail.handler
events:
- http:
path: /api/detail/*
method: get
package:
artifact: code.zip
$ ssr deploy # 此时只有一个函数需要发布,选择index函数发布即可
https://ssr-fc.com/ -> index 函数 -> 渲染index组件
https://ssr-fc.com/detail/* -> index 函数 -> 渲染detail组件
多页面应用一个函数对应一个页面。一个页面对应一个path(即服务端路由)。
这里我们的服务端路由存在多个,需要读取yml文件具体函数的配置
.
├── README.md
├── build
│ ├── mpa1
│ │ ├── client
│ │ └── server
│ └── mpa2
│ ├── client
│ └── server
├── f.yml
├── package.json
├── src
│ ├── mpa1handler.ts
│ └── mpa2handler.ts
├── tsconfig.json
├── web
│ ├── components # 存放公共组件
│ │ └── header
│ │ │ ├── index.less
│ │ │ └── index.tsx
│ │ └── layout # 默认的layout
│ │ ├── index.less
│ │ └── index.tsx
│ ├── pages
│ │ ├── index
│ │ │ ├── fetch.ts
│ │ │ ├── index.less
| | | ├── layout.tsx # 每个独立的页面可以有自己的layout
│ │ │ └── render.tsx
│ │ └── detail
│ │ ├── fetch.ts
│ │ ├── index.less
│ │ └── render$id.tsx
service:
name: serverless-ssr
provider:
name: aliyun
functions:
mpa1:
handler: mpa1.handler
render:
mode: ssr
events:
- http:
path: /
method:
- get
mpa2:
handler: mpa2.handler
render:
mode: ssr
events:
- http:
path: /detail/*
method:
- get
package:
artifact: code.zip
$ ssr deploy # 此时需要在终端选择需要发布哪个函数
https://ssr-fc.com/ -> mpa1 函数 -> 渲染mpa1文件夹下的render组件
https://ssr-fc.com/detail/* -> mpa2 函数 -> 渲染mpa2文件夹下的render组件
1)在 FaaS 函数里
在 FaaS 函数里,只需要调用ssr-core提供的render方法传入ctx即可
import { render } from 'ssr-core'
async handler () {
try {
const htmlStr = await render(this.ctx)
return htmlStr
} catch (error) {
return error
}
}
根据f.yml或者query来判断当前渲染模式
2)在 Node.js Web 框架里
该渲染方式实现是服务端无关的,理论上可以支持任何Node.js框架只需引入render方法以及有一个web目录,用法与Faas函数保持一致。
const Koa = require('koa');
const { render } = require('ssr-core')
const app = new Koa();
// mount routes from config
app.use(ssr)
// ctx.ssrRender()
app.get('/*', async ctx => {
ctx.body = render(ctx)
});
app.listen(3000);
// url查询参数或者头信息
conf.mode = req.query.ssr || req.headers['x-mode-ssr'];
- ssr(conf)
- cookie
- querystring
- header
此处需要考虑优先级,比如querystring第一,其次是f.yml里的render.mode。
config.js支持以下配置, 默认配置已适用于绝大部分应用, 无特殊需求不要修改
{
cwd: string; // 设置命令执行的cwd,默认为 process.cwd(),无特殊需求不需修改
isDev: boolean; // 当前运行环境,默认为 process.env.NODE_ENV
publicPath: string; // webpack-dev-server 的publishPath,默认为 /
useHash: boolean; // 生成文件是否带有 hash,默认本地运行关闭,生产环境构建时开启
port: number; // 前端静态资源本地开发时的监听端口,默认为 8000FaaS Server 会自动 proxy,无特殊需求不需要修改
faasPort: number; // 本地开发启动的FaaS 服务的端口,默认为3000
chunkName: string; // 生成的bundle的chunkName,默认为Page,无特殊需求不要修改
webpackDevServerConfig: webpackDevServer.Configuration; // webpack-dev-server 启动配置
staticPrefix: string; // 加载的静态资源前缀,需要发布到单独的cdn服务时可以使用该配置设置为cdn服务的地址
chainServerConfig: (config: Config) => Configuration; // 使用 webpack-chain 来修改服务端 wbepack 构建配置
chainClientConfig: (config: Config) => Configuration; // 使用 webpack-chain 来修改服务端 wbepack 构建配置
whiteList: RegExp[]; // 设置服务端构建配置 externals 的白名单,即需要让 webpack 来处理的模块
cssModulesWhiteList: RegExp[]; // 设置该选项指定样式文件不用 css-modules 处理,防止样式和 className 不匹配
}
遇到问题先去该文档找答案,该文档列举了开发SSR应用可能会遇到的大部分问题。如果没有预期的答案再提issue
Please read the document
虽然我们已经尽力检查了一遍应用,但仍有可能有疏漏的地方,如果你在使用过程中发现任何问题或者建议,欢迎提issue或者PR 欢迎直接扫码加入钉钉群