SSR同构降级策略

SSR同构降级策略「福利」 ✿✿ ヽ(°▽°)ノ ✿:文章最后有抽奖,转转纪念 T 恤一件或转转随机手办一个,走过路过不要错过哦1、相关概念CSR:客户端渲染(Client Side...
摘要由CSDN通过智能技术生成

  SSR同构降级策略

「福利」 ✿✿ ヽ(°▽°)ノ ✿:文章最后有抽奖,转转纪念 T 恤一件或转转随机手办一个,走过路过不要错过哦


1、相关概念

  • CSR:客户端渲染(Client Side Render)。渲染过程全部交给浏览器进行处理,服务器不参与任何渲染。页面初始加载的HTML文档中无内容,需要下载执行JS文件,由浏览器动态生成页面,并通过JS进行页面交互事件与状态管理。

  • SSR:服务端渲染(Server Side Render)。DOM树在服务端生成,而后返回给前端。即当前页面的内容是服务器生成好一次性给到浏览器的进行渲染的。

  • 同构:客户端渲染和服务器端渲染的结合,在服务器端执行一次,用于实现服务器端渲染(首屏直出),在客户端再执行一次,用于接管页面交互(绑定事件),核心解决SEO和首屏渲染慢的问题。采用同构思想的框架:Nuxt.js(基于Vue)、Next.js(基于React)。


2、ssr(服务端渲染)实现方案

  • 使用next.js/nuxt.js的服务端渲染方案

  • 使用node+vue-server-renderer实现vue项目的服务端渲染

  • 使用node+React renderToStaticMarkup/renderToString实现react项目的服务端渲染

  • 使用模板引擎来实现ssr(比如ejs, jade, pug等)

我所在的部门采用得基于vue的Nuxt框架来实现ssr同构渲染,但是Nuxt并未提供相应的降级策略。当node服务端请求出现偶发性错误(非接口服务挂掉),本来应该在首屏渲染的模块会因无数据而显示空白,双十一等高流量情况下,出现人肉“运维”的无奈,想象一下其他小伙伴陪着对象,吃着火锅、唱着歌,你在电脑前抱着忐忑不安的心情盯着监控系统....我们需要一个降级方案以备不时之需。

3、vue-ssr实现流程

在我们开始降级方案之前,我们必须先对ssr的原理有一定的认知。接下来我们以vue(同理)为例如上图所示有两个入口文件Server entry和Client entry,分别经webpack打包成服务端用的Server Bundle和客户端用的Client Bundle。服务端:当Node Server收到来自客户端的请求后, BundleRenderer 会读取Server Bundle,并且执行它,而 Server Bundle实现了数据预取并将填充数据的Vue实例挂载在HTML模版上,接下来BundleRenderer将HTML 渲染为字符串,最后将完整的HTML返回给客户端。客户端:浏览器收到HTML后,客户端加载了Client Bundle,通过app.$mount('#app')的方式将Vue实例挂载在服务端返回的静态HTML上。如:

<div id="app" data-server-rendered="true">

data-server-rendered 特殊属性,让客户端 Vue 知道这部分 HTML 是由 Vue 在服务端渲染的,并且应该以激活模式(Hydration:https://ssr.vuejs.org/zh/hydration.html)进行挂载。

4、性能优化

降级的目的是为了预防node服务器压力过大时造成的风险,那么在这之前,也可以通过代码实现来做一定的优化,下面简单介绍下几个常规优化方法

  • 减少服务端渲染DOM数

  1. 当页面特别长的时候,譬如我们的常见的首页、商品详情页,底部会有推荐商品流、评价、商品介绍等并不会出现在首屏的模块,也不需要在服务端的时候执行渲染,这个时候可以结合vue的插槽系统、内置component组件的is,利用vue ssr服务端只会执行beforeCreate和created生命周期的特性,封装自定义组件,被该组件在mounted的时候将包裹的组件挂载到component组件的is属性上

  2. 通过vue高级异步组件封装延迟加载方法,只有当模块到达指定可视区域时再加载

function asyncComponent({componentFactory, loading = 'div', loadingData = 'loading', errorComponent, rootMargin = '0px',retry= 2}) {
  let resolveComponent;
  return () => ({
    component: new Promise(resolve => resolveComponent = resolve),
    loading: {
      mounted() {
        const observer = new InterpObserver(([entries]) => {
          if (!entries.isIntersecting) return;
          observer.unobserve(this.$el);

          let p = Promise.reject();
          for (let i = 0; i < retry; i++) {
            p = p.catch(componentFactory);
          }
          p.then(resolveComponent).catch(e => console.error(e));
        }, {
          root: null,
          rootMargin,
          threshold: [0]
        });
        observer.observe(this.$el);
      },
   
  • 2
    点赞
  • 5
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值