<?xml version="1.0" encoding="utf-8"?>
<rss version="2.0" xmlns:dc="http://purl.org/dc/elements/1.1/" xmlns:content="http://purl.org/rss/1.0/modules/content/">
    <channel>
        <title>kkdev163 Blog</title>
        <link>https://kkdev163.github.io/presentation</link>
        <description>kkdev163 Blog</description>
        <lastBuildDate>Thu, 09 Jun 2022 00:00:00 GMT</lastBuildDate>
        <docs>https://validator.w3.org/feed/docs/rss2.html</docs>
        <generator>https://github.com/jpmonette/feed</generator>
        <language>zh-Hans</language>
        <item>
            <title><![CDATA[Febase 的工程设计与实践]]></title>
            <link>https://kkdev163.github.io/presentation/febase</link>
            <guid>https://kkdev163.github.io/presentation/febase</guid>
            <pubDate>Thu, 09 Jun 2022 00:00:00 GMT</pubDate>
            <description><![CDATA[演讲 PPT: Febase 的工程设计与实践]]></description>
            <content:encoded><![CDATA[<p>演讲 PPT: <a href="https://docs.google.com/presentation/d/1T64cQOkNFf6Ti-NZmKmY4kTTk9oSAmduNGlwnx1bkJk/edit#slide=id.g24aa805472e_1_70" target="_blank" rel="noopener noreferrer">Febase 的工程设计与实践</a></p><p>演讲场所: 网易-组内分享</p><p>演讲简介: 本次演讲介绍网易云音乐一站式研发平台 Febase 的工程设计与实现。</p><p>演讲时间: 2022-06-09</p><div class="theme-admonition theme-admonition-info alert alert--info admonition_LlT9"><div class="admonitionHeading_tbUL"><span class="admonitionIcon_kALy"><svg viewBox="0 0 14 16"><path fill-rule="evenodd" d="M7 2.3c3.14 0 5.7 2.56 5.7 5.7s-2.56 5.7-5.7 5.7A5.71 5.71 0 0 1 1.3 8c0-3.14 2.56-5.7 5.7-5.7zM7 1C3.14 1 0 4.14 0 8s3.14 7 7 7 7-3.14 7-7-3.14-7-7-7zm1 3H6v5h2V4zm0 6H6v2h2v-2z"></path></svg></span>信息</div><div class="admonitionContent_S0QG"><p>以下为演讲逐字稿</p></div></div><h1>演讲逐字稿</h1><h2 class="anchor anchorWithStickyNavbar_LWe7" id="0-开场词">0. 开场词<a href="#0-开场词" class="hash-link" aria-label="0. 开场词的直接链接" title="0. 开场词的直接链接">​</a></h2><p>大家好，我是 kkdev163，今天为大家带来的分享主题是《Febase 的工程设计与实现》。</p><p>大家在日常的开发过程中，或多或少都有使用过 Febase。那你是否对平台的技术实现有过好奇呢？</p><p>这次分享，我就为大家揭开 Febase 背后的技术实现，是怎样一个技术架构，驱动了 Febase 平台的 持续迭代 和 扩展。希望能为大家带来一些 启发、思考。</p><h2 class="anchor anchorWithStickyNavbar_LWe7" id="1-大纲">1. 大纲<a href="#1-大纲" class="hash-link" aria-label="1. 大纲的直接链接" title="1. 大纲的直接链接">​</a></h2><p>本次分享会分为 5 个部分..</p><h2 class="anchor anchorWithStickyNavbar_LWe7" id="一引言-febase-产品简介">一.引言 (Febase 产品简介)<a href="#一引言-febase-产品简介" class="hash-link" aria-label="一.引言 (Febase 产品简介)的直接链接" title="一.引言 (Febase 产品简介)的直接链接">​</a></h2><ul><li>Febase 是一站式的研发平台，覆盖了从工程初始化到线上监控的研发生命周期。</li><li>上线部署阶段，Febase 提供了一致性的部署体验，如今在一个平台，我们就可以完成 多应用类型的发布</li><li>Febase 还提供了上线流程管控，帮助研发团队养成规范的代码上线流程。</li><li>Febase 还通过 OpenApi 的方式，向第三方平台暴露 应用模型 与 全部底层能力。</li></ul><h2 class="anchor anchorWithStickyNavbar_LWe7" id="二febase-的多应用类型部署">二.Febase 的多应用类型部署<a href="#二febase-的多应用类型部署" class="hash-link" aria-label="二.Febase 的多应用类型部署的直接链接" title="二.Febase 的多应用类型部署的直接链接">​</a></h2><blockquote><p>由简化到复杂，抽象到具体，从一种 应用类型 到 多种应用类型 的部署</p></blockquote><p>Febase 作为一个研发平台，最核心的功能点是 部署。我们首先来思考下，当我们设计一个部署平台时，最小可行产品(MVP) 是什么？</p><p>我的理解是: 这个产品可以将远程仓库代码 clone 下来，构建出线上产物，最后提供出该产物的可访问链接。</p><p>当从一个 MVP 扩展到一个 生产环境 可用的服务时，整体架构会有哪些不同？</p><p>我以静态部署平台 SDP 为例，展开介绍下:</p><ul><li>SDP 需要提供一个可视化的部署平台，方便开发者配置应用部署的元信息</li><li>如 应用名、Git 仓库、访问路由等</li><li>在开发者触发构建时，管理后台会调用 构建服务，触发构建流水线</li><li>在构建完成后，通知 产物托管服务 下载产物，并存储在 磁盘中。</li><li>在产物托管完成后，管理后台会通知网关 更新路由信息。</li><li>之后用户就可以通过 公网链接，请求到 网关</li><li>最后由 网关 解析路由，读取静态资源</li></ul><p>虽然最小可行产品 MVP 很简洁，但我们看到 静态部署平台 需要分别起了 4 个服务，将静态资源托管这件事做到极致。这套架构支撑起了 云音乐庞大的 静态 Web 业务 和 RN 业务。</p><p>在 Febase 中，我们并没有重复造一套轮子，而是通过将 SDP 的部署能力下沉，提供出静态部署的原子能力。类似的 Alpaca 平台，提供了容器部署原子能力。这些部署能力，成为了我们打造 Febase 多应用类型部署的基础。</p><p>在接入 SDP、Alpaca，甚至未来更多的部署能力时，我们是如何确保整体架构的 可维护性 与 可扩展性的呢？</p><p>首先我们为部署能力，定义了一个统一的适配器抽象， 称其为 DeployProvider。在其上定义了公共的抽象方法。</p><p>在对接具体的应用类型时，适配器需要实现这个接口, 如静态 Web 类型，在实现时，会调用 sdpRpc 方法实现具体发布逻辑。 容器类型，在实现时，会调用 alpacaRpc 方法实现具体的发布逻辑。</p><p>有了这一层抽象和实现后，我们就可以为不同的应用类型部署，提供统一的 Service 和 Controller 接口。在接口内部，我们根据应用类型，选择对应的适配器做发布。</p><p>我再通过一个示意图来复述下:</p><ul><li>在 Node 服务层的后端，抽象出了 DeployProvider 接口</li><li>针对不同的应用类型，实现适配器</li><li>对前端我们可以暴露，统一的部署方法。</li><li>通过这个方式，我们解决了不同应用类型的 部署能力实现差异的问题。</li><li>并且在未来，新增部署类型时，我们只需以插件的方式新增一个 xProvider，就可以引入该部署能力。</li></ul><h2 class="anchor anchorWithStickyNavbar_LWe7" id="三-febase-的流程管控设计">三. Febase 的流程管控设计<a href="#三-febase-的流程管控设计" class="hash-link" aria-label="三. Febase 的流程管控设计的直接链接" title="三. Febase 的流程管控设计的直接链接">​</a></h2><ul><li>在 Febase 中，我们希望能规范应用的上线步骤，规避多人开发场景下的分支冲突。</li><li>同时我们也需要为不同应用类型，提供上线步骤的定制能力。</li></ul><p>为此我们在 Febase 中引入了 发布流程 的概念。</p><p>一个发布分支，需要走完 「开发」、「卡点」、「上线」等阶段的验证才能被合并入主干。</p><p>在引入发布流程时，我们首先要解决的是流程如何管理的问题:</p><ol><li>每个流程需要以一定的方式定义，并被计算机程序读取。</li><li>流程当前所处的节点，需要被持久化存储。</li><li>流程可以以 串行、并行 的方式 向前推进、也可以回退到前置的节点。</li></ol><p>为了完成上述对流程的管理，最朴素的方式是，我们可以选择在业务代码中，手动处理流程状态，但是随着 业务复杂度 的提升，流程状态的管理 与 业务逻辑 相耦合，将会使得项目变得难以维护 和 扩展。</p><p>为了简化对流程的管理。业界沉淀了一套流程状态管理工具称为「流程引擎」。这套工具就好像前端的视图层框架，我们只需要专注于业务逻辑的编写，DOM 的操作更新就交由框架来处理。大大降低前端开发的复杂度，提升项目的可维护性。</p><p>在计划引入「流程引擎」时，我们调研了市面上==的开源产==品，最终选择了自研「流程引擎」，我们的「流程引擎」除了满足常规的流程状态管理外，还有以下几点特色:</p><ol><li>JS 生态，对前端开发者友好。</li><li>提供前端套件，流程状态管理可驱动前端视图变更。</li><li>覆盖常见的编排需求，整体复杂度可控，最适合 Febase。</li></ol><p>我们来看下自研的「流程引擎」 在 Febase 中的具体使用吧。</p><p>在 packages/server/app/workflow/definition 目录中，会定义不同应用类型的流程</p><div class="codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#393A34;--prism-background-color:#f6f8fa"><div class="codeBlockContent_biex"><pre tabindex="0" class="prism-code language-text codeBlock_bY9V thin-scrollbar"><code class="codeBlockLines_e6Vv"><span class="token-line" style="color:#393A34"><span class="token plain">└── definition</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">    ├── caas-0.0.1</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">    │   ├── fn</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">    │   └── index.ts</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">    ├── graphql-0.0.1</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">    │   ├── fn</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">    │   └── index.ts</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">    ├── graphql-0.0.2</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">    │   ├── fn</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">    │   └── index.ts</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">    ├── node-0.0.1</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">    │   ├── fn</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">    │   └── index.ts</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">    ├── node-0.0.2</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">    │   ├── fn</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">    │   └── index.ts</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">    ├── react-native-0.0.1</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">    │   ├── fn</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">    │   └── index.ts</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">    ├── react-native-0.0.2</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">    │   ├── fn</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">    │   └── index.ts</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">    ├── web-0.0.1</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">    │   ├── fn</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">    │   └── index.ts</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">    └── web-0.0.2</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">        ├── fn</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">        └── index.ts</span><br></span></code></pre><div class="buttonGroup__atx"><button type="button" aria-label="复制代码到剪贴板" title="复制" class="clean-btn"><span class="copyButtonIcons_eSgA" aria-hidden="true"><svg viewBox="0 0 24 24" class="copyButtonIcon_y97N"><path fill="currentColor" d="M19,21H8V7H19M19,5H8A2,2 0 0,0 6,7V21A2,2 0 0,0 8,23H19A2,2 0 0,0 21,21V7A2,2 0 0,0 19,5M16,1H4A2,2 0 0,0 2,3V17H4V3H16V1Z"></path></svg><svg viewBox="0 0 24 24" class="copyButtonSuccessIcon_LjdS"><path fill="currentColor" d="M21,7L9,19L3.5,13.5L4.91,12.09L9,16.17L19.59,5.59L21,7Z"></path></svg></span></button></div></div></div><p>打开 web-0.0.2/index.ts</p><p>endpoint 指明在流程推进到该节点时需要执行的函数。我们可以在该函数中执行该节点初始化对应的业务逻辑 并根据业务逻辑执行结果 返回相应的 节点流程状态。</p><p>以 review 节点为例，在节点初始化时...</p><p>我们再来看 complete 节点...</p><p>不同应用类型的流程编排和相应的业务处理逻辑，我们是在后端中定义。我们再来看下前端视图部分的映射。</p><p>packages/web/src/workflow/definition 中定义不同应用类型的流程视图映射</p><p>引擎会为绑定的 Overview 组件 和 节点组件 传入流程状态信息，供渲染使用，并为组件暴露触发流程状态变更的 dispatch 函数，供前端视图层调用。</p><p>通过声明式的代码，我们将 流程编排好了 并对视图做了绑定。
在发布详情页，我们只需使用 useWorkflow 方法传入 流程 ID，引擎就会为我们提供出对应的流程组件，并在流程状态变化时，驱动对应节点视图的渲染。</p><p>通过自研的流程引擎，我们将 流程状态管理 与 具体的业务逻辑 解耦，并通过 流程引擎 驱动前端视图的变更，以此降低了业务逻辑开发的复杂度，且在未来引入新应用类型时具有良好的扩展性。</p><h2 class="anchor anchorWithStickyNavbar_LWe7" id="四-openapi-网关的设计">四. OpenAPI 网关的设计<a href="#四-openapi-网关的设计" class="hash-link" aria-label="四. OpenAPI 网关的设计的直接链接" title="四. OpenAPI 网关的设计的直接链接">​</a></h2><p>Febase 的后端，不仅需要面向 Febase 管理后台前端提供接口。
同时还要向 第三方平台，如 Overmind、低代码平台 还有 CLI 工具，如 Mug 等提供开放接口。
过去我们搭建公技产品时的做法是，面向管理后台前端使用 Cookie 登录认证。
对第三方平台，使用特殊请求头。
通过白名单的方式，来指定开放接口
然后在每个业务逻辑实现中，根据 请求来源，单独地做 权限校验。
过去这种 职责不单一、面向过程式的鉴权方式，将会导致 难以维护 和 扩展，无法承载 Febase 开放平台的全部底层能力的目标。</p><p>我们的做法是抽取出了 单独的 OpenAPI 网关层。</p><p>在后端的接口中，用声明式的方式，描述接口所需的 最低权限。
在应用启动时，会收集全部的路由+权限声明，并存入数据库中。
并将 路由与权限信息注册至网关。
由于路由与权限信息都做了入库，所以也可以在管理后台中，对接口的权限做配置管理。
对于多消费场景，统一使用 Febase 派发的 用户 Token 进行认证和鉴权。
只有满足权限要求的请求，才会打到 Febase 后端。
这么做的好处是</p><ul><li>声明式的鉴权配置</li><li>多消费场景的统一认证机制</li><li>与 业务逻辑层的职责单一</li></ul><p>以此达到架构的可扩展和易维护。</p><h3 class="anchor anchorWithStickyNavbar_LWe7" id="介绍完多个模块的设计实现后我们再来看下-febase-的主体架构">介绍完多个模块的设计实现后，我们再来看下 Febase 的主体架构<a href="#介绍完多个模块的设计实现后我们再来看下-febase-的主体架构" class="hash-link" aria-label="介绍完多个模块的设计实现后，我们再来看下 Febase 的主体架构的直接链接" title="介绍完多个模块的设计实现后，我们再来看下 Febase 的主体架构的直接链接">​</a></h3><p>介绍完主体的架构后，我们介绍下 Febase 引入 GraphQL 应用类型的步骤。</p><p>由于可扩展性的架构设计，Febase 工程引入 GraphQL 部署流程，就像增加一个新插件一样，不会污染已有应用的业务逻辑，也不会增加整体工程的复杂度，使得整体架构保持良好的可维护性。</p><h2 class="anchor anchorWithStickyNavbar_LWe7" id="四未来展望">四.未来展望<a href="#四未来展望" class="hash-link" aria-label="四.未来展望的直接链接" title="四.未来展望的直接链接">​</a></h2><blockquote><p>介绍未来将 Febase 向集团内推广，我们需要做哪些努力？ 如底层部署的 租户隔离 、 域名定制 等</p></blockquote><blockquote><p>自动创建 曙光 埋点应用</p></blockquote>]]></content:encoded>
            <category>web</category>
            <category>dev-ops</category>
            <category>cicd</category>
            <category>febase</category>
        </item>
        <item>
            <title><![CDATA[微信聊天机器人]]></title>
            <link>https://kkdev163.github.io/presentation/wechaty</link>
            <guid>https://kkdev163.github.io/presentation/wechaty</guid>
            <pubDate>Mon, 06 Dec 2021 00:00:00 GMT</pubDate>
            <description><![CDATA[演讲 PPT: 微信聊天机器人]]></description>
            <content:encoded><![CDATA[<p>演讲 PPT: <a href="https://docs.google.com/presentation/d/1kV66A0O1FGXuOaRPK4TETlDfOAqIRnBAcMAh9b2_BEI/edit#slide=id.g28624b2fc2c_1_67" target="_blank" rel="noopener noreferrer">微信聊天机器人</a></p><p>演讲场所: 网易-组内分享</p><p>演讲简介: 介绍了微信聊天机器人, 包括</p><ul><li>什么是聊天机器人</li><li>Wechaty Demo 、API</li><li>PadLocal 实现初探</li></ul><p>演讲时间: 2021-12-06</p><div class="theme-admonition theme-admonition-caution alert alert--warning admonition_LlT9"><div class="admonitionHeading_tbUL"><span class="admonitionIcon_kALy"><svg viewBox="0 0 16 16"><path fill-rule="evenodd" d="M8.893 1.5c-.183-.31-.52-.5-.887-.5s-.703.19-.886.5L.138 13.499a.98.98 0 0 0 0 1.001c.193.31.53.501.886.501h13.964c.367 0 .704-.19.877-.5a1.03 1.03 0 0 0 .01-1.002L8.893 1.5zm.133 11.497H6.987v-2.003h2.039v2.003zm0-3.004H6.987V5.987h2.039v4.006z"></path></svg></span>警告</div><div class="admonitionContent_S0QG"><p>组内演讲，无逐字稿</p></div></div>]]></content:encoded>
            <category>chat-bot</category>
            <category>wechaty</category>
        </item>
        <item>
            <title><![CDATA[GMTC 2021 参会分享之-前端质量保障专题]]></title>
            <link>https://kkdev163.github.io/presentation/gmtc-web-monitor</link>
            <guid>https://kkdev163.github.io/presentation/gmtc-web-monitor</guid>
            <pubDate>Tue, 13 Jul 2021 00:00:00 GMT</pubDate>
            <description><![CDATA[演讲 PPT: GMTC 2021 参会分享之-前端质量保障专题]]></description>
            <content:encoded><![CDATA[<p>演讲 PPT: <a href="https://docs.google.com/presentation/d/1yGC1p2678ych9A-AIrQ5IvMD3rqE4lcvgRbr6K8O0XU/edit#slide=id.g28629c6ccea_1_67" target="_blank" rel="noopener noreferrer">GMTC 2021 参会分享之-前端质量保障专题</a></p><p>演讲场所: 网易-音乐事业部-技术沙龙</p><p>演讲简介: 刚刚过去的 GMTC 大会，是技术界的一次盛宴，大前端技术部派出代表北上取经。本次演讲介绍业界公司在前端线上质量保障方向的探索与实践。</p><p>演讲时间: 2021-07-13</p><div class="theme-admonition theme-admonition-info alert alert--info admonition_LlT9"><div class="admonitionHeading_tbUL"><span class="admonitionIcon_kALy"><svg viewBox="0 0 14 16"><path fill-rule="evenodd" d="M7 2.3c3.14 0 5.7 2.56 5.7 5.7s-2.56 5.7-5.7 5.7A5.71 5.71 0 0 1 1.3 8c0-3.14 2.56-5.7 5.7-5.7zM7 1C3.14 1 0 4.14 0 8s3.14 7 7 7 7-3.14 7-7-3.14-7-7-7zm1 3H6v5h2V4zm0 6H6v2h2v-2z"></path></svg></span>信息</div><div class="admonitionContent_S0QG"><p>以下为演讲逐字稿</p></div></div><h1>演讲逐字稿</h1><h2 class="anchor anchorWithStickyNavbar_LWe7" id="前言">前言<a href="#前言" class="hash-link" aria-label="前言的直接链接" title="前言的直接链接">​</a></h2><p>大家好，我是 kkdev163，今天为大家带来的分享是 GMTC 前端质量保障专题。</p><p>本次分享的议题基于 「快手-前端监控体系建设」、「蚂蚁-前端灰度监控与变更防御」</p><p>这里加个免责声明：本分享的全部素材取自以上议题。</p><h2 class="anchor anchorWithStickyNavbar_LWe7" id="一快手">一.快手<a href="#一快手" class="hash-link" aria-label="一.快手的直接链接" title="一.快手的直接链接">​</a></h2><p>首先我们来看 「快手的前端体系建设」，原分享主要分为三块：</p><ul><li>1 监控指标定义</li><li>2 快速分析定位原因</li><li>3 监控平台的搭建和稳定性保障</li></ul><p>由于时间的关系，第三部分我们不作展开。</p><h3 class="anchor anchorWithStickyNavbar_LWe7" id="11-监控指标定义">1.1 监控指标定义<a href="#11-监控指标定义" class="hash-link" aria-label="1.1 监控指标定义的直接链接" title="1.1 监控指标定义的直接链接">​</a></h3><p>首先来看快手的 页面性能监控指标定义。</p><p>从页面请求开始 到 页面加载完毕的时间线上，有多个关键的时间节点。。快手提出 如果我们只能用一个指标来代表前端页面性能，应该用哪个呢？</p><p>快手排除了</p><ul><li>Load, 由于他在长页面下 不能很好地反应用户体感</li><li>First Paint，骨架屏下，他无法体现页面核心内容渲染时间</li><li>FMP，他没有标准的实现</li><li>LCP, 最大元素不一定是最重要的元素，并且该指标的浏览器兼容性还不太高。</li></ul><p>最终快手给出的方案是 自定义 FMP, 快手对该指标的定义是在 API 数据渲染到 DOM 后的时间。</p><p>在分享中，讲师提到 自定义 FMP 指标的采集 有 SDK 自动采集 和 开发人员手动上报 两种途径，但目前 SDK 自动采集的 算法有效性还有待验证，所以目前快手主要还是依赖开发人员的手动埋点。</p><p>除了页面加载耗时，快手也针对其核心的业务场景，视频播放、视频直播，设计了专门的监控指标。如：</p><ul><li>播放失败率</li><li>启播耗时</li><li>百秒卡顿率</li></ul><p>同时快手也提出，作为前端开发者，我们也需要去关注 H5 容器的监控数据。。讲师举了个案例，有用户反馈一个页面很慢，然后开发者把 页面完全加载时间从 6.5 秒 降低至 4.5 秒，有了很大的提升，但用户的实际感受，还是很慢。。为什么呢？</p><p>分析下来后发现，原来从 点击 H5 入口 到真正开始加载 H5 页面，中间有一个 5.9 的秒的 Webview 启动时间。如果把 Webview 加载过程放大，会发现有</p><p>创建 Webview、注入 JS 桥、注入 Cookie 等环节。。所以如果我们只关注纯前端部分，而忽略 H5 容器的开销，我们监控到的性能其实是与 用户感受有较大偏差的。</p><p>所以快手针对容器，也设计了一系列完善的监控指标。</p><h3 class="anchor anchorWithStickyNavbar_LWe7" id="12-快速定位问题">1.2 快速定位问题<a href="#12-快速定位问题" class="hash-link" aria-label="1.2 快速定位问题的直接链接" title="1.2 快速定位问题的直接链接">​</a></h3><p>在完善了监控指标的采集以后，快手又是如何帮助开发者快速定位问题？</p><p>快手提出了 数据归因 诊断能力</p><p>简单来说，快手会先建立指标的 依赖树，比如 自定义 FMP, 他可能会受</p><ul><li>tcp 建连时间</li><li>网络传输时间</li><li>api 加载耗时</li></ul><p>等等 原子指标 的影响。</p><p>在 FMP 波动了 10% 的情况下，会根据每个 原子指标 的波动率，进行归因，计算每个波动因子的贡献度，最后选出 Top3 的贡献因子。</p><p>这张截图展示了，在 FMP 波动后，给出的智能归因。</p><p>除了数据归因诊断能力，快手也做了 访问级别 的请求还原，给到一个 userId，就能查出 详细的 访问情况，接到客诉的时候，就能快速排查出，他为什么慢。</p><h3 class="anchor anchorWithStickyNavbar_LWe7" id="13-总结">1.3 总结<a href="#13-总结" class="hash-link" aria-label="1.3 总结的直接链接" title="1.3 总结的直接链接">​</a></h3><ul><li>快手建立了以 FMP 为主的 前端度量指标体系</li><li>并且通过智能数据归因、访问级别的请求还原，来帮助开发者快速定位问题。</li></ul><h2 class="anchor anchorWithStickyNavbar_LWe7" id="二蚂蚁">二.蚂蚁<a href="#二蚂蚁" class="hash-link" aria-label="二.蚂蚁的直接链接" title="二.蚂蚁的直接链接">​</a></h2><p>接着我们来看蚂蚁的灰度监控与变更防御。内容主要分为两块：</p><ul><li>建立前端灰度监控体系</li><li>基于灰度监控的发布变更防御</li></ul><h3 class="anchor anchorWithStickyNavbar_LWe7" id="21-灰度监控">2.1 灰度监控<a href="#21-灰度监控" class="hash-link" aria-label="2.1 灰度监控的直接链接" title="2.1 灰度监控的直接链接">​</a></h3><p>这里的关键词是 灰度监控，我们先来看下什么是 灰度发布。</p><p>蓝绿发布简单来说，就是在 新版本 准备完成后，一次性将流量全部切换至新版本。</p><p>而灰度发布简单来说，可以选择为 部分的用户 提供 新版本 进行线上灰度验证，待验证无误后，逐步扩大灰度比例，直至 100%。</p><p>对于不同的应用类型，实现灰度发布的方案都有不同的区别。</p><p>以 支付宝官方小程序 的灰度为例。</p><p>当用户访问支付宝客户端时，根据灰度决策，会判断 当前用户是否进入 灰度池子，如果是的话，就拉取灰度小程序包，没有的话，就拉取 正式包。</p><p>这样的灰度发布看起来很完美啊，那支付宝遇到了什么问题呢？</p><p>讲师提到，在灰度变更的过程中，线上和灰度数据混在一起，监控无法感知应用当前正在灰度。。开发者想象中，在迭代发布后，会有明显的指标变动趋势，但实际的指标变化却是这样。</p><p>所以 开发者并不知道，灰度过程中，到底发生了什么，同时也不知道，这个版本对比线上正式包，情况到底如何。</p><p>也就让这个发布过程没有安全感，同事变更相关的故障率极高。</p><p>为了解决这个问题，支付宝建立了 前端灰度监控体系。。从流程上来看，在构建阶段，自动注入
此次迭代的标识，在用户访问时，使用 迭代标识 去 初始化监控，最后在监控数据上报时，携带上 迭代标识。</p><p>在数据做 清洗 的时候，把 迭代标识 作为顶层的维度进行聚合。这样就可以得出不同迭代版本的 数据。</p><p>在平台上就可以精确地区分出 此处迭代 引起的数据变化。</p><ul><li>不同灰度版本 的流量占比</li><li>以及不同版本下的错误数</li></ul><h3 class="anchor anchorWithStickyNavbar_LWe7" id="22-基于灰度监控的发布变更防御">2.2 基于灰度监控的发布变更防御<a href="#22-基于灰度监控的发布变更防御" class="hash-link" aria-label="2.2 基于灰度监控的发布变更防御的直接链接" title="2.2 基于灰度监控的发布变更防御的直接链接">​</a></h3><p>当蚂蚁建立起 灰度监控 后，蚂蚁进一步又做了发布变更防御。</p><p>我们来看下整体的流程：</p><p>在进入灰度前，会有一个前置的校验卡点，如果没有通过会直接终止发布，通过后，会开始向客户端下发灰度策略 比如 5%，在灰度策略下发后，会有一个灰度后置校验，如果验证成功，会进一步扩大灰度比例，如果失败，就立即终止发布。</p><p>我们可以看到，这里的关键节点是 灰度后置校验。我们来看下蚂蚁到底</p><ul><li>要进行哪些验证？</li><li>又要对哪些指标做验证？</li><li>这些验证又是如何保障灰度发布的？</li></ul><h4 class="anchor anchorWithStickyNavbar_LWe7" id="进行哪些验证">进行哪些验证<a href="#进行哪些验证" class="hash-link" aria-label="进行哪些验证的直接链接" title="进行哪些验证的直接链接">​</a></h4><p>平台希望用户去关心</p><ul><li>灰度有没有充分</li><li>有没有把应用、页面、小程序搞挂</li><li>有没有引入新的异常</li><li>异常相比于线上有没有增多</li></ul><p>为此建立了</p><ul><li>灰度流量</li><li>新增异常</li><li>异常率等指标</li></ul><p>有了这些验证 和 指标，又是如何保障灰度发布的呢？</p><ul><li><ol><li>当设置 5 % 的灰度比例，为了保证充分灰度，则要求当前迭代标识的流量必须接近全量的 5% 才能进入下一个灰度梯度。</li></ol></li><li><ol start="2"><li>在每个异常出现时，系统会记录 该异常的首次出现时间、首次出现迭代，若判断出，该异常是此次迭代中引入，则拦截此次发布。</li></ol></li><li><ol start="3"><li>计算出 此处灰度的 异常率，若明显高于基线版本，则触发拦截。</li></ol></li></ul><p>总结下:</p><ul><li>蚂蚁通过 监控数据 与 发布流程结合，建立起了 发布变更防御体系。为用户带来了发布安全感、也让监控数据发挥出更大的价值。</li></ul>]]></content:encoded>
            <category>gmtc</category>
            <category>performance</category>
            <category>web</category>
        </item>
        <item>
            <title><![CDATA[谷歌体验度量指标集 Web Vitals]]></title>
            <link>https://kkdev163.github.io/presentation/web-vitals</link>
            <guid>https://kkdev163.github.io/presentation/web-vitals</guid>
            <pubDate>Wed, 21 Oct 2020 00:00:00 GMT</pubDate>
            <description><![CDATA[演讲 PPT: 谷歌体验度量指标集 Web Vitals]]></description>
            <content:encoded><![CDATA[<p>演讲 PPT: <a href="https://docs.google.com/presentation/d/1yNfcjtnKpnJQ0GYi7WBvEDLdwBJRm-bBk3_yhXQ98pg/edit#slide=id.g2861ae5d815_2_90" target="_blank" rel="noopener noreferrer">谷歌体验度量指标集 Web Vitals</a></p><p>演讲场所: 网易-集团前端沙龙</p><p>演讲简介: 本次分享主要围绕谷歌最新提出的谷歌体验度量指标集-Web Vitals, 包含:</p><ul><li>该指标集提出的背景、含义、计算原理，并对围绕该指标集的谷歌系产品做简要介绍。</li><li>最后会介绍当前云音乐前端监控平台对该指标集的跟进情况和用户最佳实践等。</li></ul><p>演讲时间: 2020-10-21</p><div class="theme-admonition theme-admonition-info alert alert--info admonition_LlT9"><div class="admonitionHeading_tbUL"><span class="admonitionIcon_kALy"><svg viewBox="0 0 14 16"><path fill-rule="evenodd" d="M7 2.3c3.14 0 5.7 2.56 5.7 5.7s-2.56 5.7-5.7 5.7A5.71 5.71 0 0 1 1.3 8c0-3.14 2.56-5.7 5.7-5.7zM7 1C3.14 1 0 4.14 0 8s3.14 7 7 7 7-3.14 7-7-3.14-7-7-7zm1 3H6v5h2V4zm0 6H6v2h2v-2z"></path></svg></span>信息</div><div class="admonitionContent_S0QG"><p>以下为演讲逐字稿</p></div></div><h1>演讲逐字稿</h1><h2 class="anchor anchorWithStickyNavbar_LWe7" id="主题介绍">主题介绍<a href="#主题介绍" class="hash-link" aria-label="主题介绍的直接链接" title="主题介绍的直接链接">​</a></h2><p>大家好，我是来自云音乐的 kkdev163，在云音乐主要从事前端监控平台建设相关的工作。本次分享为大家带来的主题是《谷歌体验度量指标集 Web Vitals》。</p><p>本次主要分为四个部分：...</p><h2 class="anchor anchorWithStickyNavbar_LWe7" id="背景">背景<a href="#背景" class="hash-link" aria-label="背景的直接链接" title="背景的直接链接">​</a></h2><p>云音乐在 19 年的时候上线了前端监控平台，我们通过部署平台自动接入 SDK， 采集用户端的一些性能数据，并在平台上做可视化展示。</p><p>同时我们也基于 Lighthouse 搭建了一套实验室环境的性能度量体系。</p><p>结合二者，我们做了一个体验基线，在全体同事的努力下，云音乐整体的评分和通过率有了一些提升。</p><p>但在运作的过程中，我们也收到了一些反馈：</p><ol><li>实验室环境指标项很多，开发者有一定的认知成本</li><li>实验室环境指标 与 真实用户侧指标 不一致，在做优化时，难以对齐。</li><li>真实用户侧指标 domReady、load 等 虽然可以评估页面加载的快慢，但与用户肉眼所见的页面渲染，并不能直接关联。</li></ol><p>谷歌在今年提出了 Web Vitals，云音乐及时做了跟进和落地，他在一定程度上解决了我们的痛点，那么我们来看下， Web Vitals 是什么，他为什么能解决我们的问题？</p><h2 class="anchor anchorWithStickyNavbar_LWe7" id="概要">概要<a href="#概要" class="hash-link" aria-label="概要的直接链接" title="概要的直接链接">​</a></h2><p>Web Vitals 是谷歌提出的一组用来衡量 用户体验 的 度量指标集。</p><p>它包含了左侧,与用户体验直接关联的核心指标，以及右侧的一些辅助指标。</p><p>绿色核心指标是我们今天要重点展开介绍的。</p><p>核心指标集主要用来度量用户体验中的三个方面：</p><ul><li><ol><li>加载时性能</li></ol></li><li><ol start="2"><li>可交互性</li></ol></li><li><ol start="3"><li>视觉稳定性</li></ol></li></ul><p>分别用 LCP、FID、CLS 三个指标来衡量，并且谷歌也基于 Chrome 的用户数据，将每个指标划分为三档：</p><ul><li>较好</li><li>有待提高</li><li>较差</li></ul><p>核心指标集将页面的健康程度最终只落到于这三项指标上，聚焦于这三项指标展开优化，就能在很大程度上解决体验问题。</p><p>接来下，我们来详细的介绍这三个指标项：</p><h2 class="anchor anchorWithStickyNavbar_LWe7" id="lcp">LCP<a href="#lcp" class="hash-link" aria-label="LCP的直接链接" title="LCP的直接链接">​</a></h2><h4 class="anchor anchorWithStickyNavbar_LWe7" id="lcp-指标的含义">LCP 指标的含义<a href="#lcp-指标的含义" class="hash-link" aria-label="LCP 指标的含义的直接链接" title="LCP 指标的含义的直接链接">​</a></h4><p>LCP 最大内容渲染时间，是指当前视窗(viewport)中的最大内容元素的渲染时间。</p><p>在用户体验中，他被用来衡量页面的加载时性能。通常来说，视窗中最大的内容区块，是网页设计者最希望为用户传递的内容，而这部分关键内容的加载快慢，直接影响了用户的体验。</p><h4 class="anchor anchorWithStickyNavbar_LWe7" id="衡量的标准">衡量的标准<a href="#衡量的标准" class="hash-link" aria-label="衡量的标准的直接链接" title="衡量的标准的直接链接">​</a></h4><p>谷歌给出的建议是 LCP 需要控制在 2.5 秒 内，如果超出了 4 秒，对于用户来说，体检就会较差。</p><p>为了我们后续更好地展开优化，我们来了解下 LCP 的判定规则。首先我们来看下哪些元素的渲染可能会被判定为 LCP。</p><h4 class="anchor anchorWithStickyNavbar_LWe7" id="哪些元素会包含在内">哪些元素会包含在内<a href="#哪些元素会包含在内" class="hash-link" aria-label="哪些元素会包含在内的直接链接" title="哪些元素会包含在内的直接链接">​</a></h4><ul><li><code>&lt;img&gt;</code> 图片元素</li><li><code>&lt;svg&gt;</code> 中的 <code>&lt;image&gt;</code> 元素</li><li><code>&lt;video&gt;</code> 元素中的 poster image</li><li><code>background-image: url()</code> 使用背景图的元素</li><li>包含文字节点的块级(block-level)元素</li></ul><h4 class="anchor anchorWithStickyNavbar_LWe7" id="如何判定元素大小">如何判定元素大小<a href="#如何判定元素大小" class="hash-link" aria-label="如何判定元素大小的直接链接" title="如何判定元素大小的直接链接">​</a></h4><p>对 LCP 来说，一个元素的大小，仅仅只包含，当前视窗内可见的部分。如果一个元素，有部分内容超出了当前视窗或者是有不可见的 overflow, 只会计算可见部分的大小。</p><p>对于图片元素的判定，是结合图片自身的大小与屏幕上渲染的大小，取二者中较小的值。</p><p>对于包含文字的块级节点，只计算包裹所有文字节点的最小矩形面积。</p><h4 class="anchor anchorWithStickyNavbar_LWe7" id="demo-演示">Demo 演示<a href="#demo-演示" class="hash-link" aria-label="Demo 演示的直接链接" title="Demo 演示的直接链接">​</a></h4><p>介绍完了 LCP 的计算原理后，我们来看一个 Demo 演示，在页面加载过程中，在 2000ms 时页面加载了骨架屏，由于不包含我们上面提到的元素，所以没有 LCP 产生；在 2400 毫秒时，加载出了文字节点，此时浏览器会抛出一个 Performance 信息对象，包含当前的 LCP 元素 和 渲染时间。随着加载的继续，在 3000 ms 和 4500 ms 分别会抛出新的 信息对象。</p><p>随着页面的加载，浏览器会持续抛出 LCP 对象，直到用户开始交互(点击、滚动、按键等)为止。该页面最终的 LCP 值是 4500ms。</p><p>由于超出了 4 秒，所以是属于较差的 LCP</p><h4 class="anchor anchorWithStickyNavbar_LWe7" id="哪些因素会影响-lcp">哪些因素会影响 LCP<a href="#哪些因素会影响-lcp" class="hash-link" aria-label="哪些因素会影响 LCP的直接链接" title="哪些因素会影响 LCP的直接链接">​</a></h4><ul><li>请求 html 时的服务器响应时间</li><li>阻塞渲染的 CSS、JavaScript</li><li>资源加载时间</li><li>客户端渲染 (Client-side rending)</li></ul><p>LCP 虽然只是衡量了最大内容元素的渲染时间，但是该元素的渲染却与整个页面的请求加载链路有关，所以优化 LCP 需要综合地从以上 4 个影响方面出发。</p><h2 class="anchor anchorWithStickyNavbar_LWe7" id="fid">FID<a href="#fid" class="hash-link" aria-label="FID的直接链接" title="FID的直接链接">​</a></h2><p>FID 用户首次输入的延迟。</p><p>我们经常会看到一些页面，在加载初期，对于用户的交互，有些响应延迟。这主要是由于页面在加载初期，虽然渲染出了部分元素，但主线程仍然处于繁忙状态，无法及时地响应事件处理器，从而让用户感知到明显的延迟。</p><p>根据谷歌的建议，这个延时如果能控制在 100 毫秒 内，会有比较好的可交互体验，如果超出了 300 毫秒，用户的可交互体验就会较差。</p><h4 class="anchor anchorWithStickyNavbar_LWe7" id="fid-计算演示">FID 计算演示<a href="#fid-计算演示" class="hash-link" aria-label="FID 计算演示的直接链接" title="FID 计算演示的直接链接">​</a></h4><p>我们通过一个例子来更好的理解 FID。</p><p>页面加载初期，屏幕上渲染出了按钮，并且正在努力地计算骨架区块内容，浏览器的主线程处于繁忙状态。</p><p>如果在这个时候用户点击了按钮，浏览器是没有办法马上响应的。</p><p>它需要等到骨架区块计算渲染完成，处于空闲状态后，才会去响应点击事件的处理器，并产生一些 UI 变更。</p><p>在整个过程中 FID 计算的是，从用户点击到主线程首次空闲，有能力去处理事件响应器的时间。</p><p>而事件处理器本身的执行耗时，和由此可能产生的 UI 变更并不会记录在 FID 内。</p><p>从这个例子，我们也可以看出，FID 的值与用户在什么时候进行交互有很大关系，如果用户是等到浏览器完全空闲后在交互，那 FID 就会很小。。所以在数据呈现上，我们会使用分布图，来查看用户 FID 的分布。</p><h4 class="anchor anchorWithStickyNavbar_LWe7" id="有哪些事件会被记录为-fid-">有哪些事件会被记录为 FID ？<a href="#有哪些事件会被记录为-fid-" class="hash-link" aria-label="有哪些事件会被记录为 FID ？的直接链接" title="有哪些事件会被记录为 FID ？的直接链接">​</a></h4><p>FID 主要发生于用户输入的独立事件 如 点击、键盘输入等</p><p>滚动、缩放等连续事件，不会记录在内。</p><h4 class="anchor anchorWithStickyNavbar_LWe7" id="如果没有绑定事件处理器">如果没有绑定事件处理器？<a href="#如果没有绑定事件处理器" class="hash-link" aria-label="如果没有绑定事件处理器？的直接链接" title="如果没有绑定事件处理器？的直接链接">​</a></h4><p>FID 记录的是 收到用户输入事件 到 下一次浏览器处于空闲状态的 时间差。所以即使没有绑定事件处理器，FID 也能够被准确测量出来。</p><p>这样设计的目的，也是因为有些交互元素并不直接依赖于事件处理器，但是需要主线程处于空闲状态才能正常地响应，如：</p><ul><li>Checkboxes, radio buttons，and Text fields(<code>&lt;input&gt;</code>, <code>&lt;textarea&gt;</code>)</li><li>选择器的下拉菜单(<code>&lt;select&gt;</code>)</li><li>链接 <code>&lt;a&gt;</code></li></ul><h4 class="anchor anchorWithStickyNavbar_LWe7" id="影响-fid-的主要因素">影响 FID 的主要因素<a href="#影响-fid-的主要因素" class="hash-link" aria-label="影响 FID 的主要因素的直接链接" title="影响 FID 的主要因素的直接链接">​</a></h4><p>导致 FID 的主要原因是 页面加载过程中 解析和执行了太重的 JavaScript, 导致主线程繁忙。</p><p>优化的思路有：</p><ol><li>减小 JS 文件体积（Code-splitting、延迟加载）</li><li>减少主线程执行开销 (拆分 Long Tasks、异步执行、web worker)</li></ol><h2 class="anchor anchorWithStickyNavbar_LWe7" id="cls">CLS<a href="#cls" class="hash-link" aria-label="CLS的直接链接" title="CLS的直接链接">​</a></h2><p>我们最后来介绍 CLS ，累计布局偏移。 我们经常会发现一些页面，存在视觉抖动的情况，主要的原因是发生了布局的偏移。</p><p>该指标，可以用来衡量用户体验中的视觉稳定性。</p><p>谷歌给出的评估标准是 &lt; 0.1，与之前指标不一样，他是一个分数，我们需要来看下，它是如何计算的？这样才能更好地帮助我们理解，布局偏移在我们页面上产生的影响面。</p><p>首先他的计算公式是 影响面积分数 <!-- -->*<!-- --> 距离分数</p><ul><li>影响面积分数指的是，布局移动前后求并集的面积占整个视窗的比例，在这个的例子中，是 1/2</li><li>距离分数指的是，这次偏移的最大距离 占 整个视窗长边的比例，在这个例子中，是 1/4</li></ul><p>所以在这个例子中，布局位移评分为 0.5 <!-- -->*<!-- --> 0.25 = 0.125。</p><p>参考这个例子，了解了计算方式后，相信大家对 0.1 的布局偏移能产生多大的影响面，应该是有了直观的感受。</p><h3 class="anchor anchorWithStickyNavbar_LWe7" id="cls-影响因素">CLS 影响因素<a href="#cls-影响因素" class="hash-link" aria-label="CLS 影响因素的直接链接" title="CLS 影响因素的直接链接">​</a></h3><ol><li>图片 size 属性</li><li>不要在已渲染的元素上方插入新元素</li><li>使用 trasmform 动画</li></ol><h3 class="anchor anchorWithStickyNavbar_LWe7" id="谷歌系工具简介">谷歌系工具简介<a href="#谷歌系工具简介" class="hash-link" aria-label="谷歌系工具简介的直接链接" title="谷歌系工具简介的直接链接">​</a></h3><p>在了解完了核心指标的定义、计算原理 常见的影响因素后，我们来看下，在日常的开发过程种，如何去使用这三个指标，帮助我们优化用户体验。</p><p>展开介绍本地端工具...</p><p>这几个工具是我们在开发阶段，可以本地使用的，那有没有办法采集用户端的数据呢？谷歌提供了用户体验报告 API，和可视化的产品。。</p><p>展开介绍用户端分析工具...</p><p>以上的用户端数据，依赖于 Chrome 的数据采集，并且维度也比较单一，我们只能查到像 music.163.com 主域名的数据，对于更细分的页面，往往是查不到数据的。</p><p>所以为了支持更精细化的数据采集，谷歌也为我们提供了 web-vitals npm 包，通过这个包，我们可以方便地采集用户端的数据。</p><h2 class="anchor anchorWithStickyNavbar_LWe7" id="云音乐实践">云音乐实践<a href="#云音乐实践" class="hash-link" aria-label="云音乐实践的直接链接" title="云音乐实践的直接链接">​</a></h2><p>在云音乐，我们将 npm 包整合到了现有的 sdk 中，采集并呈现了核心指标的视图。</p><p>对于核心指标，我们是根据谷歌的建议，将 75% 的用户 是否通过好的标准，设为达标线。以 LCP 为例，好的标准是 2.5 秒，该页面在 2.5 秒内的用户占比只有 74%，所以 LCP 指标未达标。</p><p>通过这个看板，我们能直观地了解到，我们的页面在用户端的真实体验占比如何。</p><p>由于这几个指标是依赖于 Chrome 浏览器的 API, 相信也有不少同学，会担心指标的的兼容性如何？</p><p>我们通过一段时间的数据采集，发现随着用户设备的更新，我们采集的覆盖率也在逐步的上升，云音乐的某个应用已经达到了 39% 的用户覆盖率。</p><p>同时在实验室侧，我们也将 Lighthouse 升级到了 6.0 版本，在 6.0 下会提供 核心指标值，并提供针对核心指标的优化建议。</p><p>通过这次平台的升级更新，解决了我们一开始提到的痛点。。</p><ul><li>我们的开发者只需要重点关注 核心指标集</li><li>用户侧 与 实验室侧 有了相同的指标进行验证性能</li><li>用户侧的性能，可以使用与用户体验直接相关的指标体现，我们是站在用户视角，评估最终肉眼所见的渲染快慢，与中间使用到的前端技术方案无关。</li></ul><p>通过这次升级，我们也发现了一些新的问题，比如在使用 核心指标 的评估体系下，我们整体的平均分，有了大幅的下降。。这说明，我们的页面可以在新的评估标准下，有进一步优化的空间。</p><p>比如我们的某个页面，此前长期霸榜，但在新的评估体系下，在真实用户侧 和 实验室侧都暴露出了新的体验问题。如存在 LCP 值过大，存在 CLS 等。</p><p>在平台更新了 WebVitals 后，我们现在推荐的使用方式是这样的：念 PPT</p><p>这里打个小广告，WAPM 平台是支持 网易集团内部应用接入的，如果有其他事业部的同学想使用 的话，也可以加下 POPO 交流群。</p><h2></h2><p>最后我作个简短的总结：</p><p>这次分享，我们介绍了 云音乐引入 WebVitals 的背景，并且介绍了 3 个 核心指标集的定义和计算原理等，并介绍了平时在开发过程中可以使用到的谷歌工具。</p><p>最后我们给出了当前云音乐落地情况 和 带来的一些问题。</p><p>我们相信随着 WebVitals 指标的进一步普及 还有 开发者的及时更进，我们可以给用户带来的更好的用户体验。
Make The Web More Awesome!</p><p>我的分享结束了，谢谢大家。</p>]]></content:encoded>
            <category>web-vitals</category>
            <category>web</category>
            <category>performance</category>
            <category>apm</category>
        </item>
        <item>
            <title><![CDATA[云音乐前端性能监控平台]]></title>
            <link>https://kkdev163.github.io/presentation/web-apm</link>
            <guid>https://kkdev163.github.io/presentation/web-apm</guid>
            <pubDate>Thu, 13 Jun 2019 00:00:00 GMT</pubDate>
            <description><![CDATA[演讲 PPT: 云音乐前端性能监控平台]]></description>
            <content:encoded><![CDATA[<p>演讲 PPT: <a href="https://docs.google.com/presentation/d/18qGCugsDHTBBKgdpR-hsF9T4Blg2Yt_cv74NNxkfMJ0/edit#slide=id.g28626a33313_2_982" target="_blank" rel="noopener noreferrer">云音乐前端性能监控平台</a></p><p>演讲场所: 网易-集团前端沙龙</p><p>演讲简介: 介绍了云音乐前端性能监控平台。主要包括:</p><ul><li>1、平台定位、功能模块介绍。</li><li>2、平台架构设计与搭建之路。</li><li>3、平台存在的问题与后续的改进规划。</li></ul><p>演讲时间: 2019-06-13</p><div class="theme-admonition theme-admonition-info alert alert--info admonition_LlT9"><div class="admonitionHeading_tbUL"><span class="admonitionIcon_kALy"><svg viewBox="0 0 14 16"><path fill-rule="evenodd" d="M7 2.3c3.14 0 5.7 2.56 5.7 5.7s-2.56 5.7-5.7 5.7A5.71 5.71 0 0 1 1.3 8c0-3.14 2.56-5.7 5.7-5.7zM7 1C3.14 1 0 4.14 0 8s3.14 7 7 7 7-3.14 7-7-3.14-7-7-7zm1 3H6v5h2V4zm0 6H6v2h2v-2z"></path></svg></span>信息</div><div class="admonitionContent_S0QG"><p>以下为演讲逐字稿</p></div></div><h1>演讲逐字稿</h1><h3 class="anchor anchorWithStickyNavbar_LWe7" id="前言">前言<a href="#前言" class="hash-link" aria-label="前言的直接链接" title="前言的直接链接">​</a></h3><p>大家好，我是 kkdev163，来自云音乐前端技术组，今天带来的主题是云音乐 WebAPM 平台。</p><p>本次分享会分为三个部分，第一部分将介绍平台的定位及已有的功能模块。希望能让大家了解到平台的功能为何这样设计，了解这个平台可以解决什么问题，对大家的工作可以有哪些帮助等。</p><p>第二部分将介绍平台的架构演进之路。</p><p>希望能帮助大家掌握搭建通用监控系统所需的技术栈、了解其中的难点与解决思路。为后续解决相似的架构问题，提供参考。</p><p>第三部分将介绍平台后续的规划</p><p>会点出平台当前存在的不足与后续的改进思路，希望能引起大家的共同思考，帮助平台持续改进。</p><h3 class="anchor anchorWithStickyNavbar_LWe7" id="第一部分">第一部分<a href="#第一部分" class="hash-link" aria-label="第一部分的直接链接" title="第一部分的直接链接">​</a></h3><p>我们开始第一部分，平台的定位及已有功能模块的介绍。</p><ul><li>APM 是应用性能监控的简称。</li><li>它应该帮助开发者提升应用性能质量。</li><li>它应该提供一种衡量标准，能够证明我们产出的应用具有良好的性能与用户体验。</li><li>它的终极目标是 Make the web more awesome.</li></ul><p>如果从监控手段来分区，平台的功能大致可以分为两个方向。</p><ul><li><p>真实用户性能监控，它需要在页面上安装 SDK, 在用户访问页面时，采集上报用户真实的性能数据。</p></li><li><p>实验室合成测试监控，业界常见的性能测试有 Lighthouse、WebpageTest 等。它通过持续地测试来达成监控目的。</p></li></ul><h4 class="anchor anchorWithStickyNavbar_LWe7" id="真实用户性能监控">真实用户性能监控<a href="#真实用户性能监控" class="hash-link" aria-label="真实用户性能监控的直接链接" title="真实用户性能监控的直接链接">​</a></h4><p>我们首先来看下真实用户性能监控。
开发者安装 sdk 后，可以看到应用的访问量数据、页面加载时性能、页面运行时性能、并且可以通过数据分析模块，来进行多维度的数据分析。</p><h5 class="anchor anchorWithStickyNavbar_LWe7" id="访问量数据">访问量数据<a href="#访问量数据" class="hash-link" aria-label="访问量数据的直接链接" title="访问量数据的直接链接">​</a></h5><p>访问量数据包含 PV/UV、访问量 Top10 页面、用户浏览器占比、操作系统占比等。</p><p>通过这一部分数据，可以让我们对使用我们应用的用户更加了解，可以支撑我们做一些决策，如不再支持某款浏览器、或是性能测试需要覆盖到某个系统版本以上。</p><h5 class="anchor anchorWithStickyNavbar_LWe7" id="加载时性能">加载时性能<a href="#加载时性能" class="hash-link" aria-label="加载时性能的直接链接" title="加载时性能的直接链接">​</a></h5><p>APM 会给出不同页面的加载时性能数据，这一部分的数据源是通过 W3C 性能工作组产出的 Navigation API 采集到的。</p><p>APM 会计算出页面加载时，关键时间节点的走势，如首次渲染时间、页面完全加载时间等。</p><p>除了走势，我们还计算出一段时间内，页面加载过程中各阶段耗时的瀑布流。可以较清晰的看到，页面加载过程中，在哪些阶段有较高的时间开销。。</p><p>同时我们也基于 IP 分析出各地区用户的加载性能情况。</p><p>并给出了性能样本的分布，如果需要验证应用的秒开率，95%分位数等可以在这里验证。</p><p>应用的加载时性能数据是这几块。其中有些部分我们可以有主动优化的能力，如 DOM 解析、资源加载耗时等。</p><p>有些部分我们不能做什么，如 TCP 连接、CDN 的地区网络状况等。。但是有了这部分数据，有了与其他应用的对比后，我们也可以尝试去推动上下游进行优化。</p><h5 class="anchor anchorWithStickyNavbar_LWe7" id="页运行时性能">页运行时性能<a href="#页运行时性能" class="hash-link" aria-label="页运行时性能的直接链接" title="页运行时性能的直接链接">​</a></h5><p>页面运行时性能我们主要通过 W3C 性能工作组产出的 Longtask API 采集页面运行过程中的长耗时任务。</p><p>并给出了长耗时任务的走势及持续时长的分布情况。。</p><p>那什么是长耗时任务 Longtask 呢？
我们打开 Chrome 的 Performance.</p><p>主线程的工作是由这样一个一个的 Task 组成，当一个 Task 的执行超过 50ms 时，就可能会影响一帧的渲染，造成页面的卡顿。。</p><p>Longtask 可以用来帮助侦测页面运行时的卡顿率。</p><h5 class="anchor anchorWithStickyNavbar_LWe7" id="数据分析模块">数据分析模块<a href="#数据分析模块" class="hash-link" aria-label="数据分析模块的直接链接" title="数据分析模块的直接链接">​</a></h5><p>前面的模块都是按照页面整体维度，给出一个性能指标。。</p><p>而在数据分析模块中，我们可以选择不同的维度进行分析。。</p><p>比如我们这里选择了操作系统维度，来看一看 Android 与 iOS 设备的页面加载性能走势。。可以看到 Android 要比 iOS 高出一秒左右。</p><p>内置的事件有</p><ul><li>页面访问量</li><li>加载时性能</li><li>运行时性能</li></ul><p>可选的分析维度有</p><ul><li>页面地址</li><li>浏览器版本</li><li>操作系统版本
等。。</li></ul><p>除了这些系统预设的指标之外呢，我们还提供了自定义埋点 API。</p><p>可以在数据分析模块，查看到我们开发侧关心的一些埋点事件。可以应对大部分的自定义埋点需求。甚至是 Node JS 服务端的一些埋点，也可以打到 WebAPM 平台中。。</p><h4 class="anchor anchorWithStickyNavbar_LWe7" id="实验室合成监控">实验室合成监控<a href="#实验室合成监控" class="hash-link" aria-label="实验室合成监控的直接链接" title="实验室合成监控的直接链接">​</a></h4><p>APM 平台底层采用的是谷歌的 Lighthouse。</p><p>在这个页面,我们只需输入一个 url 地址，就可以发起一次 Lighthouse 测试。</p><p>等待数秒后，会展示测试的结果。</p><p>我们提供了原始的测试报告供下载。</p><p>并从测试报告中提炼了一些关键的指标，这些指标有 FCP、FMP、Speed Index、First CPU Idle、TTI 等。</p><p>需要说明的是, 这些指标不是从浏览器的 API 中采集的，他是对页面加载过程中逐帧进行截屏，根据图像算法，来计算出以上的几个指标。所以他会更接近用户的直观感受，会比 RUM 中的指标更会准确。</p><p>我们从分析的结果中，还提炼了页面首屏的加载资源，引导开发者去尽量减少关键渲染路径中的资源大小与数量。。</p><p>其实谷歌的 Chrome 中已经内置了 Lighthouse 的测试功能，那么我们为什么还要在 APM 平台上来测试呢，有什么区别吗？</p><p>我们先来铺垫一个细节，Lighthouse Performance 的分数是根据这五项的指标，按照权重计算出来的</p><ul><li>first-contentful-paint 3</li><li>first-meaningful-paint 1</li><li>speed-index 4</li><li>interactive 5</li><li>first-cpu-idle 2</li><li>estimated-input-latency 0</li></ul><p>这几项结果与测试浏览器所在的宿主机的网速、CPU 等都是相关的。所以在不同的宿主环境下测试，结果会存在波动，说直白点就是领导说你的 Lighthouse 分数很低啊，你说我电脑上是好的，这没有说服力。。</p><p>了解了这个细节后，我们来回到为什么使用 APM 平台来测试会更好？</p><p>首先 APM 的 Lighthouse 测试是在服务端的 Docker 容器内，使用无头浏览器进行测试，所有的页面会在一个相对稳定与一致的宿主浏览器环境下进行测试。。优势一就是测试的宿主环境相对一致。</p><p>第二点，我们可以把这个页面加入到定时测试任务中。APM 平台会以 10 分钟一次的频率对页面进行测试，对多次的测试结果求均值。。优势二是降低了减少了单次测试的不稳定性。</p><p>第三点，我们将一次次孤立的测试结果，关联上了时间维度，形成了页面性能的走势，对页面的性能优化，可以很明显的体现在走势上，更加地有说服力。。</p><p>第四点，我们将孤立的一个页面测试，与组织内的其他页面形成了关联，开发者除了得到一些性能数据以外，也可以更为直观的了解到，自己的页面在组织内到底处于哪个阶段。</p><p>我们可以向着组织内的标杆看齐，在此向古典专区的开发者杨老师致敬，我们需要有榜样的力量。。</p><p>除了上面的优势外，我们还做尝试与 H5 搭建工具合作。
在页面制作完发布上线时，会将 url 与运营人员的邮箱发送的 WebAPM 平台，平台会将页面加入到定时测试中，并将页面的这一次测试结果与组织内的排名发送给运营人员。。</p><p>邮件内会包含上线建议，比如优化超标的图片资源等。。</p><h4 class="anchor anchorWithStickyNavbar_LWe7" id="一部分总结">一部分总结<a href="#一部分总结" class="hash-link" aria-label="一部分总结的直接链接" title="一部分总结的直接链接">​</a></h4><p>介绍完 WebAPM 的功能后，我们通过对比做一个总结。。</p><p>Lighthouse 的优势是</p><ul><li>无需接入 SDK, 一个 URL 即可测试。</li><li>FCP FMP 等性能指标更贴近用户直观感受。</li><li>性能的评分较为的可信。。</li></ul><p>Lighthouse 的短板是：</p><ul><li>如果依赖手工录入 url，页面可能覆盖不全</li><li>需要模拟页面的前置依赖，如登录等。</li><li>测试的机型较为单一。</li></ul><p>真实用户性能监控的优势是：</p><ul><li>页面覆盖全</li><li>无需模拟，用户正常访问就可以采集到页面性能数据</li><li>机型覆盖广、数据样本量大</li></ul><p>真实用户性能监控的短板是：</p><ul><li>需要接入 SDK, 数据上报存在开销</li><li>性能指标依赖于浏览器的实现</li><li>性能指标与用户的感知存在一定的偏差</li></ul><p>其实 WebAPM 就是一个数据平台，我们可以利用这些数据，参与需求的决策、主动地发起性能优化、推动上下游优化、优化的成果可以成为我们述职、CPP 的材料。。</p><h3 class="anchor anchorWithStickyNavbar_LWe7" id="第二部分-平台的架构演进之路">第二部分 平台的架构演进之路<a href="#第二部分-平台的架构演进之路" class="hash-link" aria-label="第二部分 平台的架构演进之路的直接链接" title="第二部分 平台的架构演进之路的直接链接">​</a></h3><p>我们先介绍一个监控系统的简化模型。。</p><p>首先需要有一个数据采集端，他可是一个 sdk，日志采集器，也可以是一个传感器。</p><p>数据采集后会上报到服务端，服务端对数据做部分的清洗和加工后，写入到时序数据库中。</p><p>当前台需要可视化展示时，服务端会发送聚合查询到数据库，然后加工成可视化数据后，返回给前台做展示。</p><p>在 WebAPM 中，数据采集端就是浏览器 SDK, 服务器就是 Node JS，时序数据库是 Influx DB, 可视化展示是 Antd。</p><p>这个模型中，大家可能对 Influx 最不熟悉了。。</p><p>在介绍后续的架构演进之前，我们首先来介绍下，什么是时序数据库？</p><p>我们以 Influx DB 为例展开介绍。。</p><p>从这个表中，我们可以看到每条记录都包含时间字段，这是时序数据库的特点，并且时序数据库中内置了大量的基于时间的聚合函数。。</p><p>比如求一段时间内均值的 Mean 函数。</p><p>这段 SQL 就是计算过去 5 分钟时间内，tcp 和 load 指标的平均值。。</p><p>我们执行这条 SQL 后，就得到了两个指标的均值。。</p><p>事实上 WebAPM 的加载瀑布图正是通过这条 SQL 来实现的。</p><p>基于上一条 SQL，我们加上 GROUP BY time()，他就可以计算出过去 5 分钟内，每一分钟的平均值。诶，这个就是关联上时间维度的走势啦。。</p><p>事实上，WebAPM 的加载耗时的走势就是通过这条 SQL 来实现的。。</p><p>每次，我们已经掌握了时序数据库的基本用法。。</p><p>OK, 这个监控架构我们也掌握了。。</p><p>但是，我们仍然有些细节需要注意的。</p><p>比如当我们从过去 5 分钟，变为计算 7 天的平均值，并且假设我们要统计的是一个大量流量应用，且 7 天有 1000 万条用户数据记录。。</p><p>我们是否可以简单粗暴的把 5m 改成 7d 呢？来，我们试一下。。</p><p>不好意思，我们真这么做的话，哨兵马上就要报警了，告诉我们内存吃紧了，CPU 要耗尽了。。</p><p>那我们怎么来计算这 1000 万条记录，对他们求均值呢？？</p><p>其中一个解决思路是，把计算均摊到每分钟。。</p><p>原来我们要直接计算 7 天 1000 万个数据点。。</p><p>现在我们不这么做了。。</p><p>我们在收到数据的时候，就定时的，每一分钟，去计算这一分钟的数据聚合结果并把它保存起来。。</p><p>那么这样持续的计算，7 天就会有 10080 个计算结果。。</p><p>当我们需要去计算 7 天的均值时，我们不需要再从 1000 万个原始数据中求均值，我们只要在 10080 个数据中计算即可。。</p><p>数据的规模量大大降低，当然我们还可以再降，比如一小时计算一次，我们不展开讲。。</p><p>那么现在的问题就是如何定时做每分钟的计算呢？？</p><p>实际上 Influx 已经提供了这样的能力，叫做 Continue Query，我们简称 CQ 吧。。</p><p>创建一个 CQ 很简单，就跟查询是一样的。。</p><p>他会定时的，把原始数据，做聚合计算，然后把计算的结果写入到 CQ 表中。。</p><p>现在我们回到刚刚的需求，我们只需要稍作变更，从 CQ 表中请均值即可。。。</p><p>那么引入 CQ 后，我们的存储与计算模型就长这样了。。</p><p>没错，我们已经掌握了时序数据库的高级用法了。</p><p>但是随着应用接入越来越多，存入的原始数据就越来越多。。</p><p>Influx 会对存入的数据建立内存索引，索引的规模可以用 Series 这个指标来衡量，通过官方文档的图片，我们可以看到，Series 数量级与内存开销的关系图。。</p><p>我们当前 Series 的数量级已经达到了 200 万，云主机上的内存压力是蛮大的。。</p><p>我们如何来缓解这个问题呢？</p><p>其实我们可以在写入数据库前就将原始的数据做聚合，只存入聚合后的数据，那么就可以缓解 DB 的压力。。</p><p>现在的问题变成，如何在存入数据数据库前做数据的聚合。。</p><p>我们可以直接在 Node JS 上做聚合计算吗？</p><p>如果我们的 Node JS 是单机部署的话，只要在 Node JS 上做一个 1 分钟的数据窗口，将聚合后的数据写入数据库，这样的模型看起来好像也是挺合理的。。</p><p>但是如果我们的 NodeJS 是集群部署。。</p><p>我们如何来协调每一台机器上的数据窗口，进行同步、进行数据结果的汇总，设计将变得复杂。。</p><p>如果时间窗口由 1 分钟变成 1 个小时、甚至 1 天，Node JS 也会成为瓶颈。。</p><p>如果有一个做聚合计算的中间件就好了。。Flink 就是干这件事的。。Flink 很牛，但是在这里，我们先简单把他理解成做聚合计算的中间件。。</p><p>我们来看下引入 Flink 后的数据流。。</p><p>首先 SDK 将数据上报到 NodeJS, NodeJS 对数据做清理和加工后，发布到消息队列的原始数据队列里。。Flink 订阅了这个数据，会从队列里不断地取出数据，并根据配置的时间窗口做集合计算，然后将计算后的结果写到计算结果队列中，NodeJS 订阅了这个队列，在收到数据后，将聚合的结果写入到 InfluxDB。</p><p>后续前台需要查询数据，仍然是从 InfluxDB 中读取。。</p><p>Flink 很牛，那我们应该怎么玩呢？是要自己搭吗？不用，我们现在已经有这样的基础设施。。其他部门可能也都有。。</p><p>云音乐 Flink 计算平台-Magina, 在上面我们可以配置一个计算任务的数据输入源，数据输出源，和对数据的操作。。</p><p>输入与输出我们这边就是消息队列了。</p><p>操作可以通过写 SQL 或者开发一个 Java JAR 包的方式。。</p><p>我们这里演示的是 SQL。</p><p>我们只要知道这个平台可以做聚合计算、并且操作很便捷就可以。。具体的语法可以后面再了解。。</p><p>那引入 Flink 后有哪些优势呢？</p><p>首先我们降低了 Influx DB 的存储与计算压力。。
那可能有同学会疑问，你自己的压力小了，别人的压力大了啊，你这是甩锅。。</p><p>不是这样的，人家 Flink 平台，肯定是可以动态扩缩容的，可以动态变更算力，我们把计算统一到他这里管理，我们的 Influx 就不需要那么高的配置，对于整个组织来说，是节约资源。。</p><p>第二个优势是 Flink 可以通过配置自定义聚合函数，那么我们对数据的加工能力将不受限于 Influx DB 内置的聚合函数。。</p><p>第三个优势是
在使用 Influx 社区单机版的前提下，我们整个系统的架构更具有弹性的高可用方案。
有了消息队列以后，我们不需要为了应对峰时的流量而无限提高 Influx 的配置，而且 Influx 挂掉后，数据依然是在聚合并写到消息队列中。。等 Influx 恢复后，数据可以继续写入，挂掉这段时间的数据不会丢失。。</p><p>OK, 这就是为大家介绍的通用的监控架构。。我们的时序数据库可以是 Influx DB 也可以换成 ES。
前端展示我们可以自己开发，也可以使用开源的可视化 Grafana，无需开发，只要通过配置就可以做可视化展示。。</p><p>这个监控架构有什么用呢？？
其实后端提供的中间件、微服务，都会对服务做监控，以保障服务的可靠性。。那么随着 Node JS 的大力发展，我们前端后续可能也会提供出一些微服务，那么这些服务的保障就需要监控。。如果我们部门的基础设施，无法满足我们的需求。。那么我们就可以自己搭一个监控。。</p><h3 class="anchor anchorWithStickyNavbar_LWe7" id="未来规划">未来规划<a href="#未来规划" class="hash-link" aria-label="未来规划的直接链接" title="未来规划的直接链接">​</a></h3><p>最后我们聊一聊未来的规划，</p><h4 class="anchor anchorWithStickyNavbar_LWe7" id="数据精细化展示">数据精细化展示<a href="#数据精细化展示" class="hash-link" aria-label="数据精细化展示的直接链接" title="数据精细化展示的直接链接">​</a></h4><p>我们当前的真实用户数据展示中，会有数据的波动，其中有一部分原因可能是极端性能状况引起。</p><p>后续我们引入 Flink 后，可以通过自定义的聚合函数，将头尾 5%的极端数据剔除后请均值。。</p><h4 class="anchor anchorWithStickyNavbar_LWe7" id="性能对比">性能对比<a href="#性能对比" class="hash-link" aria-label="性能对比的直接链接" title="性能对比的直接链接">​</a></h4><p>我们现在通过 Lighthouse 的组织排名，可以比较直观地了解到自己开发的页面的性能水平，与其他页面的差距等。。</p><p>但是真实用户性能这块，我们应用的数据仍然是比较孤立的，开发者其实比较难以知道，自己的应用性能到底处于什么水平，是好是差。。</p><ul><li><p>后续会考虑将组织内的性能数据，建立性能分档，每个档位对应一个分数，计算出应用的评分。。</p></li><li><p>将相同业务场景的应用进行对比，如同样都是云音乐小程序、同样都是 SSR 的架构</p></li><li><p>并且基于业务场景，给出针对性的优化建议。。</p></li></ul><h4 class="anchor anchorWithStickyNavbar_LWe7" id="influx-单机版问题">Influx 单机版问题。<a href="#influx-单机版问题" class="hash-link" aria-label="Influx 单机版问题。的直接链接" title="Influx 单机版问题。的直接链接">​</a></h4><p>实际上，我们当前在用的是饿了么的 Influx Proxy 方案，可以通过配置成数据双写，来做数据的备份，以达到高可用的目的。。</p><p>也可以配置成数据分片，来提高整个集群的存储计算能力。。</p><p>但这个还不是最完美的。。杭研的 NTSDB 是真 Influx 集群，后续我们可能会迁移到 NTSDB 来。。</p><p>另外也借着这个机会，感谢 NTSDB 的作者，范欣欣同事，他对 WebAPM 的架构改进提供了指导。。</p><h3 class="anchor anchorWithStickyNavbar_LWe7" id="总结">总结<a href="#总结" class="hash-link" aria-label="总结的直接链接" title="总结的直接链接">​</a></h3><p>首先我介绍了平台的定位及已有的功能，按照监控手段划分，平台可以分为 2 个方向。</p><p>其次我介绍了平台的架构演进，为大家介绍了通用的监控系统技术栈和其中的难点与解决思路。。。</p><p>最后我介绍了平台后续的规划，包括精细化数据分析、应用性能对比和 Influx 集群迁移。</p><p>感谢大家的耐心聆听，本次分享就到这里。。最后是交流时间。。</p>]]></content:encoded>
            <category>web</category>
            <category>performance</category>
            <category>apm</category>
        </item>
        <item>
            <title><![CDATA[页面渲染性能优化基础]]></title>
            <link>https://kkdev163.github.io/presentation/performance-basic</link>
            <guid>https://kkdev163.github.io/presentation/performance-basic</guid>
            <pubDate>Thu, 07 Mar 2019 00:00:00 GMT</pubDate>
            <description><![CDATA[演讲 PPT: 页面渲染性能优化基础]]></description>
            <content:encoded><![CDATA[<p>演讲 PPT: <a href="https://docs.google.com/presentation/d/19Rcl-zVZZIcBvP8a7XecZB6bBtOry3HspIefa0Z9zms/edit#slide=id.g2861993eb27_1_50" target="_blank" rel="noopener noreferrer">页面渲染性能优化基础</a></p><p>演讲场所: 网易-组内分享</p><p>演讲简介: 介绍了页面渲染性能优化基础, 包括</p><ul><li>浏览器渲染 HTML 流程</li><li>浏览器渲染一帧流程</li><li>Longtask API 及 APM 介绍</li><li>Chrome Devtools-Performance</li></ul><p>演讲时间: 2019-03-07</p><div class="theme-admonition theme-admonition-caution alert alert--warning admonition_LlT9"><div class="admonitionHeading_tbUL"><span class="admonitionIcon_kALy"><svg viewBox="0 0 16 16"><path fill-rule="evenodd" d="M8.893 1.5c-.183-.31-.52-.5-.887-.5s-.703.19-.886.5L.138 13.499a.98.98 0 0 0 0 1.001c.193.31.53.501.886.501h13.964c.367 0 .704-.19.877-.5a1.03 1.03 0 0 0 .01-1.002L8.893 1.5zm.133 11.497H6.987v-2.003h2.039v2.003zm0-3.004H6.987V5.987h2.039v4.006z"></path></svg></span>警告</div><div class="admonitionContent_S0QG"><p>组内演讲，无逐字稿</p></div></div>]]></content:encoded>
            <category>web</category>
            <category>performance</category>
        </item>
    </channel>
</rss>