前端新思路:组件即函数和Serverless SSR实践-阿里云开发者社区

开发者社区> AlibabaF2E> 正文
登录阅读全文

前端新思路:组件即函数和Serverless SSR实践

简介: 在今天,对于Node.js运维和高并发依然是很有挑战的,为了提效,将架构演进为页面即服务,可是粒度还不够,借着云原生和Serverless大潮,无运维,轻松扩展,对前端是极大的诱惑。那么,基于FaaS之上,前端有哪些可能性呢?

image.png

在今天,对于Node.js运维和高并发依然是很有挑战的,为了提效,将架构演进为页面即服务,可是粒度还不够,借着云原生和Serverless大潮,无运维,轻松扩展,对前端是极大的诱惑。那么,基于FaaS之上,前端有哪些可能性呢?

2019年上半年,我在阿里巴巴经济体前端委员会推进的Serverless研发体系共建项目中负责Serverless SSR的研究,将CSR,SSR,边缘渲染进行整合和尝试,提出组件即服务的概念(Component as Service),试图结合FaaS,做出更简单的开发方式。本次分享主要围绕Serverless SSR和它的演进过程、背后思考为主。

本文是狼叔在D2大会的分享《前端新思路:组件即服务和Serverless SSR实践》的内容,阅读需要10分钟,你将了解如下内容。

  1. 可以了解Serverless时代端侧渲染面临的具体问题
  2. 可以了解Serverless SSR规范以及渲染体系的完整工作链路和原理
  3. 为业内提供解决Serverless SSR渲染问题的新思路

image.png

狼叔(网名i5ting)现为阿里巴巴前端技术专家,Node.js 技术布道者,Node全栈公众号运营者,曾就职于去哪儿、新浪、网秦,做过前端、后端、数据分析,是一名全栈技术的实践者。目前负责BU的Node.js和基础框架开发,已出版《狼书(卷1) 更了不起的Node.js》。

Wolfred Sang (a.k.a. i5ting) is a full-stack developer and Node.js evangelist. He works for Alibaba Group as a Principal Front-End Developer and runs a self-media on the topic of Full-stack Node.js. He worked for well-known dot-com companies in the past, such as Qunar, Sina and NQ Mobile. His expertise and experiences cover not only front-end development but also back-end engineering and data insight extracting. He is currently leading the Node.js development and maintaining the core codebase in his business unit. His book “The Marvelous Node.js” (Part I) was published in July 2019.

技术趋势分析

image.png

今年的技术趋势,我的判断是技术混乱期已过,提效才是今日的挑战。

image.png

在Node.js领域,今年新东西也不多,最新已经发布到13,lts是12,Egg.js的生态持续完善,进度也不如前2年,成熟之后创新就少了。在很多框架上加入ts似乎已经政治正确了。比如自身是基于ts的nest框架,比如阿里也开源了基于Egg生态的midway框架,整体加入ts,类型系统和oop,对大规模编程来说是非常好的。另外GraphQL也有很强的应用落地场景,尤其是Apollo项目带来的改变最大,极大的降低了落地成本。已经用rust重写的deno稳步进展中,没有火起来,但也有很高的关注度,它不会替代Node.js,而是基于Node之上更好的尝试。

今日的Node.js存在的问题是会用很容易,做到高可用不容易,毕竟高可用对架构运维要求更好一些,这点对前端同学要求会更难一些。做到高可用之后,做性能调优更难。其实,所有这些难点的背后是基于前端工程师的角度来考虑的,这也是非常现实的问题。

对于很多团队,上Node是找死,不上Node是等死。在今天web框架已经相当成熟,所以如何破局,是当下最破解需要解决的问题。

你可能会感觉Node.js热度不够,但事实很多做Node.js的人已经投身到研发模式升级上了。对于今天的Node.js来说,会用很容易,但用好很难,比如高可用,性能调优,还是非常有挑战的。我们可以假想一下,流量打网关,网关根据流量来实例化容器,加载FaaS运行时环境,然后执行对应函数提供服务。在整个过程中,不许关心服务器和运维工作,不用担心高可用问题,是不是前端可以更加轻松的接入Node.js。这其实就是当前大厂前端在做的前端基于Serverless的实践,比如基于FaaS如何做服务编排、页面渲染、网关等。接入Serverless不是目的,目的是让前端能够借助Serverless创造更多业务价值。

