性能优化:通用快照方案

简介: 本文我们将探讨快照技术如何增强页面性能和用户体验,如何在业务中集成快照方案,以及我们的通用快照解决方案的技术细节。

1. 写在前面

性能优化对于提供卓越的用户体验至关重要,钉钉终端团队特别关注用户体验。我们团队采用了一系列创新的性能优化措施,显著提升了首次有意义绘制(FMP)和首次内容绘制(FCP)的性能指标。其中,利用快照方案,结合用户的本地存储能力,我们能够进一步提高页面性能。快照方案是在完成常规手段前端优化(如优化首屏加载体积、实施懒加载、渲染优化和缓存提升等)和资源离线处理之后的又一重要步骤,旨在更迅速地向用户展示页面内容。


钉钉的 PC 工作台通过应用快照技术加速了页面渲染,并从此经验中提炼出了一个通用的快照 SDK,使得其他页面也能轻松集成此技术,从而提高其性能。不仅限于钉钉端内应用本身,同样适用于解决端外等各种场景下的性能提升需求。


接下来,我们将探讨快照技术如何增强页面性能和用户体验,如何在业务中集成快照方案,以及我们的通用快照解决方案的技术细节。


2. 快照方案概述

快照从概念上,这个词从摄影领域借鉴而来,在计算机领域是指在某个特定时间点对系统、数据或配置状态的完整副本,而系统或程序可以利用这一份记录实现快速恢复、启动优化等。


基于快照的性能体验优化手段,主要利用了存储在客户端本地的快照资源,加速页面速度,提升用户体验。

image.png

快照方案对前端性能提升的作用:改善首屏显示速度,减少白屏时间。


image.png


快照优点:能很好的保存每个用户千人千面的信息,并且有可能和 SSR+流式渲染结合。


3. 使用案例和效果演示


3.1 钉钉标准 PC 工作台首页

钉钉标准 PC 工作台通过快照技术提升页面性能:


image.png


数据效果

命中快照场景的P80时间 809ms


未命中快照场景 P80 时间 2926ms


image.png

3.2 钉钉机器人管理

接入快照前后对比视频

image.png

数据效果

BEFORE-无快照理论 FMP:369msFCP:229ms

image.png

AFTER-命中快照理论 FMP:52msFCP:169ms

image.png


4. 快照 SDK

4.1 简介

快照技术的核心生效机制包括三个步骤:保存快照、渲染快照和移除快照。我们将这三项功能模块化并提供了配置选项,简化了其他业务的快照能力集成流程。


作用:通过配置快照的 webpack 插件,使页面自动化具备快照功能;


原理:该插件会在构建时向项目中修改 html 文件内容,插入快照功能逻辑;


可配置:支持配置快照内容和关键流程时机、分平台灰度控制能力;


自动数据场景:接入后会自动在 Feel 平台自动增加快照场景,便于查看快照覆盖率以及进行相关性能感知。


image.png

4.2 接入指南

anpm 包

安装

tnpm install @ali/snapshot-dd-webpack-plugin --save-dev
# 或
ayarn add @ali/snapshot-dd-webpack-plugin --dev

使用

在您的webpack配置文件中配置:

快速体验快照能力版

const SnapshotDDWebpackPlugin = require('@ali/snapshot-dd-webpack-plugin');

module.exports = {
  // ...
  plugins: [
    new HtmlWebpackPlugin(),
    // 新增代码
    new SnapshotDDWebpackPlugin(),
  ]
  // ...
};

检测快照是否开启成功

  1. 修改webpack配置后,tnpm start重启项目。
  2. 查看element元素中是否有id为html-snapshot的快照节点,检查其中内容是否符合预期(快照一般会在第二次打开页面才展示快照,第一次打开页面会存储快照)。

精细化调整配置版

默认配置中保存快照、展示快照、移除快照时机均为默认值,若需更加精细化效果呈现,请在配置中调整。


const SnapshotDDWebpackPlugin = require('@ali/snapshot-dd-webpack-plugin');

module.exports = {
  // ...
  plugins: [
    new HtmlWebpackPlugin(),
    // 新增代码
    new SnapshotDDWebpackPlugin({
      // 可选配置选项config,详细可配置项说明见下文IConfig
      
      // 页面根元素id(即react全局挂载容器id),默认取dingapp
      // rootId: 'mytestid', 

      // 默认为false,使用indexDB存储方式,核心业务可配置true使用localStorage
      // useLocalStorage: true,

      // 灰度配置, 仅支持钉钉端内
      // grayConfig: {
      //   disable: '你的general模块key', // 禁用快照开关key
      //      mobile: 'win_snapshot_enable',
      //      pc: 'pc_snapshot_enable',
      
      //      mac: '你的general模块key', // 控制mac端能力灰度,仅在mac端生效
      //      win: 'win_snapshot_enable', // 控制win端能力灰度,仅在win端生效

      //      android: 'win_snapshot_enable', // 控制android端能力灰度,仅在android端生效
      //      ios: 'win_snapshot_enable', // 控制ios端能力灰度,仅在ios端生效
      // },
    
      // 自定义处理快照内容,将以该方法返回的内容作为页面快照内容
      // handleSnapshotHtml: (data) => '<div>test</div>',

      // 快照内容替换,可配置对快照做微调处理:挖空、替换可能发生改变产生闪烁的元素
      // snapshotSlotContentMap: {
      //   '.dtm-button-secondary': `<div class='xxx'></div>`,
      // },

      // 保存快照成功回调,可进行埋点等操作
      // takeSnapShotCallback: (data) => console.log('takeSnapShotCallback data:', data),

      // 快照时机,默认onload之后100ms
      // takeSnapShotDelay: 1000,

      // 配置检测到该元素上屏时,执行隐藏快照逻辑,例如'.your-class #yourId'
      // hideSnapshotSelector: '.dtm-button-secondary',

      // 移除快照成功回调,可进行埋点等操作
      // hideSnapShotCallback: (data) => console.log('hideSnapShotCallback data:', data),

      // 未配置hideSnapshotSelector时,会自动检测 FCP后2s 隐藏快照,配置隐藏的delay时间

      // 未配置hideSnapshotSelector时,会自动检测 FCP后2s 隐藏快照,配置隐藏的delay时间
      // hideSnapShotFCPDelay: 2000,

      // 配置不支持自动FCP的delay隐藏时间,默认3s
      // hideSnapShotNotSupportFCPDelay: 2000,

      // debug模式配置,debug模式会有更多log打点
      // debug: true,
    })
  ]
  // ...
};

可配置项IConfig

interface IConfig {
    // 页面根元素id(即react全局挂载容器id),默认取dingapp,若非dingapp,请指定
    rootId?: string;
    // 默认为false,使用indexDB存储方式,核心业务可配置true使用localStorage
    useLocalStorage?: boolean;
    // 灰度配置, 仅支持钉钉端内
    grayConfig?: {
        disable?: string; // 禁用快照
        pc?: string; // 控制PC端能力灰度,仅在PC端生效
        mobile?: string; // 控制移动端能力灰度,仅在移动端生效
        android?: string; // 控制android端能力灰度,仅在android端生效
        ios?: string; // 控制ios端能力灰度,仅在ios端生效
        mac?: string; // 控制mac端能力灰度,仅在mac端生效
        win?: string; // 控制win端能力灰度,仅在win端生效
    }

