预加载

原理

web而言,预加载精髓在于当页面渲染中需要一些JS或者CSS资源的时候,他们能够立即可用,如此一来减少了页面渲染的阻塞,使得页面切换显示更加流畅。在web中往往通过rel=preload实现:

  1. <head>
  2. <meta charset="utf-8">
  3. <title>JS and CSS preload example</title>
  4. <link rel="preload" href="style.css" as="style">
  5. <link rel="preload" href="main.js" as="script">
  6. <link rel="stylesheet" href="style.css">
  7. </head>
  8. <body>
  9. <h1>bouncing balls</h1>
  10. <canvas></canvas>
  11. <script src="main.js"></script>
  12. </body>

preload技术大量应用于webpack打包生成的应用中。需要更详细了解,可以查看mdn预加载 - 图1

然而,在小程序中并非如此。当用户打开小程序的时候,微信就已经从服务器将完整的小程序代码包下载下来了。故而,web端的预加载概念并不适用于小程序开发中。

小程序的预加载主要应用于页面的相互切换之间。

a->b

在小程序运行时中,当从A页面跳转到B页面的时候,小程序需要实例化B页面,进行界面初始化渲染等操作,这里会有100~200ms的消耗。在小程序跳转完成之后,我们需要从服务器端拉取数据进行展示,用户需要继续等待数据,理想情况下(良好4G)一个查询请求只需要100~300ms就可以完成。也就是在不进行优化的情况下,用户最快也要等待200ms以上,页面内容才有意义。

仔细想想,其实数据拉取和小程序实例化两件事情其实并不耦合,并发去做两件事不就减少了很多时间?

a->b

所以,小程序预加载的其实就是在页面跳转的同时去服务器端拉取数据,达到一种页面刚跳转完成,用户直接看到了有效页面的效果。

实现

落地到wxa的话,我们可以借助beforeRouteEnter实现。

首先在B页面定义好拉取数据的逻辑:

  1. // pages/b.wxa
  2. import {Page, fetch} from '@wxa/core';
  3. // 借助一个变量缓存
  4. let handle;
  5. @Page
  6. export default class B {
  7. beforeRouteEnter() {
  8. // 注意 this 访问不到B实例,因为beforeRouteEnter执行的时候B还未实例化。
  9. handle = fetch('path/to/your/remote', {}, {});
  10. }
  11. async onLoad() {
  12. try {
  13. let info = await handle;
  14. // 增量设置数据。
  15. this.$diff({...info});
  16. } catch(e) {
  17. // 错误处理
  18. this.setData({showError: true});
  19. }
  20. }
  21. }

可以看到我们定义了一个beforeRouteEnter函数,并且在beforeRouteEnter中发起了一次请求,然后在onLoad的时候使用拉取的数据重新渲染页面。

beforeRouteEnter

注意this访问不到B实例,因为beforeRouteEnter执行的时候B还未实例化。

接着我们利用Router提供的路由跳转能力从A跳转到B。

  1. // pages/a.wxa
  2. import {Page, Debounce} from '@wxa/core';
  3. @Page
  4. export default class A {
  5. @Debounce
  6. tap() {
  7. this.$router.push('./b');
  8. }
  9. }

这样子就可以自动触发beforeRouteEnter的逻辑了~😎

总结一下,预加载我们需要做好以下两点:

  1. 利用beforeRouteEnter钩子定义好数据拉取逻辑。
  2. 跳转的时候使用Router装饰器提供的方法触发。