为何如此钟爱SSR?

在2017年底,优酷只有passport和土豆的部分页面用Node.js,QPS不高,大多是一些尝试性业务,优酷PC和H5核心页面还都是PHP模板渲染。最近2年基于阿里强大的技术体系,我们也对PC、H5多端进行了技术改造。今年双十一是React SSR第一次扛双十一,具有一定意义,这里简单总结回顾和展望。

背景就不赘述了,参见《看优酷 Node 重构之路,Serverless SSR 未来可期》

image.png

  • 将优酷C端核心页面全部用Node重写,完成了PHP到Node.js的迁移。在没有PHP同学的情况下,前端可以支撑业务。
  • 性能提升明显,从v1(Bigpipe+jQuery)到v2(React SSR),性能逐步提升。PC页面首屏渲染降到150ms、播放器起播时间从4.6秒优化到2秒。H5站上了React SSR后,性能提升3倍,H5唤端率提升也极其明显,头条短视频唤端率由5.68%提升到9.4%,环比提升65%。单机性能qps从80提升150+(压测最高可以到300左右)。
  • QPS过万,2年没有p4以上故障,相对来说是比较稳定的。扛过双十一、世界杯,最高三倍以上的流量。
  • 在集团前端委员会承担Serverless SSR专项。

裕波曾经问我,为何如此钟爱SSR?

从前端的角度看,它是一个相对小的领域。PC已经非主流,H5想争王者,却不想被rn、weex中间截胡。怎么看,SSR能做的都有限。但是,用户体验提升是永远的追求,另外web标准化是正统,在二者之间,和Node做结合,除了SSR,目前想不到更好的解法。

贴着C端业务,从后端手里接过来PC、H5,通过Node构建自己的生存之地是必然的选择。

活下来之后就开始有演进,沉淀,通过C端业务和egg-react-ssr开源项目的沉淀,我们成功的打通2点。

  1. 写法上的统一:CSR和SSR可以共存,继而实现二种模式的无缝切换
  2. 容灾降级方案:从Node SSR无缝切换到Node的CSR,做到第一层降级,从Node CSR还可以继续降到CDN的CSR

2019年,另外一个风口是Serverless,前端把Serverless看成是生死之地,下一代研发模式的前端价值证明。那么,在这个背景下,SSR能做什么呢?基于FaaS的SSR如何呢?继续推演,支持SSR,也可以支持CSR,也就是说基于FaaS的渲染都可以支持的。于是和风驰商量,做了Serverless端侧渲染方向的规划。

本来SSR是Server-side render,演进为Serverless-side render。元彦给了一个非常好概念命名,Caaf,即Component as a fuction。渲染层围绕在以组件为核心,最终统统简化到函数层面。

在今天看,SSR是成功的,一个曾经比较偏冷的点已经慢慢变得主流。集团中,基于React/Rax的一体化开发,可以满足前端所有开发场景。优酷侧的活动搭建已经升级到Rax1.0,对外提供SSR服务。在uc里,已经开始要将egg-react-ssr迁移到FaaS上,代码已经完成迁移。

  • PC/中后台,React的CSR和SSR
  • 移动端/H5,Rax的CSR和SSR。尤其是Rax SSR给站外H5提供了非常好的首屏渲染时间优化,对C端或活动支持是尤其有用的。

在2020年,基于FaaS之上的渲染已经获得大家的认可。另外大量的Node.js的BFF应用已经到了需要治理的时候,BFF感觉和当年的微服务一样,太多了就会牵扯到管理成本,这种情况下Serverless是个中台内敛的极好解决方案。对前端来说,SSR让开发变得简单,基于FaaS又能很好的收敛和治理BFF应用,结合WebIDE,一种极其轻量级基于Serverless的前端研发时代已经来临了。

Serverless-side render概念升级

从BFF到SFF

了解SSR之前,我们先看一下架构升级,从BFF到SFF的演进过程。