    // 自定义处理快照内容,将以该方法返回的内容作为页面快照内容
    handleSnapshotHtml?: string; // (html: string) => string;
    
    // 快照内容替换,可配置对快照做微调处理:挖空、替换可能发生改变产生闪烁的元素
    snapshotSlotContentMap?: {
        [querySelector: string]: string; // key为任意selector,value为HTML内容的字符串表示
    };
    // 保存快照成功回调,可进行埋点等操作
    takeSnapShotCallback?: string; // (html?: string) => void;
    // 快照时机,默认onload之后100ms
    takeSnapShotDelay?: number;

    // 配置检测到该元素上屏时,执行隐藏快照逻辑,例如'.your-class #yourId'
    hideSnapshotSelector?: string;
    // 移除快照成功回调,可进行埋点等操作
    hideSnapShotCallback?: string; // () => void;

    // 未配置hideSnapshotSelector时,会自动检测 FCP后2s 隐藏快照,配置隐藏的delay时间
    hideSnapShotFCPDelay?: number;
    // 配置不支持自动FCP的delay隐藏时间,默认3s
    hideSnapShotNotSupportFCPDelay?: number;

    // debug模式配置,debug模式会有更多log打点
    debug?: boolean;
}

灰度开关配置

注意:目前依赖钉钉 JSAPI, 仅支持钉钉端内


grayConfig?: {
    disable?: string; // 禁用快照
    pc?: string; // 控制PC端能力灰度,仅在PC端生效
    mobile?: string; // 控制移动端能力灰度,仅在移动端生效
    android?: string; // 控制android端能力灰度,仅在android端生效
    ios?: string; // 控制ios端能力灰度,仅在ios端生效
    mac?: string; // 控制mac端能力灰度,仅在mac端生效
    win?: string; // 控制win端能力灰度,仅在win端生效
}


请在钉钉gray平台创建general模块的key,可选以下纬度按需配置灰度key 。

1、【可选】禁用快照开关,不区分设备,优先级最高,默认值为false,灰度到的用户值为true,则无法使用快照 ;

2、【可选】按照平台类型建立的灰度key,用于灰度,可按照PC、移动端、Mac、Win、Android、iOS纬度进行灰度;

自定义用法

SDK 支持透出takeSnapshot 、removeSnapshot 方法,业务在项目中自行调用。

