Nuxt 自适应 SSR 方案: SEO 和首屏最小化优化

本文探讨了如何通过自适应SSR方案解决SEO需求与首屏加载时间的矛盾。采用Nuxt Fetch Pipeline,根据场景控制数据加载,优化了首字节、首屏时间和可交互时间。分享了在实现过程中遇到的SVG骨架屏卡顿和Vue SSR客户端激活问题的解决策略,以及优化效果,包括服务响应时间和首屏大小的显著改善。
摘要由CSDN通过智能技术生成

20190922173229.png

目前项目采用 Nuxt SSR 来完成服务端渲染 ,为满足 SEO 需求,将非首屏内容也进行了请求和服务端直出,导致首屏时间变长(非首屏的资源请求和组件的渲染都会带来额外开销)。对于海量的用户来说,少量的爬虫访问需求反而影响了正常用户的访问,导致 SEO 和用户体验提升存在很大的矛盾。

为了解决这个问题,我们设计和实践了自适应 SSR 方案,来同时满足这两种场景的需求。今天会分享这个方案的技术细节、设计思路以及在实施该方案过程中遇到的一些相关的子问题的实践踩坑经验,欢迎大家一起交流。

分享大纲

  • 问题来源和背景
  • 问题解决思路
  • 自适应 SSR 方案介绍
  • 采用自适应 SSR 优化前后数据
  • Vue SSR client side hydration 踩坑实践
  • 使用 SVG 生成骨架屏踩坑实践

问题来源和背景

目前项目采用 Nuxt SSR 来完成服务端渲染,为满足 SEO 需求,将非首屏资源也进行了请求和服务端直出,导致首屏时间变长(非首屏的资源请求和组件的渲染都会带来额外开销)

优化前的加载流程图

20190808160403.png

目前我们的 Nuxt 项目采用 fetch 来实现 SSR 数据预取,fetch 中会处理所有关键和非关键请求

Nuxt 生命周期图

20190808160623.png

对于海量的用户来说,少量的爬虫访问需求反而影响了正常用户的访问,导致 SEO 和用户体验提升存在很大的矛盾。

为了解决这个问题,我们希望能区分不同的场景进行不同的直出,SEO 场景全部直出,其他场景只直出最小化的首屏,非关键请求放在前端异步拉取

解决思路

计划通过统一的方式来控制数据加载,将数据加载由专门的插件来控制,插件会根据条件来选择性的加载数据,同时懒加载一部分数据

  • 判断是 SEO 情况,fetch 阶段执行所有的数据加载逻辑
  • 非 SEO 场景,fetch 阶段只执行最小的数据加载逻辑,等到页面首屏直出后,通过一些方式来懒加载另一部分数据

优化后的项目影评页加载流程图

20190808162208.png

自适应 SSR 方案介绍

Gitlab CI Pipeline

20190808160912.png

自研 Nuxt Fetch Pipeline

借鉴 Gitlab CI 持续集成的概念和流程,将数据请求设计为不同的阶段 (Stage ),每个阶段执行不同的异步任务(Job),所有的阶段组成了数据请求的管线(Pipeline)

预置的 Stage

  • seoFetch : 面向 SEO 渲染需要的 job 集合,一般要求是全部数据请求都需要,尽可能多的服务端渲染内容
  • minFetch:首屏渲染需要的最小的 job 集合
  • mounted: 首屏加载完之后,在 mounted 阶段异步执行的 job 集合
  • idle: 空闲时刻才执行的 job 集合

每一个页面的都有一个 Nuxt Fetch Pipeline 的实例来控制,Nuxt Fetch Pipeline 需要配置相应的 job 和 stage,然后会自适应判断请求的类型,针对性的处理异步数据拉取:

  • 如果是 SEO 场景,则只会执行 seoFetch 这个 stage 的 job 集合
  • 如果是真实用户访问,则会在服务端先执行 minFetch 这个 stage 的 job 集合,然后立即返回,客户端可以看到首屏内容及骨架屏,然后在首屏加载完之后,会在 mounted 阶段异步执行 mounted stage 的 job 集合,另外一些优先级更低的 job,则会在 idle stage 也就是空闲的时候才执行。
Nuxt Fetch Pipeline 使用示例

page 页面 index.vue

import NuxtFetchPipeline, {
  pipelineMixin,
  adaptiveFetch,
} from '@/utils/nuxt-fetch-pipeline';
import pipelineConfig from './index.pipeline.config';

const nuxtFetchPipeline = new NuxtFetchPipeline(pipelineConfig);

export default {
  mixins: [pipelineMixin(nuxtFetchPipeline)],

  fetch(context) {
    return adaptiveFetch(nuxtFetchPipeline, context);
  },
};

配置文件 index.pipeline.config.js

export default {
  stages: {
    // 面向SEO渲染需要的 job 集合,一般要求是全部
    seoFetch: {
      type: 'parallel',
      jobs: [
        'task1'
      ]
    },
    // 首屏渲染需要的最小的 job 集合
    minFetch: {
      type: 'parallel',
      jobs: [
      ]
    },
    // 首屏加载完之后,在 mounted 阶段异步执行的 job 集合
    mounted: {
      type: 'parallel',
      jobs: [
      ]
    },
    // 空闲时刻才执行的 job 集合
    idle: {
      type: 'serial',
      jobs: [
      ]
    }
  },
  pipelines: {
    // 任务1
    task1: {
      task: ({ store, params, query, error, redirect, app, route }) => {
        return store.dispatch('action', {})
      }
    }
  }
}

并发控制

Stage 执行 Job 支持并行和串行 Stage 配置 type 为 parallel 时为并行处理,会同时开始每一个 job 等待所有的 job 完成后,这个 stage 才完成 Stage 配置 type 为 serial 时为串行处理,会依次开始每一个 job,前一个 job

评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值