BFF即 Backend For Frontend(服务于前端的后端),也就是服务器设计 API 时会考虑前端的使用,并在服务端直接进行业务逻辑的处理,又称为用户体验适配器。BFF 只是一种逻辑分层,而非一种技术,虽然 BFF 是一个新名词,但它的理念由来已久。

在Node.js世界里,BFF是最合适的应用场景。常见的API、API proxy、渲染、SSR+API聚合,当然也有人用来做网关。

从Backend For Frontend升级Serverless For Frontend,本质上就是利用Serverless基建,完成之前BFF的工作。那么,差异在哪里呢?

image.png

核心是从Node到FaaS,本质上还是Serverless,省的其实只是运维和自动扩缩容的工作,一切看起来都是基建的功劳,但对于前端来说,却是极为重大的痛点解决方案,能够满足所有应用场景,基于函数粒度可以简化开发,乃生死必争之地。

SFF前后端分工

Serverless简单理解是FaaS+BaaS。FaaS是函数即服务,应用层面的对外接口,而BaaS则是后端即服务,更多的是业务系统相关的服务。当下的FaaS还是围绕API相关的工作为主,那么,前端如何和Serverless绑定呢?

Serverless For Frontend(简称SFF)便是这样的概念,基于Serverless架构提供对前端开发提效的方案。

下面看一下SFF分工,这张图我自认为还是非常经典的。首先将Serverless劈成2半,前端和后端,后端的FaaS大家都比较熟悉了,但前端页面和FaaS如何集成还是一片待开发的新领域。

image.png

举个例子,常见BFF的例子,hsf调用获得服务端数据,前端通过ctx完成对前端的输出。这时有2种常见应用场景

  1. API,同后端FaaS(RPC居多)
  2. 页面渲染(http居多)

基于FaaS的页面渲染对前端来说是必须的。从beidou、Next.js、egg-react-ssr到Umi SSR,可以看出服务端渲染是很重要的端侧渲染组成部分。无论如何,React SSR都是依赖Node.js Web应用的。那么,在Serverless时代,基于函数即服务(Functions as a Service,简写为FaaS)做API开发相关是非常简单的。

1)无服务,不需要管运维工作
2)代码只关系函数粒度,面向API变成,降低构建复杂度
3)可扩展

image.png

目前还是Serverless初期,大家还是围绕API来做,那么,在FaaS下,如何做好渲染层呢?直出HTML,做CSR很明显是太简单了,对于React这种高级玩法如何集成呢?

其实我们可以做的更多,笔者目前能想到的Serverless时代的渲染层具有如下特点。

  • 采用Next.js/egg-react-ssr写法,实现客户端渲染和服务端渲染统一
  • 采用Umi SSR构建,生成独立umi.server.js做法,做到渲染
  • 采用Umi做法,内置Webpack和React,简化开发,只有在构建时区分客户端渲染和服务端渲染,做好和CDN如何搭档,做好优雅降级,保证稳定性
  • 结合FaaS API,做好渲染集成。 为了演示Serverless下渲染层实现原理,下面会进行简要说明。在Serverless云函数里,一般会有server.yml作为配置文件,这里以lamda为例子。

image.png

SSR概念升级

现状

  1. 现有FaaS主要是针对API层做扩展,视图渲染是一个新的命题。
  2. 竞品Serverless.com提供了Components类似的视图渲染层方案
  3. 如何打造一个基于阿里技术栈又有业界领先的端侧渲染解决方案

业界还缺少最佳实践,这是极好的机会。因此,我们对SSR做了概念上的升级(感谢justjavac大佬的提示)

image.png

Serverless端渲染层,是针对 SSR 做概念和能力升级

  • SSR 从 Server side render 升级为 Serverless side render,基于FaaS环境,提供端侧页面渲染能力。
  • Serverless渲染层涵盖的范围扩展,从服务器端渲染升级到同时支持 CSR 和 SSR 2种渲染模式。

在 Serverless 背景下,页面渲染层包含2种情况。

  • 基于 FaaS 的客户端渲染
  • 基于 FaaS 的服务器端渲染

目标是提供基于 FaaS 的页面渲染描述规范,提供标准化组件描述,统一组件写法,用法简单,易实现,可扩展。因此,我们制定了SSR-spec规范,下面会详细讲解。

Serverless-side render规范和实现原理

