项目地址: github.com/ykfe/ssr
一句话介绍: A future-oriented ssr framework based on midway-faas that implemented serverless-side render specification for faas.
2020年Serverless这个概念更加火热。Serverless 解放了端开发者(不仅仅是 Web 开发者)的生产力,让端开发者可以更快、更好、更灵活地开发各种端上应用,不需要投入太多精力关注于后端服务的实现。但是大部分Serverless框架提供的example都是在教你如何搭建一个api服务,对于页面渲染层的例子少之又少。而next.js,nuxt.js这些框架的例子对大部分用户来说理解和使用成本实在是太高了,并且在生产环境使用过程中会遇到各种问题。
由于以上背景,我们基于之前已经开源并且受到广泛好评的[最小而美的Egg + React + SSR 服务端渲染应用骨架]( github.com/ykfe/egg-re… ) (已受到阿里集团内部多个bu以及多个外部公司生产环境使用反馈开发体验远好于next.js等框架)进一步提炼出Serverless场景下的SSR框架。让SSR应用的开发和发布变得更加简单透明友好同样支持SSR/CSR两种渲染模式随时切换。只需5分钟便可以部署一个Faas 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 http://localhost:3000复制代码
$ ssr build $ ssr build index # 可以对指定的函数进行构建(支持中)复制代码
发布命令
$ ssr deploy # 默认发布到阿里云函数计算服务,腾讯云支持中复制代码
为了解决函数发布对应用代码包的大小有要求的问题,我们不能像之前一样随意的安装node_modules并且发布了这样会导致代码大小压缩后也极易超出50MB的限制。这里我们有以下几种方案解决该问题。
使用阿里云提供的nas功能将node_modules作为固定依赖上传到云端。但这样有很多缺点,分别是node_modules并不是固定文件,随时有可能有新的模块依赖加入。以及fun nas上传步骤复杂且需要时间。对开发者不友好。
如果你使用next.js这些框架,你会发现在对代码大小有限制的runtime平台根本无法发布。因为next这些模块的dependencies包括babel/webpack这些开发依赖。当你安装next模块时这些开发依赖也一并被安装了。这时候你的选择只有不开启externals选项,将这里模块代码全部打包进服务端bundle。这样会导致服务端bundle特别大,执行效率大大降低。
所以这里我们特定将SSR框架分为ssr-core,ssr两个主要的库。ssr模块包含本地开发依赖,在生产环境时我们只需要安装ssr-core模块即可运行应用。使得开发环境真正上传的代码压缩后只有3MB。
注:本规范适用于绝大多数的业务场景,如需额外定制请先想清楚是否必要!
我们支持单页面应用(SPA)和多页面应用(MPA)两种常见的应用类型的开发。 关于SPA与MPA的区别如下(本表格转载自网络,如有侵权请提issue联系)
SPA
单页面应用一个函数对应一个页面。一个页面对应多个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文件用来定义页面渲染逻辑 │ │ └── news │ │ ├── fetch.ts │ │ ├── index.less │ │ └── render$id.tsx # 映射为/news/:id │ │ └── render$id$.tsx # 映射为/news/: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 package: artifact: code.zip复制代码
$ ssr deploy # 此时只有一个函数需要发布,选择index函数发布即可复制代码
ssr-fc.com/ -> index 函数 -> 渲染index组件
ssr-fc.com/news -> index 函数 -> 渲染news组件
多页面应用一个函数对应一个页面。一个页面对应一个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 │ │ └── news │ │ ├── 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: /news method: - get package: artifact: code.zip复制代码
$ ssr deploy # 此时需要在终端选择需要发布哪个函数复制代码
ssr-fc.com/ -> mpa1 函数 -> 渲染mpa1文件夹下的render组件
ssr-fc.com/news -> 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'];复制代码
此处需要考虑优先级,比如querystring第一,其次是f.yml里的render.mode。
我们来自
(狼叔)团队,致力于探索SSR应用在各种场景下的实践。欢迎有志之士一起加入。
联系邮箱:langshu.ssl@alibaba-inc.com
虽然我们已经尽力检查了一遍应用,但仍有可能有疏漏的地方,如果你在使用过程中发现任何问题或者建议,欢迎提 issue 或者 PR 欢迎直接扫码加入钉钉群
我来评几句
登录后评论已发表评论数()