当前位置:   article > 正文

小程序的渲染瓶颈,首屏加载优化_微信小程序的首屏加载时间怎么看

微信小程序的首屏加载时间怎么看

小程序的js

ECMAScript是一种由Ecma国际通过ECMA-262标准化的脚本程序设计语言, JavaScript 是 ECMAScript 的一种实现。理解 JavaScript 是 ECMAScript 一种实现后,可以帮助开发者理解小程序中的 JavaScript同浏览器中的 JavaScript 以及 NodeJS 中的 JavaScript 是不相同的。
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
浏览器中,脚本严格按照加载的顺序执行,而在小程序中的脚本执行顺序有所不同。小程序的执行的入口文件是 app.js 。并且会根据其中 require 的模块顺序决定文件的运行顺序,当 app.js 执行结束后,小程序会按照开发者在 app.json 中定义的 pages 的顺序,逐一执行。

小程序渲染层和逻辑层:

小程序的运行环境分成渲染层和逻辑层,WXML 模板和 WXSS 样式工作在渲染层,JS 脚本工作在逻辑层。

<view>{{ msg }}</view>
  • 1
Page({
  onLoad: function () {
    this.setData({ msg: 'Hello World' })
  }
})
  • 1
  • 2
  • 3
  • 4
  • 5

从这个例子我们可以看到3个点:
1.渲染层和数据相关。
2.逻辑层负责产生、处理数据。
3.逻辑层通过 Page 实例的 setData 方法传递数据到渲染层。
小程序的渲染层和逻辑层是通过两个线程管理,渲染层的界面使用了WebView 进行渲染;逻辑层采用JsCore线程运行JS脚本。那么他们之间是怎么通信的呢?
在这里插入图片描述
小程序逻辑层和渲染层的通信会由 Native (微信客户端)做中转,逻辑层发送网络请求也经由 Native 转发。

代码编译的过程

WXML编译:
WebView无法直接理解WXML标签,所以需要经过编译。
微信开发者工具内置了一个二进制的WXML编译器,这个编译器接受WXML代码文件列表,处理完成之后输出JavaScript代码,这段代码是各个页面的结构生成函数。

编译过程将所有的WXML代码最终变成一个JavaScript 函数,预先注入在WebView中。在运行时确定了页面路径之后,将路径作为参数传递给这个函数得到该页面的结构生成函数,页面结构生成函数接受页面数据,输出一段描述页面结构的JSON,最终通过小程序组件系统生成对应的HTML。
在这里插入图片描述

WXSS 编译
与 CSS 相比,WXSS 扩展的一些特性,包括rpx尺寸单位和样式导入语法,这些特性都是WebView无法直接理解的。
微信开发者工具内置了一个二进制的WXSS编译器,这个编译器接受WXSS文件列表,分析文件之间的引用关系,同时预处理rpx输出一个样式信息数组,如图10-4,每个WXSS文件对应于这个数组中的一项。
在这里插入图片描述
在运行时,根据当前的屏幕宽度,计算出1rpx对应多少像素单位,然后将样式信息数组转换成最终的样式添加到页面中。
JavaScript编译
微信客户端在运行小程序的逻辑层的时候只需要加载一个JS文件(我们称为app-service.js),而小程序框架允许开发者将 JavaScript 代码写在不同的文件中,所以在代码上传之前,微信开发者工具会对开发者的JS 文件做一些预处理,包括ES6转ES5和代码压缩(开发者可以选择关闭预处理操作),在服务器编译过程将每个JS文件的内容分别包裹在define域中,再按一定的顺序合并成 app-service.js 。其中对于页面JS和app.js需要主动require。
在这里插入图片描述

小程序的数据驱动

WXML结构实际上等价于一棵Dom树,通过一个JS对象也可以来表达Dom树的结构,WXML可以先转成JS对象,然后再渲染出真正的Dom树。如图:在这里插入图片描述
在setData改变数据的时候,会通过diff算法比较dom树的变化,产生的JS对象对应的节点就会发生变化,此时可以对比前后两个JS对象得到变化的部分,然后把这个差异应用到原来的Dom树上,从而达到更新UI的目的,这就是“数据驱动”的原理
小程序的每个页面各自有一个WebView线程进行渲染,但是小程序切换页面时,小程序逻辑层的JS脚本运行上下文依旧在同一个JsCore线程中

小程序的数据