在讲规范之前,我们先简单了解3个术语。

image.png

CSR和SSR

先科普一下CSR和SSR的概念
客户端渲染(简称CSR),简单理解就是html是没有被动态数据灌入的,即所谓的静态页面。比如通过React、Rax编写的组件,打包构建后,以html文件形式分发到CDN上,不需要Node.js支持,就是非常典型的CSR。

image.png

资源加载完成后(注意:只有一个bundle,一次性吐出),通过React中的render API进行页面渲染。优点是不需要服务端接入,简单,对于性能要求不高的页面是非常合适的。中后台应用大多是CSR,优化也都是打包环节玩。

服务器端渲染(SSR),简单理解就是html是由服务端写出,可以动态改变页面内容,即所谓的动态页面。早年的php、asp、jsp这些Server page都是SSR的。但基于React技术栈,又有些许不同,server bundle构建的 时候,要吐多少模块,是server端决定的。client bundle和之前一样,差别在于这次是hydrate,而非render。

image.png

hydrate是 React 中提供在初次渲染的时候,去复用原本已经存在的 DOM 节点,减少重新生成节点以及删除原本 DOM 节点的开销,来加速初次渲染的功能。主要使用场景是服务端渲染或者像prerender等情况,所以在图中hydrate之后才是tti时间。

如果想全局了解CSR和SSR,共分5个阶段,参考下图。

image.png

纯服务端渲染和纯客户端渲染是2个极端,React SSR是属于中间的,这种服务端吐出的粒度是可以根据业务来控制的,可以服务端多一点,性能会差,也可以服务端吐出刚好够首屏的数据,其他由客户端来处理,这种性能会好很多。Static SSR和预渲染的CSR也是特定场景优化的神器。

最佳写法

了解了CSR和SSR的区别,下面我们看一下最佳写法是如何演进的。业内最好的实现大概是next.js了,抛开负责度不谈,单就写法来说,它确实是最合理的。

既然是基于React做法,核心肯定以Component为主,在Component上扩展静态方法用于接口请求是很好的实践。写法如下。

image.png

早年写过bigpipe相关事项,其中模块成为biglet,它的作用是获取接口,结合tpl生成html。

image.png

biglet的生命周期如下。

before
.then(self.fetch.bind(self))
.then(self.parse.bind(self))
.then(self.render.bind(self))
end

fetch是获取接口数据,parse解析数据,最终赋值给data。render是模板引擎编译的函数。每个函数的返回值都约定是promise,便于做流程控制。

很明显,biglet和Component是异曲同工的,而fetch是对象上的方法,必须实例化biglet才能调用,而next的做法getInitialProps是静态方法,不必实例化,内存和复用性上都是非常好的。

这点在egg-react-ssr项目技术选型调研期,我们就已经达成一致了。问题是,next只支持SSR,如何能够更好的支持CSR?做到真正的组件级别的同构。于是,基于这种写法,通过高阶组件进行包装,轻松实现了CSR,核心代码如下。

image.png

既然写法上统一了,那么,我们还能进一步进行优化么?核心点在网络获取部分。我们能看到的gRPC-web或isomorphic-fetch,分别实现了:1)RPC和http的约定,2)CSR和SSR中fetch统一。给我们带来的启示是在getInitialProps里,我们还可以做更多同构的玩法。

image.png

在webpack打包构建server bundle了的时候会注入isBrowser变量。

const plugins = [
  new webpack.DefinePlugin({
    '__isBrowser__': false //eslint-disable-line
  })
]

以此来区分CSR和SSR做同构兼容就更简单了。

Page.getInitialProps = async (ctx) => {
    if (__isBrowser__) {
        // for CSR
    } else {
        // for SSR
    }
}

以上做法,都是我们基于 https://github.com/ykfe/egg-react-ssr 提炼出来的实践,这些都是SSR-spec的基础。

FaaS和SSR如何结合

有了egg-react-ssr,确立了最佳写法,接下来就是结合FaaS做好集成。

image.png

对比一下,getInitialProps的参数和FaaS函数的参数都有context,这个是非常好的

解法:给 context 扩展SSRRender方法。