注意事项

  1. 请确保您的webpack配置文件中,HtmlWebpackPlugin已经配置好,否则快照功能无法生效;
  2. 请确保您的项目中,页面根元素id若非dingapp,请在配置中指定您的rootId,否则快照功能无法生效;
  3. 请确保您的项目中,将css以内联</span><span class="lake-fontsize-1515" style="color: #3E3E3E;">形式打包到html中已经配置好,否则快照功能中样式可能错乱;</span></li><li><span class="lake-fontsize-1515" style="color: #3E3E3E;">默认配置中保存快照、展示快照、移除快照时机均为默认值,若需更加精细化效果呈现,请在配置中调整;</span></li></ol><div><span class="lake-fontsize-1515" style="color: #3E3E3E;"><br /></span></div><h2 id="4pk50">实现方案</h2><div><span class="lake-fontsize-1515" style="color: #3E3E3E;">我们先一起回顾下</span></div><h4 id="UY74f" style="text-align: justify;"><span class="lake-fontsize-1515" style="color: #FF6827;">快照的作用是什么?</span></h4><div><span class="lake-fontsize-1515" style="color: #3E3E3E;">快照机制极大缩短了用户等待JavaScript资源加载并解析后页面完成渲染的时间。通过采取这样一种在 HTML 中尽早渲染快照的策略,我们能够优化页面的加载过程,提前页面的内容渲染,减少用户等待的白屏时间。</span></div><div><span class="lake-fontsize-1515" style="color: #3E3E3E;"><br /></span></div><div><span class="lake-fontsize-1515" style="color: #3E3E3E;"><span data-card-type="inline" data-ready-card="image" data-card-value="data:%7B%22src%22%3A%22https%3A%2F%2Fucc.alicdn.com%2Fpic%2Fdeveloper-ecology%2F6ibaby6qg4ku4_5bccbfb1f90d4516bb40d4468b14315c.png%22%2C%22originWidth%22%3A845%2C%22originHeight%22%3A227%2C%22name%22%3A%22image.png%22%2C%22size%22%3A221149%2C%22display%22%3A%22inline%22%2C%22align%22%3A%22left%22%2C%22linkTarget%22%3A%22_blank%22%2C%22status%22%3A%22done%22%2C%22style%22%3A%22none%22%2C%22search%22%3A%22%22%2C%22margin%22%3A%7B%22top%22%3Atrue%2C%22bottom%22%3Atrue%7D%2C%22width%22%3A499%2C%22height%22%3A134%7D"></span></span></div><div><span class="lake-fontsize-1515" style="color: #3E3E3E;"><br /></span></div><h3 id="7VxcM">1 工作原理</h3><ul style="text-align: justify;"><li><span class="lake-fontsize-1515" style="color: #3E3E3E;">生成快照:把页面中关键元素的数据缓存到本地存储</span></li><li><span class="lake-fontsize-1515" style="color: #3E3E3E;">展示快照:页面加载过程中,从本地存储中提取出之前保存的快照,并将其作为临时的DOM覆盖在实际页面之上</span></li><li><span class="lake-fontsize-1515" style="color: #3E3E3E;">移除快照:当页面的真实DOM渲染完毕,移除上层的DOM快照,让用户得以看到最新渲染的页面内容</span></li></ul><h3 id="kcY2l" style="text-align: justify;"><span class="lake-fontsize-1515" style="color: #FF6827;">Step1 生成快照</span></h3><div><span class="lake-fontsize-1515" style="color: #3E3E3E;">把页面中关键元素的数据缓存到本地存储。</span></div><h4 id="86v9K" style="text-align: justify;"><strong><span class="lake-fontsize-1515" style="color: #3E3E3E;">什么时候生成快照?</span></strong></h4><div><span class="lake-fontsize-1515" style="color: #3E3E3E;">一般情况下,在页面渲染完成之后,即页面 onload 。</span></div><h4 id="obxlI" style="text-align: justify;"><strong><span class="lake-fontsize-1515" style="color: #3E3E3E;">关键元素</span></strong></h4><div><span class="lake-fontsize-1515" style="color: #3E3E3E;">快照的内容包括页面哪些部分?</span><span class="lake-fontsize-1515" style="color: #3E3E3E;">对于首屏内容多变的场景,可以只对页面中每次基本不变的部分进行快照,使首屏部分内容实现秒出的同时避免快照闪烁。对于页面中一些不适合快照的部分,可以选择挖空或者替换为骨架屏的方式。</span></div><h4 id="eNOpE" style="text-align: justify;"><strong><span class="lake-fontsize-1515" style="color: #3E3E3E;">数据</span></strong></h4><div><span class="lake-fontsize-1515" style="color: #3E3E3E;">快照内容是什么形式?快照内容可以是两种:HTML 或图片,因 HTML 形式具备便于数据处理,并且可拓展性强的优点,采用 HTML 形式</span></div><h5 id="ITKKP" style="text-align: justify;"><strong><span class="lake-fontsize-1515" style="color: #3E3E3E;">快照形式内容对比</span></strong></h5><div style="text-align: justify;"><strong><span class="lake-fontsize-1515" style="color: #3E3E3E;"><span data-card-type="inline" data-ready-card="image" data-card-value="data:%7B%22src%22%3A%22https%3A%2F%2Fucc.alicdn.com%2Fpic%2Fdeveloper-ecology%2F6ibaby6qg4ku4_e35b75da18f94f0982b2ea98711f0fd5.png%22%2C%22originWidth%22%3A826%2C%22originHeight%22%3A533%2C%22name%22%3A%22image.png%22%2C%22size%22%3A367446%2C%22display%22%3A%22inline%22%2C%22align%22%3A%22left%22%2C%22linkTarget%22%3A%22_blank%22%2C%22status%22%3A%22done%22%2C%22style%22%3A%22none%22%2C%22search%22%3A%22%22%2C%22margin%22%3A%7B%22top%22%3Atrue%2C%22bottom%22%3Atrue%7D%2C%22width%22%3A464%2C%22height%22%3A299%7D"></span></span></strong></div><div style="text-align: justify;"><strong><span class="lake-fontsize-1515" style="color: #3E3E3E;"><br /></span></strong></div><div style="text-align: justify;"><strong><span class="lake-fontsize-1515" style="color: #3E3E3E;">本地存储</span></strong></div><div><span class="lake-fontsize-1515" style="color: #3E3E3E;">生成的快照存放在哪里?</span><span class="lake-fontsize-1515" style="color: #3E3E3E;">考虑到端内各业务在同一域名下共用 localStorage 内存,在快照 SDK 中默认将放在 indexDB 中,支持通过配置项使用 localStorage。(配置项中通过传入 useLocalStorage 参数控制)</span><span class="lake-fontsize-1515" style="color: #3E3E3E;">考虑到 indexDB 空间够用,故没有使用磁盘。</span></div><div><span class="lake-fontsize-1515" style="color: #3E3E3E;"><br /></span></div><div><span class="lake-fontsize-1515" style="color: #3E3E3E;">✅ localStorage</span></div><div><span class="lake-fontsize-1515" style="color: #3E3E3E;">✅ indexDB</span></div><ul style="text-align: justify;"><li><span class="lake-fontsize-1515" style="color: #3E3E3E;">磁盘</span></li></ul><h5 id="jDdG7" style="text-align: justify;"><strong><span class="lake-fontsize-1515" style="color: #3E3E3E;">存储位置对比</span></strong></h5><div data-card-type="block" data-ready-card="table" data-card-value="data:%7B%22html%22%3A%22%3Ctable%20style%3D%5C%22width%3A%20677px%3B%20text-align%3A%20justify%3B%5C%22%20class%3D%5C%22lake-table%5C%22%3E%3Ccolgroup%3E%3Ccol%3E%3Ccol%3E%3Ccol%3E%3Ccol%3E%3C%2Fcolgroup%3E%3Ctbody%3E%3Ctr%20style%3D%5C%22height%3A%2040px%3B%5C%22%3E%3Ctd%20colspan%3D%5C%221%5C%22%20rowspan%3D%5C%221%5C%22%20style%3D%5C%22vertical-align%3A%20middle%3B%5C%22%3E%3Cspan%20style%3D%5C%22color%3A%20rgb(62%2C%2062%2C%2062)%3B%5C%22%20class%3D%5C%22lake-fontsize-1515%5C%22%3E%E5%AD%98%E5%82%A8%E6%96%B9%E5%BC%8F%3C%2Fspan%3E%3C%2Ftd%3E%3Ctd%20colspan%3D%5C%221%5C%22%20rowspan%3D%5C%221%5C%22%20style%3D%5C%22vertical-align%3A%20middle%3B%5C%22%3E%3Cspan%20style%3D%5C%22color%3A%20rgb(62%2C%2062%2C%2062)%3B%5C%22%20class%3D%5C%22lake-fontsize-1515%5C%22%3E%E5%AD%98%E5%82%A8%E9%80%9F%E5%BA%A6%3C%2Fspan%3E%3C%2Ftd%3E%3Ctd%20colspan%3D%5C%221%5C%22%20rowspan%3D%5C%221%5C%22%20style%3D%5C%22vertical-align%3A%20middle%3B%5C%22%3E%3Cspan%20style%3D%5C%22color%3A%20rgb(62%2C%2062%2C%2062)%3B%5C%22%20class%3D%5C%22lake-fontsize-1515%5C%22%3E%E5%AD%98%E5%82%A8%E7%A9%BA%E9%97%B4%3C%2Fspan%3E%3C%2Ftd%3E%3Ctd%20colspan%3D%5C%221%5C%22%20rowspan%3D%5C%221%5C%22%20style%3D%5C%22vertical-align%3A%20middle%3B%5C%22%3E%3Cspan%20style%3D%5C%22color%3A%20rgb(62%2C%2062%2C%2062)%3B%5C%22%20class%3D%5C%22lake-fontsize-1515%5C%22%3E%E9%80%82%E7%94%A8%E5%9C%BA%E6%99%AF%3C%2Fspan%3E%3C%2Ftd%3E%3C%2Ftr%3E%3Ctr%20style%3D%5C%22height%3A%2064px%3B%5C%22%3E%3Ctd%20colspan%3D%5C%221%5C%22%20rowspan%3D%5C%221%5C%22%20style%3D%5C%22vertical-align%3A%20middle%3B%5C%22%3E%3Cspan%20style%3D%5C%22color%3A%20rgb(62%2C%2062%2C%2062)%3B%5C%22%20class%3D%5C%22lake-fontsize-1515%5C%22%3ElocalStorage%3C%2Fspan%3E%3C%2Ftd%3E%3Ctd%20colspan%3D%5C%221%5C%22%20rowspan%3D%5C%221%5C%22%20style%3D%5C%22vertical-align%3A%20middle%3B%5C%22%3E%3Cspan%20style%3D%5C%22color%3A%20rgb(62%2C%2062%2C%2062)%3B%5C%22%20class%3D%5C%22lake-fontsize-1515%5C%22%3E%E8%BE%83%E5%BF%AB%3C%2Fspan%3E%3C%2Ftd%3E%3Ctd%20colspan%3D%5C%221%5C%22%20rowspan%3D%5C%221%5C%22%20style%3D%5C%22vertical-align%3A%20middle%3B%5C%22%3E%3Cspan%20style%3D%5C%22color%3A%20rgb(62%2C%2062%2C%2062)%3B%5C%22%20class%3D%5C%22lake-fontsize-1515%5C%22%3E%E7%BA%A6%26nbsp%3B5MB%3C%2Fspan%3E%3C%2Ftd%3E%3Ctd%20colspan%3D%5C%221%5C%22%20rowspan%3D%5C%221%5C%22%20style%3D%5C%22vertical-align%3A%20middle%3B%5C%22%3E%3Cspan%20style%3D%5C%22color%3A%20rgb(62%2C%2062%2C%2062)%3B%5C%22%20class%3D%5C%22lake-fontsize-1515%5C%22%3E%E5%AD%98%E5%82%A8%E7%AE%80%E5%8D%95%E6%95%B0%E6%8D%AE%E6%88%96%E9%9C%80%E8%A6%81%E5%BF%AB%E9%80%9F%E8%AF%BB%E5%86%99%3C%2Fspan%3E%3C%2Ftd%3E%3C%2Ftr%3E%3Ctr%20style%3D%5C%22height%3A%2088px%3B%5C%22%3E%3Ctd%20colspan%3D%5C%221%5C%22%20rowspan%3D%5C%221%5C%22%20style%3D%5C%22vertical-align%3A%20middle%3B%5C%22%3E%3Cspan%20style%3D%5C%22color%3A%20rgb(62%2C%2062%2C%2062)%3B%5C%22%20class%3D%5C%22lake-fontsize-1515%5C%22%3EindexDB%3C%2Fspan%3E%3C%2Ftd%3E%3Ctd%20colspan%3D%5C%221%5C%22%20rowspan%3D%5C%221%5C%22%20style%3D%5C%22vertical-align%3A%20middle%3B%5C%22%3E%3Cspan%20style%3D%5C%22color%3A%20rgb(62%2C%2062%2C%2062)%3B%5C%22%20class%3D%5C%22lake-fontsize-1515%5C%22%3E%E8%BE%83%E6%85%A2%3C%2Fspan%3E%3C%2Ftd%3E%3Ctd%20colspan%3D%5C%221%5C%22%20rowspan%3D%5C%221%5C%22%20style%3D%5C%22vertical-align%3A%20middle%3B%5C%22%3E%3Cspan%20style%3D%5C%22color%3A%20rgb(62%2C%2062%2C%2062)%3B%5C%22%20class%3D%5C%22lake-fontsize-1515%5C%22%3E250MB%26nbsp%3B%E4%BB%A5%E4%B8%8A%3C%2Fspan%3E%3C%2Ftd%3E%3Ctd%20colspan%3D%5C%221%5C%22%20rowspan%3D%5C%221%5C%22%20style%3D%5C%22vertical-align%3A%20middle%3B%5C%22%3E%3Cspan%20style%3D%5C%22color%3A%20rgb(62%2C%2062%2C%2062)%3B%5C%22%20class%3D%5C%22lake-fontsize-1515%5C%22%3E%E9%9C%80%E8%A6%81%E5%AD%98%E5%82%A8%E5%A4%A7%E9%87%8F%E7%BB%93%E6%9E%84%E5%8C%96%E6%95%B0%E6%8D%AE%E6%88%96%E9%9C%80%E8%A6%81%E8%BF%9B%E8%A1%8C%E5%A4%8D%E6%9D%82%E6%95%B0%E6%8D%AE%E6%93%8D%E4%BD%9C%3C%2Fspan%3E%3C%2Ftd%3E%3C%2Ftr%3E%3Ctr%20style%3D%5C%22height%3A%2033px%3B%5C%22%3E%3Ctd%20colspan%3D%5C%221%5C%22%20rowspan%3D%5C%221%5C%22%20style%3D%5C%22vertical-align%3A%20middle%3B%5C%22%3E%3Cspan%20style%3D%5C%22color%3A%20rgb(62%2C%2062%2C%2062)%3B%5C%22%20class%3D%5C%22lake-fontsize-1515%5C%22%3E%E7%A3%81%E7%9B%98%3C%2Fspan%3E%3C%2Ftd%3E%3Ctd%20colspan%3D%5C%221%5C%22%20rowspan%3D%5C%221%5C%22%20style%3D%5C%22vertical-align%3A%20middle%3B%5C%22%3E%3Cspan%20style%3D%5C%22color%3A%20rgb(62%2C%2062%2C%2062)%3B%5C%22%20class%3D%5C%22lake-fontsize-1515%5C%22%3E%E8%BE%83%E6%85%A2%3C%2Fspan%3E%3C%2Ftd%3E%3Ctd%20colspan%3D%5C%221%5C%22%20rowspan%3D%5C%221%5C%22%20style%3D%5C%22vertical-align%3A%20middle%3B%5C%22%3E%3Cspan%20style%3D%5C%22color%3A%20rgb(62%2C%2062%2C%2062)%3B%5C%22%20class%3D%5C%22lake-fontsize-1515%5C%22%3E%E5%A4%A7%EF%BC%88%E5%8F%96%E5%86%B3%E4%BA%8E%E7%94%A8%E6%88%B7%E7%A3%81%E7%9B%98%EF%BC%89%3C%2Fspan%3E%3C%2Ftd%3E%3Ctd%20colspan%3D%5C%221%5C%22%20rowspan%3D%5C%221%5C%22%20style%3D%5C%22vertical-align%3A%20middle%3B%5C%22%3E%3Cspan%20style%3D%5C%22color%3A%20rgb(62%2C%2062%2C%2062)%3B%5C%22%20class%3D%5C%22lake-fontsize-1515%5C%22%3E%E7%AB%AF%E5%86%85%E6%94%AF%E6%8C%81%E8%B0%83%E7%94%A8%26nbsp%3Bjsapi%26nbsp%3B%E5%9C%BA%E6%99%AF%3C%2Fspan%3E%3C%2Ftd%3E%3C%2Ftr%3E%3Ctr%20style%3D%5C%22height%3A%2033px%3B%5C%22%3E%3Ctd%20colspan%3D%5C%221%5C%22%20rowspan%3D%5C%221%5C%22%20style%3D%5C%22vertical-align%3A%20middle%3B%5C%22%3E%3Cspan%20style%3D%5C%22color%3A%20rgb(62%2C%2062%2C%2062)%3B%5C%22%20class%3D%5C%22lake-fontsize-1515%5C%22%3EServiceWorker%3C%2Fspan%3E%3C%2Ftd%3E%3Ctd%20colspan%3D%5C%221%5C%22%20rowspan%3D%5C%221%5C%22%20style%3D%5C%22vertical-align%3A%20middle%3B%5C%22%3E%3Cbr%3E%3C%2Ftd%3E%3Ctd%20colspan%3D%5C%221%5C%22%20rowspan%3D%5C%221%5C%22%20style%3D%5C%22vertical-align%3A%20middle%3B%5C%22%3E%3Cbr%3E%3C%2Ftd%3E%3Ctd%20colspan%3D%5C%221%5C%22%20rowspan%3D%5C%221%5C%22%20style%3D%5C%22vertical-align%3A%20middle%3B%5C%22%3E%3Cspan%20style%3D%5C%22color%3A%20rgb(62%2C%2062%2C%2062)%3B%5C%22%20class%3D%5C%22lake-fontsize-1515%5C%22%3E%E5%85%BC%E5%AE%B9%E6%80%A7%E9%97%AE%E9%A2%98%EF%BC%9F%3C%2Fspan%3E%3C%2Ftd%3E%3C%2Ftr%3E%3C%2Ftbody%3E%3C%2Ftable%3E%22%2C%22rows%22%3A5%2C%22cols%22%3A4%2C%22id%22%3A%22MHPkL%22%7D"></div><div style="text-align: justify;"><strong><span class="lake-fontsize-1515" style="color: #3E3E3E;"><br /></span></strong></div><h3 id="DfKXW" style="text-align: justify;"><strong><span class="lake-fontsize-1515" style="color: #3E3E3E;">Step2 展示快照</span></strong></h3><h4 id="q8PLK" style="text-align: justify;"><span class="lake-fontsize-1515" style="color: #3E3E3E;">什么时候展示快照?</span></h4><div><span class="lake-fontsize-1515" style="color: #3E3E3E;">在 HTML 中尽早展示快照逻辑。SDK 中会将展示逻辑插入到<body>内 dom 节点之后,建议接入快照后,把 html 内容中快照逻辑前的逻辑(加载脚本等)后置。</span></div><h3 id="2cvPB" style="text-align: justify;"><strong><span class="lake-fontsize-1515" style="color: #3E3E3E;">Step3 移除快照</span></strong></h3><h4 id="mn7uA" style="text-align: justify;"><span class="lake-fontsize-1515" style="color: #3E3E3E;">什么时候隐藏快照?</span></h4><div><span class="lake-fontsize-1515" style="color: #3E3E3E;"> 在页面的真实 DOM 渲染完成时,实现快照和真实页面的无缝衔接</span></div><h3 id="7i8lH" style="text-align: justify;"><strong><span class="lake-fontsize-1515" style="color: #3E3E3E;">快照 SDK 效果</span></strong></h3><div><span class="lake-fontsize-1515" style="color: #3E3E3E;">自动完成快照三个功能的注入</span></div><div><span class="lake-fontsize-1515" style="color: #3E3E3E;"><br /></span></div><div data-card-type="block" data-ready-card="codeblock" data-card-value="data:%7B%22mode%22%3A%22plain%22%2C%22code%22%3A%22%3C!doctype%20html%3E%5Cn%3Chtml%3E%5Cn%20%20%20%20%3Chead%3E%5Cn%20%20%20%20%20%20%20%20%3Cscript%3E%5Cn%20%20%20%20%20%20%20%20%20%20%20%20%2F%2F%20%E5%A2%9E%E5%8A%A0%E5%8F%82%E6%95%B0%E4%BC%A0%E9%80%92%E9%83%A8%E5%88%86%5Cn%20%20%20%20%20%20%20%20%20%20%20%20window.__DD_SNAPSHOT_CONFIG__%20%3D%20%7B%5Cn%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%5C%22rootId%5C%22%3A%20%5C%22Root%5C%22%2C%5Cn%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%5C%22debug%5C%22%3A%20true%2C%5Cn%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%5C%22useLocalStorage%5C%22%3A%20true%5Cn%20%20%20%20%20%20%20%20%20%20%20%20%7D%5Cn%3C%2Fscript%3E%5Cn%20%20%20%20%20%20%20%20%3C!--%20%3Cstyle%3E%E4%BD%A0%E7%9A%84%E9%A1%B9%E7%9B%AEcss%EF%BC%8C%E9%9C%80%E8%A6%81%E8%87%AA%E8%A1%8C%E4%BF%AE%E6%94%B9webpack%E9%85%8D%E7%BD%AE%3C%2Fstyle%3E%20--%3E%5Cn%20%20%20%20%20%20%20%20%3C!--%20%E6%96%87%E4%BB%B6%E5%86%85%E5%85%B6%E4%BB%96%E5%8E%9F%E6%9C%89%E5%86%85%E5%AE%B9%20--%3E%5Cn%20%20%20%20%20%20%20%20%3C%2Fscript%3E%5Cn%20%20%20%20%3C%2Fhead%3E%5Cn%20%20%20%20%3Cbody%20class%3D%5C%22bg-common%5C%22%20data-spm%3D%5C%2228107366%5C%22%3E%5Cn%20%20%20%20%20%20%20%20%3C!--%20%E5%BF%AB%E7%85%A7%E6%8C%82%E8%BD%BD%E7%9A%84dom%E8%8A%82%E7%82%B9%20--%3E%5Cn%20%20%20%20%20%20%20%20%3Cdiv%20id%3D%5C%22html-snapshot%5C%22%20style%3D%5C%22position%3A%20absolute%3B%20left%3A%200%3B%20right%3A%200%3B%20top%3A%200%3B%20z-index%3A%209999999%3B%5C%22%3E%3C%2Fdiv%3E%5Cn%20%20%20%20%20%20%20%20%3Cscript%3E%5Cn%20%20%20%20%20%20%20%20%20%20%20%20%2F%2F%20%E5%BF%AB%E7%85%A7%E5%B1%95%E7%A4%BA%E9%80%BB%E8%BE%91%5Cn%20%20%20%20%20%20%20%20%20%20%20%20!async%20function()%20%7B%5Cn%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20const%20n%20%3D%20function()%20%7B%5Cn%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20const%20n%20%3D%20new%20URL(window.location.href)%5Cn%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%2C%20%7Bpathname%3A%20t%2C%20search%3A%20e%2C%20hash%3A%20o%7D%20%3D%20n%3B%5Cn%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20let%20a%20%3D%20e%3B%5Cn%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20e.startsWith(%5C%22%3F%5C%22)%20%26%26%20(a%20%3D%20e.substring(1))%3B%5Cn%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20const%20r%20%3D%20new%20URLSearchParams(a)%5Cn%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%2C%20s%20%3D%20%5B%5D%3B%5Cn%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20for%20(const%20%5Bn%2Ct%5D%20of%20r)%5Cn%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20!%5B%5C%22dd_mini_app_id%5C%22%2C%20%5C%22pc_slide%5C%22%2C%20%5C%22dd_darkmode%5C%22%2C%20%5C%22dd_progress%5C%22%2C%20%5C%22dtaction%5C%22%5D.includes(n)%20%26%26%20s.push(t)%3B%5Cn%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20const%20i%20%3D%20%5Bt.replace(%2F%5C%5C.html%24%2F%2C%20%5C%22%5C%22)%2C%20s.join(%5C%22_%5C%22)%2C%20o%5D.filter((n%3D%3En)).join(%5C%22-%5C%22).replace(%2F%5B%5Ea-zA-Z0-9-%5D%2Fg%2C%20%5C%22_%5C%22)%3B%5Cn%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20return%20window.__ddSnapshotKey%20%3D%20%60ddSnapshotKey_%24%7Bi%7D%60%2C%5Cn%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20window.__ddSnapshotKey%5Cn%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%7D()%5Cn%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%2C%20t%20%3D%20await%20async%20function(n)%20%7B%5Cn%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20let%20t%3B%5Cn%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20return%20t%20%3D%20window.__DD_SNAPSHOT_CONFIG__%20%26%26%20window.__DD_SNAPSHOT_CONFIG__.useLocalStorage%20%3F%20localStorage.getItem(n)%20%3A%20await%20async%20function(n)%20%7B%5Cn%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20try%20%7B%5Cn%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20const%20t%20%3D%20await%20function(n%2C%20t%2C%20e)%20%7B%5Cn%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20return%20new%20Promise(((n%2Co)%3D%3E%7B%5Cn%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20const%20a%20%3D%20indexedDB.open(%5C%22SnapshotDatabase8%5C%22)%3B%5Cn%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20a.onerror%20%3D%20n%3D%3E%7B%5Cn%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20o(%5C%22%E6%95%B0%E6%8D%AE%E5%BA%93%E6%89%93%E5%BC%80%E5%A4%B1%E8%B4%A5%5C%22)%5Cn%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%7D%5Cn%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%2C%5Cn%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20a.onsuccess%20%3D%20a%3D%3E%7B%5Cn%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20const%20r%20%3D%20a.target.result.transaction(t%2C%20%5C%22readonly%5C%22).objectStore(t).get(e)%3B%5Cn%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20r.onerror%20%3D%20n%3D%3E%7B%5Cn%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20o(%5C%22%E8%8E%B7%E5%8F%96%E6%95%B0%E6%8D%AE%E5%A4%B1%E8%B4%A5%5C%22)%5Cn%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%7D%5Cn%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%2C%5Cn%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20r.onsuccess%20%3D%20t%3D%3E%7B%5Cn%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20n(t.target.result)%5Cn%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%7D%5Cn%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%7D%5Cn%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%7D%5Cn%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20))%5Cn%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%7D(0%2C%20%5C%22snapshot%5C%22%2C%20n)%3B%5Cn%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20return%20t%5Cn%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%7D%20catch%20(n)%20%7B%5Cn%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20console.error(%5C%22%5Bsnapshot%5D%3A%20Error%20while%20loading%20snapshot%3A%5C%22%2C%20n)%5Cn%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%7D%5Cn%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%7D(n)%2C%5Cn%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20t%20%7C%7C%20%5C%22%5C%22%5Cn%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%7D(n)%5Cn%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%2C%20e%20%3D%20document.getElementById(%5C%22html-snapshot%5C%22)%3B%5Cn%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20if%20(e%20%26%26%20t)%20%7B%5Cn%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20window.__useLocalSnapshotHtmlDD%20%3D%20!0%3B%5Cn%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20var%20o%20%3D%20(new%20DOMParser).parseFromString(t%2C%20%5C%22text%2Fhtml%5C%22).body%3B%5Cn%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20o.firstChild%20%26%26%20e.appendChild(o.firstChild)%2C%5Cn%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20window.performance%20%26%26%20window.performance.mark%20%26%26%20window.performance.mark(%5C%22fmp_snapshot%5C%22)%2C%5Cn%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20window.addEventListener(%5C%22load%5C%22%2C%20(function()%20%7B%5Cn%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20setTimeout((function()%20%7B%5Cn%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20var%20n%20%3D%20document.getElementById(%5C%22html-snapshot%5C%22)%3B%5Cn%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20n%20%26%26%20(n.style.display%20%3D%20%5C%22none%5C%22)%5Cn%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%7D%5Cn%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20)%2C%201e4)%5Cn%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%7D%5Cn%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20))%5Cn%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%7D%5Cn%20%20%20%20%20%20%20%20%20%20%20%20%7D()%3B%5Cn%3C%2Fscript%3E%5Cn%20%20%20%20%20%20%20%20%3C!--%20%E4%BF%9D%E5%AD%98%E5%BF%AB%E7%85%A7%E5%92%8C%E7%A7%BB%E9%99%A4%E5%BF%AB%E7%85%A7%E5%A4%96%E9%93%BE%E8%84%9A%E6%9C%AC%20--%3E%5Cn%20%20%20%20%20%20%20%20%3Cscript%20src%3D%5C%22https%3A%2F%2Fdev.g.alicdn.com%2Fcode%2Fnpm%2F%40ali%2Fsnapshot-dd-webpack-plugin%2F1.0.0%2FdebugSnapshot.js%3Ft%3D1706019210161%5C%22%3E%3C%2Fscript%3E%5Cn%20%20%20%20%20%20%20%20%3Cdiv%20id%3D%5C%22Root%5C%22%3E%3C%2Fdiv%3E%5Cn%20%20%20%20%20%20%20%20%3C!--%20html%E5%86%85%E5%85%B6%E4%BB%96%E5%8E%9F%E6%9C%89%E5%86%85%E5%AE%B9%20--%3E%5Cn%20%20%20%20%3C%2Fbody%3E%5Cn%3C%2Fhtml%3E%22%2C%22heightLimit%22%3Atrue%2C%22margin%22%3Atrue%2C%22id%22%3A%22tf8rT%22%7D"></div><h3 id="8Yokl">2  适用场景</h3><h4 id="z9BDJ" style="text-align: justify;"><span class="lake-fontsize-1515" style="color: #FF6827;">快照方案适用的场景有哪些?</span></h4><div style="text-align: justify;"><span class="lake-fontsize-1515" style="color: #FF6827;"><span data-card-type="inline" data-ready-card="image" data-card-value="data:%7B%22src%22%3A%22https%3A%2F%2Fucc.alicdn.com%2Fpic%2Fdeveloper-ecology%2F6ibaby6qg4ku4_6dd99d72f065402fa5d4fd7f21e5fc47.png%22%2C%22originWidth%22%3A835%2C%22originHeight%22%3A292%2C%22name%22%3A%22image.png%22%2C%22size%22%3A216183%2C%22display%22%3A%22inline%22%2C%22align%22%3A%22left%22%2C%22linkTarget%22%3A%22_blank%22%2C%22status%22%3A%22done%22%2C%22style%22%3A%22none%22%2C%22search%22%3A%22%22%2C%22margin%22%3A%7B%22top%22%3Atrue%2C%22bottom%22%3Atrue%7D%2C%22width%22%3A518%2C%22height%22%3A181%7D"></span></span></div><h3 id="W8ocL">3  接入时机</h3><h4 id="9Enw1" style="text-align: justify;"><span class="lake-fontsize-1515" style="color: #FF6827;">快照方案接入的阶段?</span></h4><div><span class="lake-fontsize-1515" style="color: #3E3E3E;">适合在性能优化的后期阶段,此时已经完成了大量基本性能提升措施:诸如减小资源包体积、优化渲染流程、完善数据接口效率、加强缓存机制以及接入离线包技术等策略之后,适合考虑引入快照技术。</span></div><div><span class="lake-fontsize-1515" style="color: #3E3E3E;"><br /></span></div><h3 id="XgxXE">4  准确性&稳定性保障</h3><h4 id="hQoSR" style="text-align: justify;"><span class="lake-fontsize-1515" style="color: #FF6827;">如何保障快照的准确性?</span></h4><div><span class="lake-fontsize-1515" style="color: #3E3E3E;">通过以下措施保障快照内容的<strong>可控性</strong>和<strong>稳定性</strong>:</span><span class="lake-fontsize-1515" style="color: #3E3E3E;">1、选取页面中多次刷新页面展示不变的部分作为快照内容;</span><span class="lake-fontsize-1515" style="color: #3E3E3E;">2、对快照做微调处理:挖空、替换<strong>可能发生改变</strong>、产生闪烁的元素/模块,例如 PC 工作台将不可预测的插件内容进行了骨架模版的替换;</span></div><h4 id="3XPdy" style="text-align: justify;"><span class="lake-fontsize-1515" style="color: #FF6827;">接入快照是否会对业务性能有影响?</span></h4><ul style="text-align: justify;"><li><span class="lake-fontsize-1515" style="color: #3E3E3E;">根据快照逻辑测试数据,预计耗费时间不超过 20ms(参考文档 SSG 方案 5ms、工作台测试 demo18ms);</span></li><li><span class="lake-fontsize-1515" style="color: #3E3E3E;">需要将 css 打包到 html 内,假如首屏 css 文件体积很大,建议结合离线包方案使用;</span></li></ul><h4 id="zwbfB" style="text-align: justify;"><span class="lake-fontsize-1515" style="color: #FF6827;">异常边界:真实页面加载失败了怎么办?</span></h4><div><span class="lake-fontsize-1515" style="color: #3E3E3E;">对展示快照的时间设置一个展示的兜底时间,如果展示时间达到上限时,首屏仍然没有渲染成功,那么快照将直接隐藏。然后</span><span class="lake-fontsize-1515" style="color: #3E3E3E;">1、展示页面的真实加载失败的情况。</span><span class="lake-fontsize-1515" style="color: #3E3E3E;">2、进一步优化:展示快照部分的 HTML 结构</span></div><h4 id="SaOxH" style="text-align: justify;"><span class="lake-fontsize-1515" style="color: #FF6827;">稳定性保障怎么做?</span></h4><ul style="text-align: justify;"><li><span class="lake-fontsize-1515" style="color: #3E3E3E;">做好四端设备/不同机型/不同系统的测试:不同机型、系统表现可能会有不同。例如工作台的快照在 mac 端低系统版本会有一个上屏时间检测异常的 bug。</span></li><li><span class="lake-fontsize-1515" style="color: #3E3E3E;">分设备能力灰度,灰度观察时间适当放长,灰度过程观察是否符合预期。</span></li></ul><h4 id="OOF6i" style="text-align: justify;"><span class="lake-fontsize-1515" style="color: #FF6827;">如何预防安全风险?</span></h4><ul style="text-align: justify;"><li><span class="lake-fontsize-1515" style="color: #3E3E3E;">存储快照时只存储页面的 dom 结构。</span></li><li><span class="lake-fontsize-1515" style="color: #3E3E3E;">展示快照时,避免用户本地的快照内容被篡改,会过滤 script 标签,仅展示 dom 结构部分。</span></li></ul><h3 id="x4a6S">5  优点和限制</h3><h4 id="kKL3o" style="text-align: justify;"><span class="lake-fontsize-1515" style="color: #FF6827;">快照手段的优点是什么?</span></h4><div><span class="lake-fontsize-1515" style="color: #3E3E3E;">1、 利用用户的本地缓存,无额外服务器成本。</span><span class="lake-fontsize-1515" style="color: #3E3E3E;">2、 快照能很好的保存用户千人千面的信息,相比统一骨架屏,具备适配千人千面的能力。</span><span class="lake-fontsize-1515" style="color: #3E3E3E;">3、 快照手段可以是对 SSR、CSR 或离线包等端侧性能优化手段的补充。</span></div><h4 id="JlW0Y" style="text-align: justify;"><span class="lake-fontsize-1515" style="color: #FF6827;">快照的限制是什么?</span></h4><div><span class="lake-fontsize-1515" style="color: #3E3E3E;">快照缓存的生效前提是二次访问,重复访问率较高的页面快照<strong>覆盖率高</strong>,效果会明显一些。</span></div><div><span class="lake-fontsize-1515" style="color: #3E3E3E;"><br /></span></div><h2 id="Unrys">下一步展望</h2><div><br /></div><h3 id="c1RlE">1  效果优化</h3><h4 id="qpQXE"><span class="lake-fontsize-1515" style="color: #FF6827;">覆盖率提升</span></h4><div><span class="lake-fontsize-1515" style="color: #3E3E3E;">目前PC工作台的快照命中率在82%左右,ACTION 是如何提升覆盖率。</span></div><h4 id="apL3x"><span class="lake-fontsize-1515" style="color: #FF6827;">首屏渲染提升</span></h4><div><span class="lake-fontsize-1515" style="color: #3E3E3E;">快照内容优化:保存快照时计算组件高度,仅保存&展示首屏内容,加速快照真实上屏时间。</span></div><div><br /></div><h3 id="X4H35">2  方案优化</h3><div><span class="lake-fontsize-1515" style="color: #3E3E3E;">为了进一步提高页面加载速度和用户体验,我们可以对基于快照的展示方案进行以下优化:</span></div><ul><li><span class="lake-fontsize-1515" style="color: #3E3E3E;">快照作为页面框架: 我们将快照HTML作为基础框架存储在客户端本地。在页面加载时,这一框架被迅速从本地存储中取出并渲染,为用户提供初步的页面结构。</span></li><li><span class="lake-fontsize-1515" style="color: #3E3E3E;">数据注入优化: 利用本地缓存的首页schema数据,我们可以对快照框架进行必要的调整和数据填充。这个过程中,难点在于确保业务改造的成本与复杂度之间的平衡。</span></li><li><span class="lake-fontsize-1515" style="color: #3E3E3E;">渐进式增量渲染: 在JavaScript资源加载并执行后,我们继续“注水”,即将剩余的数据和内容注入到快照框架中,完成页面的最终渲染。</span></li></ul><div><span class="lake-fontsize-1515" style="color: #3E3E3E;"><br /></span></div><div data-card-type="block" data-ready-card="table" data-card-value="data:%7B%22rows%22%3A1%2C%22cols%22%3A1%2C%22html%22%3A%22%3Ctable%20class%3D%5C%22lake-table%5C%22%20style%3D%5C%22width%3A%20677px%3B%5C%22%3E%3Ccolgroup%3E%3Ccol%20width%3D%5C%22677%5C%22%20span%3D%5C%221%5C%22%20%2F%3E%3C%2Fcolgroup%3E%3Ctbody%3E%3Ctr%20style%3D%5C%22height%3A%2033px%3B%5C%22%3E%3Ctd%20colspan%3D%5C%221%5C%22%20rowspan%3D%5C%221%5C%22%20style%3D%5C%22background-color%3A%20%23FFF8B9%3B%20vertical-align%3A%20top%3B%5C%22%3E%3Cspan%20class%3D%5C%22lake-fontsize-1515%5C%22%20style%3D%5C%22color%3A%20%233E3E3E%3B%5C%22%3E%E4%B8%8E%E5%8E%9F%E6%9C%89%E6%96%B9%E6%A1%88%E7%9B%B8%E6%AF%94%EF%BC%8C%E6%AD%A4%E4%BC%98%E5%8C%96%E7%AD%96%E7%95%A5%E7%9A%84%E5%85%B3%E9%94%AE%E5%9C%A8%E4%BA%8E%EF%BC%8C%E4%BB%8E%E6%9C%AC%E5%9C%B0%E5%AD%98%E5%82%A8%E4%B8%AD%E6%8F%90%E5%8F%96%E7%9A%84%E5%BF%AB%E7%85%A7%E4%B8%8D%E5%8F%AA%E6%98%AF%E4%BD%9C%E4%B8%BA%E4%B8%B4%E6%97%B6%E8%A6%86%E7%9B%96%E5%B1%82%E6%9D%A5%E5%8A%A0%E9%80%9F%E9%A1%B5%E9%9D%A2%E5%B1%95%E7%A4%BA%EF%BC%8C%E8%80%8C%E6%98%AF%E4%BD%9C%E4%B8%BA%E5%AE%9E%E9%99%85%E9%A1%B5%E9%9D%A2%E7%9A%84%E8%B5%B7%E5%A7%8B%E7%82%B9%EF%BC%8C%E9%9A%8F%E5%90%8E%E9%80%9A%E8%BF%87%E5%A2%9E%E9%87%8F%E6%9B%B4%E6%96%B0%E5%AE%9E%E7%8E%B0%E5%AE%8C%E6%95%B4%E5%90%8C%E6%9E%84%E6%B8%B2%E6%9F%93%E3%80%82%3C%2Fspan%3E%3C%2Ftd%3E%3C%2Ftr%3E%3C%2Ftbody%3E%3C%2Ftable%3E%22%2C%22margin%22%3Afalse%2C%22hideBorder%22%3Afalse%2C%22id%22%3A%22iDMGH%22%7D"></div><div data-card-type="block" data-ready-card="table" data-card-value="data:%7B%22rows%22%3A1%2C%22cols%22%3A1%2C%22html%22%3A%22%3Ctable%20class%3D%5C%22lake-table%5C%22%20style%3D%5C%22width%3A%20677px%3B%5C%22%3E%3Ccolgroup%3E%3Ccol%20width%3D%5C%22677%5C%22%20span%3D%5C%221%5C%22%20%2F%3E%3C%2Fcolgroup%3E%3Ctbody%3E%3Ctr%20style%3D%5C%22height%3A%2033px%3B%5C%22%3E%3Ctd%20colspan%3D%5C%221%5C%22%20rowspan%3D%5C%221%5C%22%20style%3D%5C%22background-color%3A%20%23E8F2FE%3B%20vertical-align%3A%20top%3B%5C%22%3E%3Cp%20data-lake-id%3D%5C%22713dde0cd3dccc1e2168f4591955d5ee%5C%22%3E%3Cspan%20class%3D%5C%22lake-fontsize-1515%5C%22%20style%3D%5C%22color%3A%20%233E3E3E%3B%5C%22%3E%E6%AD%A4%E6%96%B9%E6%B3%95%E4%B8%8E%E6%9C%8D%E5%8A%A1%E5%99%A8%E7%AB%AF%E7%94%9F%E6%88%90%E9%9D%99%E6%80%81%E9%A1%B5%E9%9D%A2(SSG)%E7%9A%84%E5%B7%AE%E5%BC%82%E5%9C%A8%E4%BA%8E%EF%BC%8CHTML%E6%A8%A1%E6%9D%BF%E7%9A%84%E7%94%9F%E6%88%90%E8%BD%AC%E7%A7%BB%E5%88%B0%E4%BA%86%E5%AE%A2%E6%88%B7%E7%AB%AF%EF%BC%8C%E8%80%8C%E4%B8%8D%E6%98%AF%E5%9C%A8%E6%9C%8D%E5%8A%A1%E5%99%A8%E7%AB%AF%E5%A4%84%E7%90%86%E3%80%82%E8%BF%99%E7%A7%8D%E5%81%9A%E6%B3%95%E6%9C%89%E6%95%88%E5%9C%B0%E5%88%A9%E7%94%A8%E4%BA%86%E5%AE%A2%E6%88%B7%E7%AB%AF%E7%9A%84%E6%9C%AC%E5%9C%B0%E7%BC%93%E5%AD%98%E8%83%BD%E5%8A%9B%EF%BC%8C%E4%B8%8D%E4%BB%85%E5%87%8F%E5%B0%91%E4%BA%86%E6%9C%8D%E5%8A%A1%E5%99%A8%E7%9A%84%E8%B4%9F%E8%BD%BD%EF%BC%8C%E8%BF%98%E5%8F%AF%E8%83%BD%E9%99%8D%E4%BD%8E%E6%95%B0%E6%8D%AE%E4%BC%A0%E8%BE%93%E9%87%8F%EF%BC%8C%E4%BB%8E%E8%80%8C%E6%8F%90%E9%AB%98%E4%BA%86%E6%95%B4%E4%BD%93%E7%9A%84%E9%A1%B5%E9%9D%A2%E5%8A%A0%E8%BD%BD%E6%80%A7%E8%83%BD%E3%80%82%3C%2Fspan%3E%3C%2Fp%3E%3Cp%20data-lake-id%3D%5C%227e06dc08daa7b69121b56c3d48c745a8%5C%22%3E%3Ccard%20type%3D%5C%22inline%5C%22%20name%3D%5C%22image%5C%22%20value%3D%5C%22data%3A%257B%2522src%2522%253A%2522https%253A%252F%252Fucc.alicdn.com%252Fpic%252Fdeveloper-ecology%252F6ibaby6qg4ku4_59315506a1094d599f5abfe392e93baf.png%2522%252C%2522originWidth%2522%253A813%252C%2522originHeight%2522%253A266%252C%2522name%2522%253A%2522image.png%2522%252C%2522size%2522%253A188111%252C%2522display%2522%253A%2522inline%2522%252C%2522align%2522%253A%2522left%2522%252C%2522linkTarget%2522%253A%2522_blank%2522%252C%2522status%2522%253A%2522done%2522%252C%2522style%2522%253A%2522none%2522%252C%2522search%2522%253A%2522%2522%252C%2522margin%2522%253A%257B%2522top%2522%253Atrue%252C%2522bottom%2522%253Atrue%257D%252C%2522width%2522%253A406.5%252C%2522height%2522%253A133%257D%5C%22%3E%3C%2Fcard%3E%3Ccard%20type%3D%5C%22inline%5C%22%20name%3D%5C%22image%5C%22%20value%3D%5C%22data%3A%257B%2522src%2522%253A%2522%2522%252C%2522originWidth%2522%253A140%252C%2522originHeight%2522%253A140%252C%2522display%2522%253A%2522inline%2522%252C%2522align%2522%253A%2522left%2522%252C%2522linkTarget%2522%253A%2522_blank%2522%252C%2522status%2522%253A%2522error%2522%252C%2522style%2522%253A%2522none%2522%252C%2522search%2522%253A%2522%2522%252C%2522margin%2522%253A%257B%2522top%2522%253Afalse%252C%2522bottom%2522%253Afalse%257D%252C%2522width%2522%253A140%252C%2522height%2522%253A140%252C%2522message%2522%253A%2522%25E5%259B%25BE%25E7%2589%2587%25E4%25B8%258D%25E6%2594%25AF%25E6%258C%2581%25E6%258B%25B7%25E8%25B4%259D%25E5%25A4%258D%25E5%2588%25B6%25EF%25BC%258C%25E8%25AF%25B7%25E5%258D%2595%25E7%258B%25AC%25E5%25A4%258D%25E5%2588%25B6%25E4%25B8%258A%25E4%25BC%25A0%2522%252C%2522size%2522%253A0%257D%5C%22%3E%3C%2Fcard%3E%3C%2Fp%3E%3C%2Ftd%3E%3C%2Ftr%3E%3C%2Ftbody%3E%3C%2Ftable%3E%22%2C%22margin%22%3Afalse%2C%22hideBorder%22%3Afalse%2C%22id%22%3A%22RUmCA%22%7D"></div><h2 id="2lVYx"><span class="lake-fontsize-1515" style="color: #FF6827;">方案对比</span></h2><div><span class="lake-fontsize-1515" style="color: #FF6827;"><span data-card-type="inline" data-ready-card="image" data-card-value="data:%7B%22src%22%3A%22https%3A%2F%2Fucc.alicdn.com%2Fpic%2Fdeveloper-ecology%2F6ibaby6qg4ku4_16f6d012136346aabf1611773262de31.png%22%2C%22originWidth%22%3A828%2C%22originHeight%22%3A366%2C%22name%22%3A%22image.png%22%2C%22size%22%3A261941%2C%22display%22%3A%22inline%22%2C%22align%22%3A%22left%22%2C%22linkTarget%22%3A%22_blank%22%2C%22status%22%3A%22done%22%2C%22style%22%3A%22none%22%2C%22search%22%3A%22%22%2C%22margin%22%3A%7B%22top%22%3Atrue%2C%22bottom%22%3Atrue%7D%2C%22width%22%3A627%2C%22height%22%3A277%7D"></span></span></div><div>
  4. 来源|阿里云开发者公众号 </div><div>
  5. 作者|星迎</div><div style="text-align: center;"><br /></div><div><br /></div><div><br /></div>
作者介绍
目录