App实例是单例的,因此不同页面直接可以通过App实例下的属性来共享数据。App构造器可以传递其他参数作为全局属性以达到全局共享数据的目的。
保持一个原则就可以提高小程序的渲染性能:每次只设置需要改变的最小单位数据。

页面常见的交互行为

触摸反馈,Toast和模态对话框,界面滚动
页面的用户行为

下拉刷新 onPullDownRefresh
上拉触底 onReachBottom
页面滚动 onPageScroll 用户转发
onShareAppMessage

小程序的协同工作:

在这里插入图片描述

小程序的底层框架

双线程模型
小程序是基于双线程模型的,在这个模型中,小程序的逻辑层与渲染层分开在不同的线程运行,这跟传统的Web 单线程模型有很大的不同,使得小程序架构上多了一些复杂度,也多了一些限制。
一般来说,渲染界面的技术有三种:

  1. 用纯客户端原生技术来渲染
  2. 用纯 Web 技术来渲染
  3. 介于客户端原生技术与 Web 技术之间的,互相结合各自特点的技术(下面统称
    Hybrid 技术)来渲染

由于小程序的宿主是微信,所以我们不太可能用纯客户端原生技术来编写小程序 。如果这么做,那小程序代码需要与微信代码一起编包,跟随微信发版本,这种方式跟开发节奏必然都是不对的。因此,我们需要像Web 技术那样,有一份随时可更新的资源包放在云端,通过下载到本地,动态执行后即可渲染出界面。
但是,如果我们用纯 Web 技术来渲染小程序,在一些有复杂交互的页面上可能会面临一些性能问题,这是因为在 Web 技术中,UI渲染跟 JavaScript 的脚本执行都在一个单线程中执行,这就容易导致一些逻辑任务抢占UI渲染的资源。

按照上面的讨论,使用纯客户端原生技术或纯 Web 技术都有各自的缺点,那如果使用两者结合起来的 Hybrid 技术来渲染小程序,能否优于各自独立渲染的技术方案呢?实际上,这种 Hybrid 技术在业界过去几年里演化过数种技术方案,典型的如早期的PhoneGap[1],还有近两年流行的React Native[2](下称 RN),还有像微信网页里的 JS-SDK[3] 这种轻量级的应用。
最终,我们选择类似于微信 JSSDK 这样的 Hybrid 技术,即界面主要由成熟的 Web 技术渲染,辅之以大量的接口提供丰富的客户端原生能力。同时,每个小程序页面都是用不同的WebView去渲染,这样可以提供更好的交互体验,更贴近原生体验,也避免了单个WebView的任务过于繁重。

Exparser框架

Exparser是微信小程序的组件组织框架,内置在小程序基础库中,为小程序的各种组件提供基础的支持。小程序内的所有组件,包括内置组件和自定义组件,都由Exparser组织管理。

1.基于Shadow DOM模型:模型上与WebComponents的ShadowDOM高度相似,但不依赖浏览器的原生支持,也没有其他依赖库;实现时,还针对性地增加了其他API以支持小程序组件编程。
2.可在纯JS环境中运行:这意味着逻辑层也具有一定的组件树组织能力。
3.高效轻量:性能表现好,在组件实例极多的环境下表现尤其优异,同时代码尺寸也较小。

小程序中,所有节点树相关的操作都依赖于Exparser,包括WXML到页面最终节点树的构建、createSelectorQuery调用和自定义组件特性等。

性能优化

小程序启动的时候:下载小程序代码包、加载小程序代码包、初始化小程序首页。
代码包下载:下载到的小程序代码包不是小程序的源代码,而是编译、压缩、打包之后的代码包。
从开发者的角度看,控制代码包大小有助于减少小程序的启动时间。

精简代码,去掉不必要的WXML结构和未使用的WXSS定义。

减少在代码包中直接嵌入的资源文件。

压缩图片,使用适当的图片格式。

如果小程序比较复杂,优化后的代码总量可能仍然比较大,此时可以采用分包加载的方式进行优化。
采用分包时,小程序的代码包可以被划分为几个:一个是“主包”,包含小程序启动时会马上打开的页面代码和相关资源;其余是“分包”,包含其余的代码和资源。这样,小程序启动时,只需要先将主包下载完成,就可以立刻启动小程序。这样就可以显著降低小程序代码包的下载时间。
在这里插入图片描述
页面层级准备
在小程序启动前,微信会提前准备好一个页面层级用于展示小程序的首页。除此以外,每当一个页面层级被用于渲染页面,微信都会提前开始准备一个新的页面层级,使得每次调用wx.navigateTo都能够尽快展示一个新的页面。