为了演示Serverless下渲染层实现原理,下面会进行简要说明。在Serverless云函数里,一般会有server.yml作为配置文件,这里以lamda为例子。

image.png

通过这个配置,我们可以看出函数app.server对应的http请求路径是'/',这个配置其实描述的就是路由信息。对应的app.server函数实现如下图。

image.png

通过提供ctx.SSRRender方法,读取dist目录下的Page.server.js完成服务端渲染。

核心要点:

  • SSRRender方法比较容易实现
  • 采用类似Umi SSR的方式,将源码打包到Page.server.js文件中
  • 在发布的时候,将配置,app.server函数和Page.server.js等文件上传到Serverless运行环境即可

架构升级4阶段

纵观SSR相关技术栈的演进过程,我们大致可以推出架构升级的4阶段。

  • CSR,很多中后台都是这样的开发的,最常见
  • 其次是阿里开源的beidou,基于egg做的React SSR,这是一个集成度很高的项目,很好用,难度也很大
  • Umi SSR是基于egg-react-ssr上演进出来的,在umi之上,用户不需要关心webpack,但打包后的代码返回的是stream,这点抽象,云谦做的非常到位,对于开发者来说,还是需要自建Node web server的。
  • 在Serverless里,具体怎么玩是需要我们来创造的。

image.png

对照上图,说明如下

  1. 在CSR中,开发者需要关心React和Webpack
  2. 在SSR中,开发者需要关心React、Webpack和Egg.js
  3. 在Umi SSR同构中,开发者需要关心React和Egg.js,由于Umi内置了Webpack,开发者基本不需要关注Webpack
  4. 在Serverless时代,基于FaaS的渲染层,开发者需要关心React,不需要关心Webpack和Egg.js

在这4个阶段中,依次出现了CSR和SSR,之后在同构实践中,对开发者要求更高,甚至是全栈。所有这些经验和最佳实践的积累,沉淀出了更简单的开发方式,在Serverless环境下,可以让前端更加简单、高效。

组件即函数

对于组件写法,我们继续抽象,将布局也拉出来。

function Page(props) {
  return <div> {props.name} </div>
}

Page.fetch = async (ctx) => {
  return Promise.resolve({
    name: 'Serverless side render'
  })
}

Page.layout = (props) => {
    const { serverData } = props.ctx
    const { injectCss, injectScript } = props.ctx.app.config
    return (
      <html lang='en'>
        <head>
          <meta charSet='utf-8' />
          <meta name='viewport' content='width=device-width, initial-scale=1, shrink-to-fit=no' />
          <meta name='theme-color' content='#000000' />
          <title>React App</title>
          {
            injectCss && injectCss.map(item => <link rel='stylesheet' href={item} key={item} />)
         }
        </head>
        <body>
          <div id='app'>{ commonNode(props) }</div>
          {
            serverData && <script dangerouslySetInnerHTML={{
              __html: `window.__USE_SSR__=true; window.__INITIAL_DATA__ =${serialize(serverData)}`
            }} />
          }
          <div dangerouslySetInnerHTML={{
            __html: injectScript && injectScript.join('')
          }} />
        </body>
      </html>
    )
}

export default Page

layout目前看只有第一次渲染有用,但做Component抽象是可以考虑的。现在是首次渲染模式,以后不排除递归组件树的方式(结合bigpipe可以更嗨),那时layout还是有用的。

这样看来,render、fetch、layout是函数,结合Serverless.yml(f.yml)配置,能否将他们放到配置里呢?

路由由f.yml的配置文件中,所以在f.yml增加render配置扩展,具体如下。

functions:
  home:
    handler: index.handler
    render:
      - Component: src.home.index
      - layout: src.home.layout
      - fetch: src.home.fetch
      - mode: SSR | CSR(默认SSR)
      - injectScript(自己改loyout更好)
          - runtime~Page.js
          - vendor.chunk.js
          - Page.chunk.js
      - injectCSS
          - Page.chunk.css
      - serverBundle: Page.server.js
    events:
      - http:
          path: /
          method:
            - GET
  news:
    handler: index.handler
    render:
      - Component: src.news.index
      - layout: src.news.layout
      - fetch: src.news.fetch
      - mode: SSR | CSR(默认SSR)
    events:
      - http:
          path: /
          method:
            - GET

