在落地一种技能的时候,我们首先要想一想:
是否一定须要引入这种技能呢?他能办理什么问题,或者能带来什么收益?为什么要采取这种技能选型而不是其他的?引入了这种技能后,会带来什么问题吗(比如额外的开拓本钱等)?上面三个问题思考清楚之后,才能真正地去落地。上面三个问题思考清楚之后,才能真正地去落地。而有赞教诲接入做事端渲染,正是为了优化 H5 页面的首屏内容到达韶光,带来更好的用户体验(顺便利于 SEO)。
说了这么多,以下开始正文。
一、后端模版引擎时期
在较早期间,前后真个合营模式为:后端卖力做事层、业务逻辑层和模版渲染层(表现层);前端只是实现页面的交互逻辑以及发送 AJAX。比较范例的例子便是 JSP 或 FreeMarker 模板引擎卖力渲染出 html 字符串模版,字符串模版里的 js 静态资源才是真正前端卖力的东西。
而这种形式,便是天然的做事端渲染模式:用户要求页面 -> 要求发送到运用做事器 -> 后端根据用户和要求信息获取底层做事 -> 根据做事返回的数据进行组装,同时 JSP 或 FreeMarker 模版引擎根据组装的数据渲染为 html 字符串 -> 运用做事器讲 html 字符串返回给浏览器 -> 浏览器解析 html 字符串渲染 UI 及加载静态资源 -> js 静态资源加载完毕界面可交互。
那么既然后端模版引擎时期带来的效果便是我们想要的,那为啥还有往后让前端发展做事端渲染呢?由于很明显,这种模式从开拓角度来讲还有挺多的问题,比如:
后端须要写表现层的逻辑,但实在后端更该当看重做事层(和部分业务逻辑层)。当然,实在也可以让前端写 JSP 或 FreeMarker,但从体验上来说,肯定不如写 JS 来的爽;本地开拓的时候,须要启动后端环境,比如 Tomcat,影响开拓效率,对前端也不友好;所授予前真个能力太少,使得前端须要的一些功能只能由后端供应,比如路由掌握;前后端耦合。二、SPA 时期
后来,出身了 SPA(Single Page Application),办理了上面说的部分问题:
后端不须要关心表现层的逻辑,只须要看重做事层和业务逻辑层就可以了,暴露出相应的接口供前端调用。这种模式也同时实现了前后端解耦。本地开拓的时候,前端只须要启动一个本地做事,如:dev-server 就可以开始开拓了。授予了前端更多的能力,比如前真个路由掌握和鉴权,比如通过 SPA + 路由但同时,也带来了一些问题:
页面的 DOM 完备由 js 来渲染,使得大部分搜索引擎无法爬取渲染后真实的 DOM,不利于 SEO。页面的首屏内容到达韶光强依赖于 js 静态资源的加载(由于 DOM 的渲染由 js 来实行),使得在网络越差的情形下,白屏韶光大幅上升。三、做事端渲染
正由于 SPA 带来的一些问题(尤其是首屏白屏的问题),接入做事端渲染显得尤为必要。// 终于讲到做事端渲染这个重点了。
而正是 Node 的发展和基于 Virtual DOM 的前端框架的涌现,使得用 js 实现做事端渲染成为可能。因此在 SPA 的上风根本上,我们顺便办理了由于 SPA 引入的问题:
做事端渲染的首屏直出,使得输出到浏览器的便是完备的 html 字符串模板,浏览器可以直接解析该字符串模版,因此首屏的内容不再依赖 js 的渲染。正是由于做事端渲染输出到浏览器的是完备的 html 字符串,使得搜索引擎能抓取到真实的内容,利于 SEO。同时,通过基于 Node 和前端 MVVM 框架结合的做事端渲染,有着比后端模版引擎的做事端渲染更明显的上风:可以优雅降级为客户端渲染(这个后续会讲,先占个坑)。3.1 实现
既然做事端渲染能带来这么多好处,那详细怎么实现呢?从官网给出的事理图,我们可以清晰地看出:
Source 为我们的源代码区,即工程代码;Universal Appliation Code 和我们平时的客户端渲染的代码组织形式完备同等,只是须要把稳这些代码在 Node 端实行过程触发的生命周期钩子不要涉及 DOM 和 BOM 工具即可;比客户端渲染多出来的 app.js、Server entry 、Client entry 的紧张浸染为:app.js 分别给 Server entry 、Client entry 暴露出 createApp() 方法,使得每个要求进来会天生新的 app 实例。而 Server entry 和 Client entry 分别会被 webpack 打包成 vue-ssr-server-bundle.json 和 vue-ssr-client-manifest.json(这两个 json 文件才是有用的,app.js、Server entry 、Client entry 可以抽离,开拓者不感知);Node 端会根据 webpack 打包好的 vue-ssr-server-bundle.json,通过调用 createBundleRenderer 天生 renderer 实例,再通过调用 renderer.renderToString 天生完备的 html 字符串;Node 端将 render 好的 html 字符串返回给 Browser,同时 Node 端根据 vue-ssr-client-manifest.json 天生的 js 会和 html 字符串 hydrate,完成客户端激活 html,使得页面可交互。3.2 优化
按照 Vue SSR 官方文档建立起一个做事端渲染的工程后,是否就可以直接上线了呢?别急,我们先看看是否有什么可以优化的地方。
3.2.1 路由和代码分割
一个大的 SPA,主文件 js 每每很大,通过代码分割可以将主文件 js 拆分为一个个单独的路由组件 js 文件,可以很大程度上减小首屏的资源加载体积,其他路由组件可以预加载。
复制代码
// router.jsconstIndex =()=>import(/ webpackChunkName: \"大众index\公众 /'./pages/Index.vue');constDetail =()=>import(/ webpackChunkName: \公众detail\"大众 /'./pages/Detail.vue');constroutes = [{path:'/',component: Index},{path:'/detail',component: Detail}];constrouter =newRouter({mode:'history',routes});
3.2.2 部分模块(不须要 SSR 的模块)客户端渲染
由于做事端渲染是 CPU 密集型操作,非首屏的模块或者不主要的模块(比如底部的推举列表)完备可以采取客户端渲染,只有首屏的核心模块采取做事端渲染。这样做的好处是明显的:1. 较大地节省 CPU 资源;2. 减小了做事端渲染直出的 html 字符串长度,能够更快地相应给浏览器,减小白屏韶光。
复制代码
// Index.vueasyncData({ store }) {returnthis.methods.dispatch(store);// 核心模块数据预取,做事端渲染}mounted() {this.initOtherModules();// 非核心模块,客户端渲染,在 mounted 生命周期钩子里触发}
3.2 3 页面缓存 / 组件缓存
页面缓存一样平常适用于状态无关的静态页面,命中缓存直接返回页面;组件缓存一样平常适用于纯静态组件,也可以一定程度上提升性能。
复制代码
// page-level cachingconstmicroCache = LRU({max:100,maxAge:1000// 主要提示:条款在 1 秒后过期。})server.get('', (req, res) => {consthit = microCache.get(req.url)if(hit) {// 命中缓存,直接返回页面returnres.end(hit)}// 做事端渲染逻辑...})
复制代码
// component-level caching// server.jsconstLRU =require('lru-cache')constrenderer = createRenderer({cache: LRU({max:10000,maxAge: ...})});// component.jsexportdefault{name:'item',// 必填选项props: ['item'],serverCacheKey:props=>props.item.id,render (h) {returnh('div',this.item.id)}};
3.2.4 页面静态化
如果工程中大部分页面都是状态干系的,以是技能选型采取了做事端渲染,但有部分页面是状态无关的,这个时候用做事端渲染就有点摧残浪费蹂躏资源了。像这些状态无关的页面,完备可以通过 Nginx Proxy Cache 缓存到 Nginx 做事器,可以避免这些流量打到运用做事器集群,同时也能减少相应的韶光。
3.3 降级
进行优化之后,是否就可以上线了呢?这时我们想一想,万一做事端渲染出错了怎么办?万一做事器压力飙升了怎么办(由于做事端渲染是 CPU 密集型操作,很耗 CPU 资源)?为了担保系统的高可用,我们须要设计一些降级方案来避免这些。详细可以采取的降级方案有:
单个流量降级 – 偶发的做事端渲染失落败降级为客户端渲染Disconf / Apollo 配置降级 – 分布式配置平台修正配置主动降级,比如可预见性的大流量情形下(双十一),可提前通过配置平台将全体运用集群都降级为客户端渲染CPU 阈值降级 – 物理机 / Docker 实例 CPU 资源占用达到阈值触发降级,避免负载均衡做事器在某些情形下给某台运用做事器导入过多流量,使得单台运用做事器的 CPU 负载过高旁路系统降级 – 旁路系统跑定时任务监控运用集群状态,集群资源占用达到设定阈值将全体集群降级(或触发集群的自动扩容)渲染做事集群降级 – 若渲染做事和接口做事是独立的做事,当渲染做事集群宕机,html 的获取逻辑回溯到 Nginx 获取,此时触发客户端渲染,通过 ajax 调用接口做事获取数据3.4 上线前准备
3.4.1 压测
压测可以分为多个阶段:本地开拓阶段、QA 性能测试阶段、线上阶段。
本地开拓阶段:当本地的做事端渲染开拓完成之后,首先须要用 loadtest 之类的压测工具压下性能如何,同时可以根据压测出来的数据做一些优化,如果有内存泄露之类的 bug 也可以在这个阶段就能被创造。QA 性能测试阶段:当通过本地开拓阶段的压测之后,我们的代码已经是经由性能优化且没有内存泄露之类严重 bug 的。支配到 QA 性能测试环境之后,通过压真实 QA 环境,和原来的客户端渲染做比拟,看 QPS 会低落多少(由于做事端渲染耗更多的 CPU 资源,以是 QPS 比拟客户端渲染肯定会有低落)。线上阶段:QA 性能测试阶段压测过后,若性能指标达到原来的预期,支配到线上环境,同时可以开启一定量的压测,确保做事的可用性。3.4.2 日志
作为生产环境的运用,肯定不能“裸奔”,必须接入日志平台,将一些报错信息网络起来,以便之后问题的排查。
3.4.3 灰度
如果上线做事端渲染的工程是供应核心做事的运用,该当采取灰度发布的办法,避免全量上线。一样平常灰度方案可以采取:百分比灰度、白名单灰度、自定义标签灰度。详细采取哪种灰度办法看场景自由选择,每隔一段韶光不雅观察灰度集群没有问题,以是逐渐增大灰度比例 / 覆盖范围,直到全量发布。
3.5 落地
在有赞电商的做事端渲染的落地场景中,我们抽离了单独的依赖包,供应各个能力。
3.6 效果
从终极的上线效果来看,相同功能的页面,做事端渲染的首屏内容韶光比客户端渲染提升了 300%+。
3.7 Q & A
Q1:为什么做事端渲染就比客户端渲染快呢?
A:首先我们明确一点,做事端渲染比客户端渲染快的是首屏的内容到达韶光(而非首屏可交互韶光)。至于为什么会更快,我们可以从两者的 DOM 渲染过程来比拟:
客户端渲染:浏览器发送要求 -> CDN / 运用做事器返回空 html 文件 -> 浏览器吸收到空 html 文件,加载的 css 和 js 资源 -> 浏览器发送 css 和 js 资源要求 -> CDN / 运用做事器返回 css 和 js 文件 -> 浏览器解析 css 和 js -> js 中发送 ajax 要求到 Node 运用做事器 -> Node 做事器调用底层做事后返回结果 -> 前端拿到结果 setData 触发 vue 组件渲染 -> 组件渲染完成
做事端渲染:浏览器发送要求 -> Node 运用做事器匹配路由 -> 数据预取:Node 做事器调用底层做事拿到 asyncData 存入 store -> Node 端根据 store 天生 html 字符串返回给浏览器 -> 浏览器吸收到 html 字符串将其激活:
我们可以很明显地看出,客户端渲染的组件渲染强依赖 js 静态资源的加载以及 ajax 接口的返回韶光,而常日一个 page.js 可能会达到几十 KB 乃至更多,很大程度上制约了 DOM 天生的韶光。而做事端渲染从用户发出一次页面 url 要求之后,运用做事器返回的 html 字符串便是完备的打算好的,可以交给浏览器直接渲染,使得 DOM 的渲染不再受静态资源和 ajax 的限定。
Q2:做事端渲染有哪些限定?
A:比较常见的限定比如:
由于渲染过程是在 Node 端,以是没有 DOM 和 BOM 工具,因此不要在常见的 Vue 的 beforeCreate 和 created 生命周期钩子里做涉及 DOM 和 BOM 的操为难刁难第三方库的哀求比较高,如果想直接在 Node 渲染过程中调用第三方库,那这个库必须支持做事端渲染Q3:如果我的需求只是天生文案类的静态页面,须要用到做事端渲染吗?
A:像这些和用户状态无关的静态页面,完备可以采取预渲染的办法(详细见 Vue SSR 官方指南),做事端渲染适用的更多场景会是状态干系的(比如用户信息干系),须要经由 CPU 打算才能输出完备的 html 字符串,因此做事端渲染是一个 CPU 密集型的操作。而静态页面完备不须要涉及任何繁芜打算,通过预渲染更快且更节省 CPU 资源。