页面层级的准备工作分为三个阶段。
第一阶段是启动一个WebView,在iOS和Android系统上,操作系统启动WebView都需要一小段时间。
第二阶段是在WebView中初始化基础库,此时还会进行一些基础库内部优化,以提升页面渲染性能。
第三阶段是注入小程序WXML结构和WXSS样式,使小程序能在接收到页面初始数据之后马上开始渲染页面(这一阶段无法在小程序启动前执行)。

数据通信
在小程序启动或一个新的页面被打开时,页面的初始数据(data)和路径等相关信息会从逻辑层发送给视图层,用于视图层的初始渲染。Native层会将这些数据直接传递给视图层,同时向用户展示一个新的页面层级,视图层在这个页面层级上进行界面绘制。视图层接收到相关数据后,根据页面路径来选择合适的WXML结构,WXML结构与初始数据相结合,得到页面的第一次渲染结果。
在这里插入图片描述
更新数据通信
初始渲染完毕后,视图层可以在开发者调用setData后执行界面更新。在数据传输时,逻辑层会执行一次JSON.stringify来去除掉setData数据中不可传输的部分,之后将数据发送给视图层。
为了提升数据更新的性能,开发者在执行setData调用时,最好遵循以下原则:

1.不要过于频繁调用setData,应考虑将多次setData合并成一次setData调用;
2.数据通信的性能与数据量正相关,因而如果有一些数据字段不在界面中展示且数据结构比较复杂或包含长字符串,则不应使用setData来设置这些数据;
3.与界面渲染无关的数据最好不要设置在data中,可以考虑设置在page对象的其他字段下。

Page({
  onShow: function() {
    // 不要频繁调用setData
    this.setData({ a: 1 })
    this.setData({ b: 2 })
    // 绝大多数时候可优化为
    this.setData({ a: 1, b: 2 })
    // 不要设置不在界面渲染时使用的数据,并将界面无关的数据放在data外
    this.setData({
      myData: {
        a: '这个字符串在WXML中用到了',
        b: '这个字符串未在WXML中用到,而且它很长…………………………'
      }
    })
    // 可以优化为
    this.setData({
      'myData.a': '这个字符串在WXML中用到了'
    })
    this._myData = {
      b: '这个字符串未在WXML中用到,而且它很长…………………………'
    }

  }
})
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24

视图更新渲染
视图层在接收到初始数据(data)和更新数据(setData数据)时,需要进行视图层渲染。在一个页面的生命周期中,视图层会收到一份初始数据和多份更新数据。收到初始数据时需要执行初始渲染,每次收到更新数据时需要执行重渲染。
在这里插入图片描述
在这里插入图片描述
swiper组件数据加载慢怎么办?列表数据量太大怎么办?
先初始化数组,只给前三个赋值。可以只渲染3个,然后在swiperChange里面,每次change的时候删掉一个listCopy已经显示的值,再加上一个未显示的值。
首屏时间指的是什么?
首屏时间是指用户从打开小程序看到第一屏主要内容的时间,首屏时间太长会导致用户长时间看到的都是白屏,影响使用体验。
优化首屏时间,可以分为以下几种情况:

首屏渲染的内容较多,需要集合多份数据进行渲染。这种情况需要开发者把内容分优先级,把优先级高的内容做优先展示,缩短白屏时间;
首屏内容依赖的数据从服务端请求的时间太长。开发者需要从服务端侧具体分析服务端数据返回的时间长的原因;
一次性渲染数据太大或依赖的计算过于复杂。减少渲染的数据量、优化渲染相关数据的算法可以解决这类问题。