元彦提了一个非常好的命名:组件即函数。最贴切不过。

将渲染、接口请求、布局分别拆成独立函数,放到配置文件里。如此做法,可以保证函数粒度的职责单一,对于可选项默认值也更友好。比如没有接口请求就不调用fetch,或者没有布局使用默认布局。

image.png

这里再拔高一下,CSR和SSR写法一致了,这种写法可以和FaaS结合,也就是说写法上拆成函数后,前端关心的只有函数写法了,使得面向组件开发更容易。

我们在抽象一下,提炼3重境界。

  • 组件即函数,就是上面将的内容。
  • 页面即函数,对开发者而言,其实页面的概念不大,第一次渲染布局,然后entry执行而已,如果抛开构建和配置细节,页面就是第一个组件,即函数
  • 页面即服务,是我之前提的概念,每个页面对应一个Node服务,这样的好处是避免服务器雪崩,同时可以降低页面开发复杂度。

对于页面即服务来说,一个FaaS函数提供http,天然就是独立服务,在基建侧,网关根据流量决定该服务的容器个数,完美的解决了页面渲染过程的所有问题,也就是我们只需要关注组件写法,组件写法又都是函数。所以,组
件即函数,是Serverless渲染层最好的概括。

下面这张图很好的表达了组件即函数的核心:统一写法和用法。

image.png

制定规范之前,定位还是先要想明白的。

  • 在FaaS rutime之上,保证可移植性
  • 通过CSR和SSR无缝切换,可以保证首屏渲染效率
  • 由于面向组件和配置做到轻量级开发,可以很好的结合

统一写法和用法是件约定大于配置的事儿,约定才是最难的,既要保证功能强大,还要写法简单,又要有扩展性。显然,这是比写代码更有挑战的事儿。

SSR规范

统一写法,上一小结已经讲过了。

image.png

接下来就是规范相关的周边,如下图。

image.png

构建,扩展,跨平台,以及目录都需要约定。

SSR-spec规范主要定义 SSR 特性,组件写法、目录结构以及 f.yml 文件扩展的编写规范。目录结构待讨论,例如新增功能的API与删除功能的API理论上应该放在一个project当中,此时应该在src目录下建立不同的文件夹来隔离不同函数的模块.

├── dist // 构建产物
│   ├── Page.server.js // 服务端页面bundle
│   ├── asset-manifest.json // 打包资源清单
│   ├── index.html // 页面承载模版文件,除非想换成传统的直接扔一个html文件部署的方式
│   └── static // 前端静态资源目录
│       ├── css
│       └── js
├── config // 配置
│   ├── webpack.js // webpack配置文件,使用chainWebpackConfig方式导出,非必选
│   └── other // 
├── index.js // 函数入口文件
├── f.yml // FaaS函数规范文件
├── package.json
├── src // 存放前端页面组件
│   ├── detail // 详情页
│   │   ├── fetch.js // 数据预取,非必选
│   │   ├── index.js // React组件,必选
│   │   └── layout.js // 页面布局,非必选,没有默认使用layout/index.js
│   ├── home // 首页
│   │   ├── fetch.js
│   │   ├── index.js
│   │   └── layout.js
│   └── layout
│       └── index.js // 默认的布局文件,必选,脚手架默认生成
└── README.md //

命令用法

$ SSR build
$ SSR deploy

生成的dist目录结构如下。

  • dist

    • funcName

      • static

        • clientBundle.js
        • js
        • css
        • images
      • serverBundle.js

构建命令

$ SSR build
$ SSR build --spa
$ SSR build hello
$ SSR build hello2

Serverless集成步骤,通用方案集成

$ SSR xxx
$ Serverless deploy // f deploy

这里以Umi为例,开发过程分3个步骤。

  1. 本地源码组件开发,然后通过umi dev完成构建
  2. 如果是线上,可以走线上构建服务(webpack打包),如果需要修改,可以走
  3. 构建后的产物结合FaaS函数,直接发布到Serverless平台上

image.png

