可能很多朋友还没有完全看完文章时,就说小程序中 image组件有一个 lazy-load属性,设置了就可以自动实现图片懒加载,我也知道这个属性,假如一个商品的图片,我不用image组件渲染呢?我如果在view组件中使用背景图片渲染呢?假如要进行view块的渲染呢?这些我都有考虑到,请大家耐心看完文章~

很多东西,我们不应该仅仅只是为了实现而去实现,更多的在此过程中探索最基本的原理,不能因为有了更方便的方法或者框架,而摒弃了探索与学习。

之前在思否上看到一篇文章:小程序之图片懒加载[完美方案,你不来看看?]  ,讲到了小程序中图片懒加载的实现思路,但讲得再多,终究只是DEMO,于是用上述方法在小程序中实测,并且在IOS与android环境下实机测试。

实现思路:小程序监听页面滚动事件,在页面滚动时,实时监测节点(虽说小程序没有节点,但还是可以使用 wx.createSelectorQuery()  这个API创建对象实例),当对象实例出现在屏幕显示区域时,做出相应的操作。

wxml:

<view class="girls-list-wrap">
  <view class='girls-list clearfix' id="J_GirlsList">
    <view class='item item-{{index}}' wx:for="{{group}}" wx:key="index">
      <view class='a'>
        <view class='item-wrap'>
          <view class='img'>
            <!-- image 如果图片属性show为true,显示真实地址,false时显示loading图片 -->
            <image src="{{item.show ? item.cdn : '/static/images/common/loading.png'}}" ></image>
          </view>
          <view class='info'>
            <view class="info">
              <text class="name">商品-{{item.id}}</text>
            </view>
          </view>
        </view>
      </view>
    </view>
  </view>
</view>

核心在于判断item的show属性,当show == true时,显示图片的真实地址,当show == false时,显示loading图片地址;

完整的js:


const app = getApp()
Page({
  data: {
    height: '', // 获取当前页面的可视高度
    group: [],
  },
  onLoad: function() {
    let that = this;

    that.showImg();

    wx.getSystemInfo({ // 获取页面可视区域的高度
      success: (res) => {
        that.setData({
          height: res.screenHeight,
        })
      },
    });


    wx.request({
      url: 'https://www.360mtd.com/app/ewei_shopv2_api.php?i=5&r=shop.getRecommand',
      method: 'GET',
      success: function(res) {
        let temp = res.data.list;
        for (let i = 0; i < temp.length; i++) {
          temp[i].show = false;
        }
        that.setData({
          group: temp,
        });

        that.showImg();

      },
    });



  },


  onPageScroll() { // 调用showImg函数
    let that = this;
    that.showImg();
  },


  showImg() { // 判断高度是否需要加载
    let that = this;
    wx.createSelectorQuery().selectAll('.item').boundingClientRect((ret) => {
      const group = that.data.group;
      const height = that.data.height;
      ret.forEach((item, index) => {
        if (item.top < height) {
          group[index].show = true;
        }
      })
      that.setData({
        group:group,
      })
    }).exec()
  }


})

JS部分讲解:

在页面onLoad时,首先获取当前设备的信息,得出屏幕的实际高度;

这里的数据,使用的真实数据,onLoad时通过wx.request请求到,保存到data中,并且拿到初始数据后,进行简单的改造:

        let temp = res.data.list;
        for (let i = 0; i < temp.length; i++) {
          temp[i].show = false;
        }
        that.setData({
          group: temp,
        });

目的是在每个item中添加show属性,并且默认值为false;

自定义一个方法,叫showImg();

  showImg() {
    let that = this;
    wx.createSelectorQuery().selectAll('.item').boundingClientRect((ret) => {
      const group = that.data.group;
      const height = that.data.height;
      ret.forEach((item, index) => {
        if (item.top < height) {
          group[index].show = true;
        }
      })
      that.setData({
        group: group,
      })
    }).exec()
  },

再到onPageScroll()中调用showImg(),当页面滚动时,触发showImg方法;

核心在于创建一个对象实例,并且监听item节点,如果某一个item节点出现在屏幕显示区域,则将item下的show属性改为true,从而达到wxml中渲染显示;

全部完成!

先看看PC端模拟器的显示情况:

但是,到了实机上就会出现问题,以下是IOS系统下的情况:

以下是Android系统下的情况:

可以看出,IOS下流畅加载,但是到了安卓系统中,渲染总会卡顿,初步分析:

1、因为页面滚动,会消耗大量系统资源,安卓手机,例如华为这种GPU处理不强的手机,就会卡顿

2、使用网络上别人做的DEMO,测试就完全没有问题,因为DEMO中使用的图片路径全部是本地路径,换成实际项目的情况下,从服务器请求,那么在请求未完成时,页面中是没有节点的,就算在请求完成的回调函数中调用方法,依旧存在问题,又怀疑为是微信小程序在IOS与android环境下不同的渲染机制造成的,经过网上搜索,看到一些资料

运行环境差异
微信小程序运行在三端:iOS、Android 和 用于调试的开发者工具。
三端的脚本执行环境以及用于渲染非原生组件的环境是各不相同的:
在 iOS 上,小程序的 javascript 代码是运行在 JavaScriptCore 中,是由 WKWebView 来渲染的,环境有 iOS8、iOS9、iOS10
在 Android 上,小程序的 javascript 代码是通过 X5 JSCore来解析,是由 X5 基于 Mobile Chrome 53/57 内核来渲染的
在 开发工具上, 小程序的 javascript 代码是运行在 nwjs 中,是由 Chrome Webview 来渲染的

因为之前有个VUE的配送员项目,为了方便挂在微信公众号上面,别的浏览器测试完全没有问题,只要在公众号上面打开,就无法请求数据,最后认定是微信浏览器的问题。

最终的取舍:

网络上面的例子,全部是本地的资源路径,但实际的使用中请求真实图片地址,还是会出问题;

最后仔细思考了一下,不论是页面滚动时使用 wx.createSelectorQuery() API的方法还是另一种 节点布局相交状态IntersectionObserver ,所有的操作其实都是基于 节点,但小程序、VUE这种新的框架,是虚拟DOM,我猜也是渲染的机制或者时间节点造成了部分异常,并且,该方法也不是一个科学合理的方案,因为上述的方法,都是一次性请求全部的数据,只不过图片是在后期进行渲染,假如有一种极端情况,请求出来的item有10万个或者更多,就算图片在后期进行懒加载,那么10万条数据的文本信息,也能把客户端卡爆,所以对于商品列表等多个数据请求时,最佳方案为分段请求,当页面触底,向后台发送参数 page=1,那么后台只返给客户端1页,每页可以自定义有XX个数据,第二次页面触底则发送page=2,以此类推,就算有10万条数据,客户端每次还是拿到10条或20条,同样能达到节省资源、快速渲染加载的效果,网易严选、京东的商品列表都用到该种方法,具体的实现会在后续章节中进行讲解,请持续关注!