【腾讯云前端性能优化大赛】微信小程序首屏耗时优化,减少等待降低耗能

零、记(2021/12/30 更)

0.1 关于耗时

1. 首屏耗时与用户设备、所在网络环境、程序代码有很大关系。线上环境有2/3的运气成分。

2. 理论上相同地域访问https比访问ip慢,但小程序要求必须使用https。

3. 小程序启动时由微信加载代码包,与服务域名无关,进入页面后才会发起首次网络请求,某些用户环境出现ERR_CONNECTION_RESET,导致上报的API接口及首屏耗时因网络不通畅爆表。

小白记 于2021年12月

0.2 小程序码

参赛程序 - 姨妈备忘助手

一、RUM 接入

RUM 是腾讯提供的一款前端监控方案,只需根据赛事指引在控制台上创建业务系统和应用,获取上报ID;通过安装 npm 依赖配置 JSON 就可以实现测速和日志的收集。

代码语言:txt
复制
    // 在应用支持 NPM 时使用 NPM 安装 Aegis SDK。
    // npm install --save aegis-mp-sdk
// 引入后进行初始化
import Aegis from 'aegis-mp-sdk';

const aegis = new Aegis({
    id: 'xxxx', // 上报 id
    uin: 'xxx', // 用户唯一 ID(可选)
    reportApiSpeed: true, // 接口测速
    spa: true // spa 应用页面跳转的时候开启 pv 计算
});</code></pre></div></div><p> <strong>大写的注:</strong> </p><ol class="ol-level-0"><li>在小程序中,要按照小程序规则开启NPM构建。</li><li>必须将  https://aegis.qq.com  添加到 request 合法域名中,否则RUM控制台中没有正式版本的数据。一般会在 devTools 中开启忽略合法域名校验,为了方便开发调试调用开发环境,而忽略这个配置。 <code>o(╯□╰)o</code> </li></ol><h2 id="7dd35" name="%E4%BA%8C%E3%80%81%E6%95%B0%E6%8D%AE%E5%88%86%E6%9E%90">二、数据分析</h2><p>在 RUM 控制台页面性能页面,列出了时间段内每个页面的页面首次渲染时间,也提供了不同维度的数据统计,如地域、网络、机型等,接下来需要从页面中分析出耗时较大且访问较为频繁的页面进行优化。</p><p>通过按耗时和按采样量排序,很明显 1,3,4 这三个页面拉高了用户的等待时间,是主要考虑优化的页面。</p><figure class=""><div class="rno-markdown-img-url" style="text-align:center"><div class="rno-markdown-img-url-inner" style="width:100%"><div style="width:100%"><img src="https://cdn.static.attains.cn/app/developer-bbs/upload/1722837840349868163.png" /></div><div class="figure-desc">页面首次渲染时间.png</div></div></div></figure><h2 id="7fk9p" name="%E4%B8%89%E3%80%81%E5%85%B7%E4%BD%93%E9%A1%B5%E9%9D%A2%E5%88%86%E6%9E%90">三、具体页面分析</h2><p>这三个页面都包含网络请求,并在请求结束后通过 setData 触发页面的 DOM 更新,此外:</p><p>第一个页面是小程序的首页,包含广告组件和自定义的姨妈记录预测卡片的列表。</p><p>第三个页面含有自定义的日历组件和日常备忘卡片列表。</p><p>第四个页面包含广告组件和互联网图片资源。</p><h2 id="4qli6" name="%E5%9B%9B%E3%80%81%E9%A6%96%E5%B1%8F%E4%BC%98%E5%8C%96%E5%88%86%E6%9E%90">四、首屏优化分析</h2><p>提高网页的性能一般要包括优化加载的速度和程序执行的流畅度;而加载速度又可以优化服务端响应的时间(包括代码包的下载时间,网络请求接口的响应时间,互联网的图片及字体等资源文件)和页面自身加载和渲染的时间。</p><h3 id="bcb31" name="4.1-%E5%9B%BE%E7%89%87%E8%B5%84%E6%BA%90">4.1 图片资源</h3><p>常见图片可以采用<code>JPG</code>、<code>PNG</code>、<code>WEBP</code>、雪碧图或字体图标实现,可搜索到的描述大概都是不同格式适用的不同场景,以及从占用带宽上的描述。</p><ul class="ul-level-0"><li>首先要选择合适的图片格式<ol class="ol-level-1"><li>照片图片使用 webP</li><li>复杂的图形使用 PNG 或 JPG,看哪个格式的文件小</li><li>有透明度的图形使用 PNG 或 webP</li><li>可能缩放的图形、图标等使用 SVG</li><li>动态图尽量不使用 GIF,无法使用 CSS 的用视频替代</li></ol></li><li>在小程序中,只有网络图片且在基础库高于2.9.0的微信上可以使用<code>WEBP</code>,目前绝大多数的微信用户基础库以及高于2.16.0,因此可以对互联网图片转换为<code>WEBP</code>格式,减小网络加载耗时。</li><li>目前没有查到关于这几种方式显示图片资源的页面渲染耗时分析。</li></ul><h3 id="a0ukf" name="4.2-%E7%BD%91%E7%BB%9C%E5%88%86%E6%9E%90%E4%BC%98%E5%8C%96">4.2 网络分析优化</h3><p>通过控制台中的 API 监控,可以发现从微信小程序中检测到的网络请求耗时波动很大,分布在100ms - 4000ms 。</p><figure class=""><div class="rno-markdown-img-url" style="text-align:center"><div class="rno-markdown-img-url-inner" style="width:100%"><div style="width:100%"><img src="https://cdn.static.attains.cn/app/developer-bbs/upload/1722837840679342631.png" /></div><div class="figure-desc">API监控.png</div></div></div></figure><p>进入 nginx 筛选出对应时间段的网络请求,按照 <code>$upstream_response_time</code> 分组计数如图,发现服务器端接口性能比较稳定。问题应该出现在客户端网络到服务端中间的网络上。</p><figure class=""><div class="rno-markdown-img-url" style="text-align:center"><div class="rno-markdown-img-url-inner" style="width:100%"><div style="width:100%"><img src="https://cdn.static.attains.cn/app/developer-bbs/upload/1722837840963635981.png" /></div><div class="figure-desc">Nginx 日志中的耗时统计.png</div></div></div></figure><p>小程序中强制要求使用 https 协议发起网络请求,请求链路为 DNS -&gt; Connect -&gt; SSL -&gt; request -&gt; response ,在本机调试各阶段起止时间戳及耗时如下:(数据只代表本机网络情况,结果可能存在波动)。可见小程序在发起第二次请求时,已经将 DNS 等信息缓存,并重用了 Socket 链路,从客户端到动态加速网络之间的耗时大概在40ms,从动态加速网络到服务器耗时大概在 20ms。而在第一次请求中,客户端需要进行 DNS 解析,建立 SSL 链路增加了网络耗时。</p><h3 id="b3ufb" name="4.3-JS-%E4%BC%98%E5%8C%96">4.3 JS 优化</h3><ul class="ul-level-0"><li>首先排查在主线程上不要使用同步方法,减小主线程阻塞。</li><li>开启小程序中的代码按需注入,避免没有使用的代码注入到小程序运行环境中,影响注入耗时和内存占用。</li></ul><div class="rno-markdown-code"><div class="rno-markdown-code-toolbar"><div class="rno-markdown-code-toolbar-info"><div class="rno-markdown-code-toolbar-item is-type"><span class="is-m-hidden">代码语言:</span>txt</div></div><div class="rno-markdown-code-toolbar-opt"><div class="rno-markdown-code-toolbar-copy"><i class="icon-copy"></i><span class="is-m-hidden">复制</span></div></div></div><div class="developer-code-block"><pre class="prism-token token line-numbers language-txt"><code class="language-txt" style="margin-left:0">        {
        &#34;lazyCodeLoading&#34;: &#34;requiredComponents&#34;  
     }</code></pre></div></div><ul class="ul-level-0"><li>生产环境去掉不必要的日志打印</li></ul><div class="rno-markdown-code"><div class="rno-markdown-code-toolbar"><div class="rno-markdown-code-toolbar-info"><div class="rno-markdown-code-toolbar-item is-type"><span class="is-m-hidden">代码语言:</span>txt</div></div><div class="rno-markdown-code-toolbar-opt"><div class="rno-markdown-code-toolbar-copy"><i class="icon-copy"></i><span class="is-m-hidden">复制</span></div></div></div><div class="developer-code-block"><pre class="prism-token token line-numbers language-txt"><code class="language-txt" style="margin-left:0">        /** @const */
    var LOG = false;
    LOG &amp;&amp; log(&#39;hello world !&#39;);</code></pre></div></div><h3 id="9nu" name="4.4-CSS-%E4%BC%98%E5%8C%96">4.4 CSS 优化</h3><p>因为构建 CSSOM 树时会阻塞页面的解析,因此需要:</p><ul class="ul-level-0"><li>删除没有使用的 CSS 代码</li><li>减小 CSS 文件大小,如 #FFFFFF -&gt; #FFF , 0px -&gt; 0,不要使用 Base64 编码的图片。</li></ul><h2 id="8kgdi" name="%E4%BA%94%E3%80%81%E4%BC%98%E5%8C%96%E5%8E%86%E5%8F%B2%EF%BC%882021/12/30-%E6%9B%B4%EF%BC%89">五、优化历史(2021/12/30 更)</h2><h3 id="114ti" name="5.1-%E8%B5%84%E6%BA%90%E4%BC%98%E5%8C%96">5.1 资源优化</h3><p>1. 按照页面访问的频次,将图片资源分成常用的和不常用的,将常用的放入小程序代码包通过本地加载,将不常用的转换成webp放到服务器。</p><p>2. 减小代码包体积:压缩代码包中的图片资源,对PNG格式的透明图设置更少的颜色数;通过依赖分析,更换体积更小的markdown渲染依赖。<strong>从1M+下降到280K+。</strong></p><h3 id="8msb0" name="5.2-%E7%BD%91%E7%BB%9C%E4%BC%98%E5%8C%96">5.2 网络优化</h3><p>1. 由于只有一台华北地区的服务器,为解决不同地域和运营商的访问速度,开启全站动态加速。通过某测速网站看到可解析到70+个独立IP,并且不同地区的用户会访问到同运营商较近的IP,然后由DCDN通过内部线路转发用户到服务器的请求。</p><p>2. 通过分析,首次请求时需要进行域名的解析和建连,后续的请求会复用该链路,解析和SSL等时间为0。 <strong>通过某测速网站可看到平均响应时间在0.3s,考虑到链路复用,网络请求时间会在60ms左右。</strong></p><p>2. 对不经常变更且频繁访问的接口,在DCDN中设置静态缓存,并触发接口预热,将接口的响应缓存到DCDN的二级节点上。这样用户发起这些请求时,直接由DCDN做出应答而不回源到真实服务器处理。</p><p>3. 在服务端和客户端开启http/2协议支持。<strong>由于小程序中无高并发请求,自测没有明显改善。</strong></p><p>4. 对超过1kb的响应体开启gzip压缩。</p><p>5. 对服务端的图片等静态资源设置浏览器缓存。</p><p>7. 由于小程序不像BAT这么大用户量和高频的访问,根据DNS的解析机制,各地运营商如果有缓存DNS的解析结果就不会向跟DNS进行解析,利用网站测速提供的服务,发送GET测速,让各地运营商缓存DNS结果。</p><p>8. 开启OCSP Stapling,提高证书的校验性能。</p><p>9. 小程序使用中,服务端会调用api.weixin.qq.com换取用户的openid,通过调用微信公众平台接口获取该域名的各个ip地址,在服务器执行ping命令,找到最快的一个ip,写入host文件。<strong>从40ms降低到28.8ms。</strong></p><h3 id="6u0t6" name="5.3-%E4%BB%A3%E7%A0%81%E7%BC%96%E5%86%99">5.3 代码编写</h3><p>1. 代码中对图片标签开启懒加载。</p><p>2. 减小冗余的&lt;view&gt;层次嵌套。</p><p>3. 为data赋初始值,避免在网络请求结束后赋值引起页面元素的位置变动。</p><p>4. 开启代码的按需注入</p><p>5. 采用DCDN后,各地域各运营商的绝大部分网络请求会在0.3s内到达,因此删除了网络请求起止的loading浮层动画。</p><p>6. 修复一处很二的bug。</p><p>7. 原来为了真机调试方便,把网络请求、自定义组件的响应等打印到了vConsole中。目前线上生产环境删除了所有的日志打印。</p><p>8. 删除了部分无用的css代码。优化的部分css样式的选择器。</p><p>9. 将网络请求从onReady提前到onLoad。</p><p>10. 亲测使用骨架屏只会让人感觉页面非白了,但会增加首屏的时间。</p><h3 id="bs3ea" name="5.4-%E4%B8%9A%E5%8A%A1%E9%80%BB%E8%BE%91">5.4 业务逻辑</h3><p>1. 所有新用户(首次访问)最初进入小程序时,页面和data中的数据都是一样的。将这些数据作为data的默认值,直接显示出来,网络请求到达后,由于data内容不变,不会发生页面的重新渲染。</p><p>2. 用户使用程序后,将从网络请求中获取的用户数据存储到storage中。</p><p>3. 在服务端,当小程序用户登入后,开启异步线程将主要的用户数据从mysql刷入redis进行热备。</p><p>4. 对于长列表改为数据分页加载。</p><h2 id="66j4r" name="%E5%85%AD%E3%80%81%E6%88%AA%E5%9B%BE%E7%95%99%E5%BF%B5%EF%BC%882021/12/30-%E6%9B%B4%EF%BC%89">六、截图留念(2021/12/30 更)</h2><p></p><figure class=""><div class="rno-markdown-img-url" style="text-align:center"><div class="rno-markdown-img-url-inner" style="width:96.43%"><div style="width:100%"><img src="https://cdn.static.attains.cn/app/developer-bbs/upload/1722837841379324734.png" /></div><div class="figure-desc">RUM首屏截图</div></div></div></figure><figure class=""><div class="rno-markdown-img-url" style="text-align:center"><div class="rno-markdown-img-url-inner" style="width:100%"><div style="width:100%"><img src="https://cdn.static.attains.cn/app/developer-bbs/upload/1722837841833938176.jpg" /></div><div class="figure-desc">某测速网站DCDN截图</div></div></div></figure>