规范里还有很多点也是有思考的,比如多组件支持是基于bigpipe的方式,首先写入layout布局,然后处理多个组件的组合逻辑,最终res.end即可。另外,组件上如果只有fetch方法,没有render方法也是没有问题的。写法有2种,Component的值是数组,即串行方式。Component的值是对象,即并行方式。限于篇幅,这里就不一一赘述了。参见http://gitlab.alibaba-inc.com/Node/SSR-spec

落地实践、性能和未来思考

打包与构建

Umi的实现是非常巧妙的,核心在于构建后的server bundle返回值是stream,解耦了对web框架的依赖。在当时还没有实现更好的,所以以Umi为例。

image.png

构建产物,各个文件大小的说明,有2种方式。

  • Node_modules打包到bundle,对FaaS runtime无依赖。
  • Node_modules不打包到bundle,放到FaaS runtime里。

这2种方式打包大小可以接受,性能上第二种会更好一点,但没有差很多。

image.png

快速切换CSR还是SSR

前面说了写法上的统一,在工程实践中,可以在配置里,直接设置type快速切换CSR还是SSR。在公司内部,还可以通过diamand配置下发的方式进行动态控制。

image.png

其实SSR-spec规范里,还做了更多扩展.

//检查query的信息或者url查询参数或者头信息
conf.mode = req.query.SSR || req.headers['x-mode-SSR'];

容灾打底方案

简单说,3层容灾

  • Node SSR优先,Node调用hsf。
  • Node CSR通过diamind可以快速切换,html是Node吐出的,前端走MTop请求。
  • 当Node服务挂掉,走CDN上的纯CSR,前端MTop请求。

该流程有以下优点:

  • 构建方式一致

    • 服务端/客户端文件构建方式一致
  • 发布方式一致

    • 发布方式一致,统一发布到 CDN,前端资源可以使用 CDN 加速
  • 无需服务端发布

    • 组件代码变动统一使用 diamond 下发版本号,无需服务端发布
  • 及时生效

    • diamond 配置下发后可及时生效

如图。

image.png

性能优化

性能优化的要点

  • 控制SSR中server端占的比例,性能和体验是鱼和熊掌不可兼得。看业务诉求。
  • 能缓存的尽量缓存,和BFF应用一样。

这里举个例子,对接口字段瘦身,就可以渠道意想不到的效果。

image.png

性能对比

优酷PC的React SSR性能很好,从v1(Bigpipe+jQuery)到v2(React SSR),性能逐步提升。PC页面首屏渲染降到150ms、播放器起播时间从4.6秒优化到2秒。H5站上了React SSR后,性能提升3倍,H5唤端率提升也极其明显,头条短视频唤端率由5.68%提升到9.4%,环比提升65%。单机性能qps从80提升150+(压测最高可以到300左右)。

image.png

淘宝的Rax SSR也性能优异,以一个带数据请求的真实 Rax SSR 应用为例,性能对比数据显示:WIFI 下, SSR 的首屏呈现时间相比 CSR 提升 1 倍;弱网环境下,SSR 相比 CSR 提升约 3.5 倍。

参见水澜Rax SSR:重塑 SSR 应用的开发体验

SSR Demo 地址:https://Rax-demo.now.sh/SSR/home
CSR Demo 地址:https://Rax-demo.now.sh/CSR/home

未来思考

从用户访问页面流程,具体如下。

image.png

要点

  • 应用网关,肯定是要有的,毕竟要挂域名,反向代理等
  • 页面,已经可以放到FaaS上,FaaS之上的http网关可以满足基本需求,离应用级别的还差很多。
  • API代理,这个基于Node FaaS函数非常容易实现
  • API,调用后端服务提供API,比如访问db等,基于Node FaaS函数也是非常容易实现

FaaS细化到函数粒度,在管理上会有巨大挑战,在架构上也需要重新设计。

  • 具有前端特色的函数管理:比如API、Page等类型,path,域名,甚至是具体的应用设置
  • 页面、组件和网关联动,让开发更简单快速
  • 周边,比如监控,统计,数据等等

这里尝试一张图来表示一下前后端的Serverless时代的分工。

image.png

  • 统一接入网关是必须的,主要是处理页面和域名的接入。
  • 对于页面进行抽象,围绕组件和搭建来展开,通过在线|本地构建,最终放到页面托管服务中。有了页面托管,才是万里长城的一小步。
  • 接下对API进行拆分,这也是组件组成里重要的部分。