首屏的加载速度除了和网络有关系,和小程序自身启动机制也有关系,首先要了解小程序的启动机制,小程序的启动分为冷启动和热启动。
检查

  1. 检查图片 检查图片包括:
  2. 图片是否过大 检查图片属性,如果图片加载过大,就利用工具压缩图片。此时要考虑如果图片像素要求很高,压缩要注意不能失真,其次压缩要注意等比例,留意是否不小心剪裁了图片大小等。
  3. 图片懒加载 如果首页要加载的有很多列表或者图片展示,此时要注意图片加载时长,如果超过一定时间,懒加载是个不错的办法。
  4. 图片是否可以用cdn托管 对于icon小图标可以放在小程序项目image文件夹里,但是如果图片占用资源,放在cdn托管既可以缩小代码包的大小还可以提升加载效率。。
  1. 检查首屏接口耗时 一个接口一个接口的排查,在network中查看加载的时间,逐个排查原因,所有请求最好在1s内返回结果。
  1. 检查有无错误日志
    在JS中如果在同步任务中,一个错误的发生会造成后面整段代码都不执行。
    此时应该排查下是否有异常错误,避免出现首屏一直处于加载的状态。必要的时候try catch处理。
  1. 检查界面是否使用定时器 定时器一般设置为全局变量,或者定时器和倒计时相关功能绑定,用定时器一定要记得及时回收。
  1. 检查基础版本库
    如果首页使用里自定义组建,不同颁布要注意基础库要一致。可能不同基础库有些功能的支持条件不一致,要做兼容处理。

优化策略

  1. 分包加载:减少代码包

  2. 利用缓存:快速展示。
    当小程序被销毁需要重新渲染界面时,此时冷启动会再次请求接口加载数据,可以利用小程序提供了缓存方法wx.setStorage、wx.getStorage将数据存储在本地。

  3. 不使用空白屏
    视觉效果,增加交互感。
    当数据没有被渲染时展示页面的基本骨架,利用toast提示加载进度,或者loading提示,给用户反馈出加载的进度,会延长用户的等待时间。把优先级高的内容做优先展示,缩短白屏时间;

  4. 首页架构调整
    代码量的减少,主要是减少dom树的构建。

  5. 渲染优化
    避免首页多次setData,以上有介绍,初始渲染相关问题。

  6. 使用懒注入

{
  "lazyCodeLoading": "requiredComponents"
}
  • 1
  • 2
  • 3

注意:添加这项配置后,未使用到的代码文件将不被执行
7. 提前首屏数据请求
大部分小程序在渲染首页时,需要依赖服务端的接口数据,小程序为开发者提供了提前发起数据请求的能力:

数据预拉取:能够在小程序冷启动的时候通过微信后台提前向第三方服务器拉取业务数据,当代码包加载完时可以更快地渲染页面,减少用户等待时间,从而提升小程序的打开速度。
周期性更新:在用户未打开小程序的情况下,也能从服务器提前拉取数据,当用户打开小程序时可以更快地渲染页面,减少用户等待时间。

小程序动画的实现原理

简单来说,整个动画实现过程就三步:

创建一个动画实例 animation。
调用实例的方法来描述动画。
最后通过动画实例的 export 方法导出动画数据传递给组件的 animation 属性。

在这里插入图片描述
也可以通过css3动画样式来实现。
小程序直播-疯狂点赞动画实现原理解析
需求分析:

点赞动画图片大小不一,运动轨迹也是随机的。
点赞动画图片都是先放大再匀速运动。
快到顶部的时候,逐渐缩小并消失。
收到大量的点赞请求的时候,点赞动画不扎堆,井然有序持续出现。

具体实现参考该链接:https://www.yuque.com/caixiaozhen/fx8n76/zvyl06

h5原生功能怎么和webview通信呢?

在微信打开的H5可以调用到微信原生一些能力,例如公众号文章里可以打开公众号的Profile页。所以早期微信提供了Webview到原生的通信机制,在Webview里注入JSBridge的接口,使得H5可以通过它调用到原生能力。

我们可以通过JSBridge微信预览图片的功能:

WeixinJSBridge.invoke('imagePreview', {
  current: https://img1.gtimg.com/1.jpg',
  urls: [
    'https://img1.gtimg.com/1.jpg',
    'https://img1.gtimg.com/2.jpg',
    'https://img1.gtimg.com/3.jpg'
  ]
})
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8

在这里插入图片描述

wx.previewImage({
  current: https://img1.gtimg.com/1.jpg',
  urls: [
    'https://img1.gtimg.com/1.jpg',
    'https://img1.gtimg.com/2.jpg',
    'https://img1.gtimg.com/3.jpg'
  ]
})
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
声明:本文内容由网友自发贡献,不代表【wpsshop博客】立场,版权归原作者所有,本站不承担相应法律责任。如您发现有侵权的内容,请联系我们。转载请注明出处:https://www.wpsshop.cn/w/知新_RL/article/detail/120845
推荐阅读
相关标签
  

闽ICP备14008679号