重渲染与自定义组件优化(上)
[视频] 你好,我是李艺。上节课我们主要学习了小程序里面4种实现动画的方式,其中以WXS脚本动画最高效,以关键帧动画最好用。 这节课我们学习自定义组件的优化技巧。 首先我们看一个问题,什么是重渲染? 我们知道视图如果需要改变,需要逻辑层通过setData方法、改变视图上面绑定的数据。当数据发生变化的时候,小程序会将原数据与新数据进行结合,应用在对应的wxml节点之上。在这个内存里面得到一个新的节点树并拿新的节点树与我们当前这个页面里边的原节点树进行一个差异比较,以此来获得哪些节点的属性需要更新、哪些节点需要添加或者是移除等等这些信息。得到这些信息以后再使用新节点树,有目标地将原节点树上的节点然后进行更新或者是删掉或者是添加或者是更新原来的内容这样的一个操作。这个过程就是重渲染。 从渲染机制我们可以看出来每次通过setData传递数据,它不仅受到底层传输通道窄小的限制,它还受到设备渲染能力的一个限制。这个数据越大节点越多越复杂。它渲染也就越慢,而渲染一慢在用户侧看来它表现起来就是卡顿 操作反馈不及时。那么针对这种问题有没有办法解决?有没有办法可以提升重渲染的一个效率?小程序在进行新节点树与原点节点树的一个差异比较的时候会着重比较那些 setData数据影响到的节点属性。因此我们可以将这个界面功能进行组件化处理,将频繁变化的数据封装在一个个的单独的组件里边,同时去掉一些不必要的数据设置,减少每次setData传递的一个数据量,也可以提升这个视图的渲染效率,还有通过wxs脚本改写组件,让可以在这个视图层里面完成的代码逻辑,就在这个视图层里面进行完成。这也是一种提升渲染效率的有效技巧之一。 接下来我们看项目实践。 在主页里面有一个秒表计时器,它可以以毫秒为单位进行更新,在这个视图页面上,这一块它属于数据密集更新的一个区域,为此我们将它进行组件化。 首先我们看实践一:实现自定义组件stopwatch。 stopwatch是一个常规的秒表组件,JS代码。我们可以看到它主要是有三个方法start stop和switch分别控制秒表的开始、停止和切换这样的一个动作。在stopwatch组件内部,稍后我们会看到它有一个使用setInterval创建的一个定时器,在start方法里面调用的时候会启动,同时会在stop方法调用的时候会清除。组件的wxss样式代码很简单,它只有一个类样式,将这个组件的样式放在组件的内部,这样可以在一定程度上保证组件样式的一个独立性而不受其他的页面元素的一个影响。 stopwatch组件在创建完成以后怎么样去使用它?首先我们需要在app.json配置文件里面添加对这个组件的一个引用,在这里我们添加的是全局引用,添加以后这样在任何一个页面里面都可以直接去使用它了。组件的引入信息添加以后我们就可以在主页的wxml这个页面里面使用stopwatch组件代码了,在测试这个组件的时候可以同时打开Performance面板,这个面板的表现也就是如果我们现在屏幕上看到这张图。我们可以看到每一次的渲染大概会花费30毫秒的一个时间,这个时间不算长,但是这个组件的本身它要做的事情却是十分简单的,它仅仅是计算消失的一个时间以及对时间对象进行一个字符串的格式化,如果说做这件事情就需要浪费那么多时间的话效率其实已经不算高了。 下面我们开始实践一:实现自定义组件stopwatch的代码演示。 首先第一步我们需要在我们这个项目里面,然后添加一个新的组件。组件我们要放在index_addons在这个目录下面,这里面有一个components放在这个下面,然后组件我们看一下它这个代码放在这个地方。首先我们看它wxml代码,这个代码很简单,它有一个view的容器,然后里边有一个slot。这个slot它可以将我们调用端的消费代码,就是业务代码放在这个地方。然后下面有一个text节点的文本的数据的绑定,我们再看它的样式代码,样式代码也很简单,这是它的一个样式,这是一个类名。 这个类名我们在这个地方有使用,我们将组件的样式代码然后放在组件内部可以让每个组件都保持一定的独立性。当然缺点也是有的,就是它每个组件,你在确定以后就不方便在每个页面里面进行个性化了。然后折中的办法,我们可以进一步地给组件添加一些个性化的属性去控制它的样式的展示。一般情况下对于小程序的一个开发,基本上所有的页面它的风格都是统一的,所以你也不需要太多的样式,简单的一个处理在组件的内部让组件自身去负责它的样式 这样就可以了,这样是一种简单方便的一种方法。 我们再看它的JS代码,最上面这个地方是它的属性,属性里面有一个是mode代表的是我们当前这个模式,它的默认值是stop就代表的是停止,秒表状态默认是一个停止的状态,再往下面data数据里面有一个text,这是我们的要渲染的文本,稍后这个文本它会以一种秒表的形式它不停地去跳动这是我们要实现的一个功能。 methods里面有三个方法start stop还有switch,start我们在这个里面主要干什么事情,我们看一下“改变状态”组件的状态,接下来是我们记一个起始时间。然后在这个convertTimeStampToString这个函数里面,做了一个转化一个时间串的格式化,时间对象的格式化,目的就是将我们的时间一个差值就是相对于某个时间的差值当前时间相对于某个点的一个差值进行一个格式化,把它转化成这样一个 就是有分钟、有秒、有毫秒的这样一个时间字符串,这是它的一个功能。由于JS里面没有直接的一个格式化时间的这样的一个标准方法,所以这样的一个功能需要我们自己写代码去实现。 再往下就是我们用了这个setInterval,去开了一个定时器。开定时器,每100毫秒去调用,然后间断地去执行我们这个方法同时我们传给它的参数,我们也可以看到因为它起始值是这样的一个值,随着时间往后推移,这个时间其实是慢慢地会变少的、慢慢地会变化的,stop相对就简单了,改变组件的状态然后用clearInterval去清除定时器的id,这是它的一个方法。switch是切换,本质上switch不要也可以,它其实是对我们start和stop这两个方法的一个代替调用,我们用了一个switch就可以了。它可以在stop以及start这两种状态的变化中间然后进行切换,这是它的一个主要的功能。这是这个组件的代码,我们已经说完了。 接下来我们做另外一件事情就是我们在app.json里面去做一个组件的全局引入。组件的全局引入,在这个里面我们需要加一个usingComponents。当我们打us的时候,它还会有代码提示的,直接回车就可以了,然后这个里面我们要加上对这个组件的一个引入,可以看一下我们最终的一个源码看它是怎么写的。如果是你们在实践的过程当中遇到一些代码不知道怎么写,也可以查看最终的源码。源码也是随我们这个课程一起提供的,找到我们这个usingComponents这个节点,然后下面这个stopwatch这就是我们要添加一个组件,将这个字段复制一下,这样就添加完了。 接下来就是使用。需要在我们的首页里面,找到我们的首页index pages然后index这个页面去添加这个代码。首先我们要加wxml这个标签,看一下最终代码是长什么样子,找到stopwatch在这个地方,这是它的标签代码。这个地方我们那个标签还没有,我们将整体的这个都需要给它拿过来放在长列表这个区域的上方,然后JS代码这个里面涉及到了一个theme一个样式。这个数据属性我们在这个地方已经有, 没有关系。 另外还有一个是我们这个代码,看看有没有涉及到其他的一些方法,这地方有一些方法对吧。在我们这个组件上面看一下,组件本身stopwatch组件上面这个地方有一个switch。它本身在单击的时候,就会调用自身的switch方法,所以在我们外面就是我们消费代码这个地方。这个地方 其实已经不需要了。 switch我们现在不需要它,把它去掉这个代码现在已经修改完了。 现在我们尝试编译一下,看它的一个运行效果。这个地方我们可以看到 是不是有代码错误了,是不是有代码错误,然后这个地方有没有路径问题,首先要检查一下 它这个错误信息,我们看一下它说是组件没有找到,在这个路径下面没有找到。它为啥没有找到呢?是不是这个路径不对,我们可以看一下这个路径然后清除编译缓存,我们再测试一下切到我们的首页。现在我们可以看到秒表组件已经显示了然后因为我们在上面已经添加了tap组件,我们单击的时候它应该是可以运行的对吧,现在我们单击一下试试。 看到了吧,我们单击这个组件已经开始运行了,并且文本在快速地进行变化。像这样的一个区域就是我们前面提到的属于数据密集更新的这样的一个区域,就是对于这样的一个区域因为它变化十分的频繁,我们需要将它进行组件化,然后放在一个小组件里边。这个组件它本身在更新的时候只是更新它内部这一块区域,做一个局部的这样一个渲染,这个可以从重渲染机制去提升这个组件的运行效率这样一种方式。这个地方会频繁地有一个打印对不对,这个打印我们看一下这个信息在哪里,我们可以尝试先让它停一下,它界面的反应有些迟钝。 我们可以看到单击以后其实没有马上停止,这个地方还有一些输出,缓存区的输出。另外由于它这个地方在密集地进行更新,现在导致我们整个的微信开发者工具整个软件显得都有些卡顿了,这也说明了我们这个组件目前这种方式肯定是有问题的,是有运行时的一个性能问题的。我们把这个重启刷一下,终于启动了,连续点了好几次然后终于启动了,这个组件肯定是有问题的、是值得优化的。 我们先说一下这个地方打印,打印是在这个地方我可以将频繁地打印先给它注释掉不让它打印,接下来我们还可以打开我们的Performance面板、性能面板。打开性能面板,这一次我们在测试的时候就看一下面板的表现,然后单击开始,然后我们这个地方单击录制,可以了。现在我们可以停止了,看到了吧,是不是有很多红三角。 前面我们说到了当我们在Performance面板里面看到有很多倒的这种红三角的时候,就说明我们项目的性能、运行的性能已经非常糟糕了。我们可以具体地挑某一个来看一下,比如说这个它执行时间是73.58ms ,这个大概是稍微好一点是37,但是也不理想,具体我们把这个地方可以放大看一下它里边都是运行了哪些东西。这地方有一个start然后从start里面往里它会有setData,总体上执行时间是54.75ms,时间是相当长的而且它如果时间执行长的话本身Task任务条颜色也会有变化,就是颜色显得比较靓丽比较有警示作用,这个地方我们可以看一下。这地方执行了很多的匿名函数。本身也是由start进行驱动,执行时间达到了65.26ms,这个时间太长了,当然对于我们人类来讲几十毫秒很小,但是对于计算机来讲几十毫秒、大于30毫秒就是每一个Task它大于30毫秒,它其实已经是有问题的。这种可以造成我们这个页面的卡顿,甚至会造成我们整个软件的一个卡顿都是可能有这个问题的。 好,这个代码演示我们就说到这里。