围绕搭建,可以想象到的是组件和接口的抽象。组件除了智能化我能想到的很少,智能化在计算上也能弹性玩就更有想象力了。对于接口可以再细分

  • 直接操作表,虽不推荐前端做,但确实是必备能力。
  • 通过配置来生成,即元数据管理,对于已有API进行包装,逻辑编排是非常好的。
  • 如果都不满足,自己基于FaaS函数定制就好了。

image.png

搭建本身是提效的,组件和接口都能提效,对于前端的价值是尤其大的。前端Serverless专项里,也是有逻辑编排组的,原因大抵如此。

下面再解释一下页面托管服务实现原理。其实这是页面即服务的升级版,以前每个页面对应一个Node服务,这就导致很多Node服务的运维成本非常高,有了Serverless,依然还是一个页面对应一个FaaS函数,但函数的扩容是Serverless基建做的事儿。也就是说页面托管,其实是基于页面的FaaS函数的管理。垂直到页面管理,一切都自动化,会让开发更简单。

目前每个FaaS函数是可以提供http透出地址的,但这个地址不具备定制能力,所以在页面托管服务之上有一层应用网关是必须的。那么,应用网关和页面页面托管服务之间如何联动呢?

  • 最外层,统一接入网关里做应用管理,每个应用都有对应的域名和子应用网关,二者进行绑定 根据流浪,子应用网关也可以自动扩缩容。
  • 在应用设置里,管理子应用网关包含的path和页面,提供反向代理相关的基础功能即可。
  • 设置完子应用包含的页面之后,系统具备将对应页面同步到子应用网关的能力,并且当页面更新的时候能够自动同步,类似于etcd/consul等服务发现同步功能。

有了这部分设计,开发者只需要关注页面的编写就好了。比较上面的3点配置在系统中并不经常做。

image.png

基于上面的设计,目的是提高开发速度,沉淀前端中台,具体好处如下。

  • 中后台能够一定程度的收敛到一起,配置化
  • 页面和系统分离,应变能力更强,结合微前端可以有更多想象力
  • 所有开发聚焦到页面维度,能够更好的提效,比如组件沉淀,智能化,搭建等都可以更专注。
  • 很多后端服务也能够很好的沉淀和复用,这和后端常说的能力地图类似,将BFF聚合管理,简化开发

image.png

关于未来,我能想到的是

  • 组件:写代码或智能化生成
  • 页面:配置出来
  • 系统:配置出来

只需要组件级别的函数的轻量级开发模式里,必然会简化前端开发方式,提供人效,最终实现技术赋能业务的目的。在“大中台,小前台”的背景下,贡献前端应变能力。

image.png

总结

在《2019,如何放大大前端的业务价值?》一文中,我曾提过:“前端技术趋于成熟,不可否认,这依然是个大前端最好的时代,但对前端来说更重要的是证明自己,不是资源,而是可以创造更多的业务价值。在垂直领域深耕可以让大家有更多生存空间,但我更愿意认为Serverless可以带来前端研发模式上的颠覆,只有简化前后端开发难度,才能更好的放大前端的业务价值。”

致敬所有为Serverless付出的同仁们!

image.png

版权声明:本文内容由阿里云实名注册用户自发贡献,版权归原作者所有,阿里云开发者社区不拥有其著作权,亦不承担相应法律责任。具体规则请查看《阿里云开发者社区用户服务协议》和《阿里云开发者社区知识产权保护指引》。如果您发现本社区中有涉嫌抄袭的内容,填写侵权投诉表单进行举报,一经查实,本社区将立刻删除涉嫌侵权内容。

分享:
AlibabaF2E
使用钉钉扫一扫加入圈子
+ 订阅

阿里经济体前端技术最新内容汇聚在此,由阿里经济体前端委员会官方运营。我们的愿景是建立全球一流的前端团队,链接商业,让数字世界触手可及是我们的使命。阿里经济体前端委员会致力于加强技术前瞻性、推进集体成长、提升国际影响力。同时我们运营着阿里经济体前端的官方公众号:Alibaba F2E,欢迎关注。

官方博客