Code Monkey home page Code Monkey logo

recycle-view's Introduction

recycle-view

小程序自定义组件

使用此组件需要依赖小程序基础库 2.2.2 版本,同时依赖开发者工具的 npm 构建。具体详情可查阅官方 npm 文档

背景

​ 电商小程序往往需要展示很多商品,当一个页面展示很多的商品信息的时候,会造成小程序页面的卡顿以及白屏。原因有如下几点:

  1. 商品列表数据很大,首次 setData 的时候耗时高
  2. 渲染出来的商品列表 DOM 结构多,每次 setData 都需要创建新的虚拟树、和旧树 diff 操作耗时都比较高
  3. 渲染出来的商品列表 DOM 结构多,占用的内存高,造成页面被系统回收的概率变大。

因此实现长列表组件来解决这些问题。

实现思路

​ 核心的思路就是只渲染显示在屏幕的数据,基本实现就是监听 scroll 事件,并且重新计算需要渲染的数据,不需要渲染的数据留一个空的 div 占位元素。

​ 假设列表数据有100个 item,知道了滚动的位置,怎么知道哪些 item 必须显示在页面?因为 item 还没渲染出来,不能通过 getComputedStyle 等 DOM 操作得到每个 item 的位置,所以无法知道哪些 item 需要渲染。为了解决这个问题,需要每个 item 固定宽高。item 的宽高的定义见下面的 API 的createRecycleContext()的参数 itemSize 的介绍。

​ 滚动过程中,重新渲染数据的同时,需要设置当前数据的前后的 div 占位元素高度,同时是指在同一个渲染周期内。页面渲染是通过 setData 触发的,列表数据和 div 占位高度在2个组件内进行 setData 的,为了把这2个 setData 放在同一个渲染周期,用了一个 hack 方法,所以定义 recycle-view 的 batch 属性固定为batch="{{batchSetRecycleData}}"

​ 在滚动过程中,为了避免频繁出现白屏,会多渲染当前屏幕的前后2个屏幕的内容。

包结构

长列表组件由2个自定义组件 recycle-view、recycle-item 和一组 API 组成,对应的代码结构如下

├── miniprogram-recycle-view/
    └── recycle-view 组件
    └── recycle-item 组件
    └── index.js

包结构详细描述如下:

目录/文件 描述
recycle-view 组件 长列表组件
recycle-item 组件 长列表每一项 item 组件
index.js 提供操作长列表数据的API

使用方法

  1. 安装组件
npm install --save miniprogram-recycle-view
  1. 在页面的 json 配置文件中添加 recycle-view 和 recycle-item 自定义组件的配置

    {
      "usingComponents": {
        "recycle-view": "miniprogram-recycle-view/recycle-view",
        "recycle-item": "miniprogram-recycle-view/recycle-item"
      }
    }
  2. WXML 文件中引用 recycle-view

    <recycle-view batch="{{batchSetRecycleData}}" id="recycleId">
      <view slot="before">长列表前面的内容</view>
      <recycle-item wx:for="{{recycleList}}" wx:key="id">
        <view>
            <image style='width:80px;height:80px;float:left;' src="{{item.image_url}}"></image>
          {{item.idx+1}}. {{item.title}}
        </view>
      </recycle-item>
      <view slot="after">长列表后面的内容</view>
    </recycle-view>

    recycle-view 的属性介绍如下:

    字段名 类型 必填 描述
    id String id必须是页面唯一的字符串
    batch Boolean 必须设置为{{batchSetRecycleData}}才能生效
    height Number 设置recycle-view的高度,默认为页面高度
    width Number 设置recycle-view的宽度,默认是页面的宽度
    enable-back-to-top Boolean 默认为false,同scroll-view同名字段
    scroll-top Number 默认为false,同scroll-view同名字段
    scroll-y Number 默认为true,同scroll-view同名字段
    scroll-to-index Number 设置滚动到长列表的项
    placeholder-image String 默认占位背景图片,在渲染不及时的时候显示,不建议使用大图作为占位。建议传入SVG的Base64格式,可使用工具将SVG代码转为Base64格式。支持SVG中设置rpx。
    scroll-with-animation Boolean 默认为false,同scroll-view的同名字段
    lower-threshold Number 默认为false,同scroll-view同名字段
    upper-threshold Number 默认为false,同scroll-view同名字段
    bindscroll 事件 同scroll-view同名字段
    bindscrolltolower 事件 同scroll-view同名字段
    bindscrolltoupper 事件 同scroll-view同名字段

    recycle-view 包含3个 slot,具体介绍如下:

    名称 描述
    before 默认 slot 的前面的非回收区域
    默认 slot 长列表的列表展示区域,recycle-item 必须定义在默认 slot 中
    after 默认 slot 的后面的非回收区域

    ​ 长列表的内容实际是在一个 scroll-view 滚动区域里面的,当长列表里面的内容,不止是单独的一个列表的时候,例如我们页面底部都会有一个 copyright 的声明,我们就可以把这部分的内容放在 before 和 after 这2个 slot 里面。

    recycle-item 的介绍如下:

    ​ 需要注意的是,recycle-item 中必须定义 wx:for 列表循环,不应该通过 setData 来设置 wx:for 绑定的变量,而是通过createRecycleContext方法创建RecycleContext对象来管理数据,createRecycleContext在 index.js 文件里面定义。建议同时设置 wx:key,以提升列表的渲染性能。

  3. 页面 JS 管理 recycle-view 的数据

    const createRecycleContext = require('miniprogram-recycle-view')
    Page({
        onReady: function() {
            var ctx = createRecycleContext({
              id: 'recycleId',
              dataKey: 'recycleList',
              page: this,
              itemSize: { // 这个参数也可以直接传下面定义的this.itemSizeFunc函数
                width: 162,
                height: 182
              }
            })
            ctx.append(newList)
            // ctx.update(beginIndex, list)
            // ctx.destroy()
        },
        itemSizeFunc: function (item, idx) {
            return {
                width: 162,
                height: 182
            }
        }
    })

    typescript支持,使用如下方式引入

    import * as createRecycleContext from 'miniprogram-recycle-view';

    ​ 页面必须通过 Component 构造器定义,页面引入了miniprogram-recycle-view包之后,会在 wx 对象下面新增接口createRecycleContext函数创建RecycleContext对象来管理 recycle-view 定义的的数据,createRecycleContext接收类型为1个 Object 的参数,Object 参数的每一个 key 的介绍如下:

    参数名 类型 描述
    id String 对应 recycle-view 的 id 属性的值
    dataKey String 对应 recycle-item 的 wx:for 属性设置的绑定变量名
    page Page/Component recycle-view 所在的页面或者组件的实例,页面或者组件内可以直接传 this
    itemSize Object/Function 此参数用来生成recycle-item的宽和高,前面提到过,要知道当前需要渲染哪些item,必须知道item的宽高才能进行计算
    Object必须包含{width, height}两个属性,Function的话接收item, index这2个参数,返回一个包含{width, height}的Object
    itemSize如果是函数,函数里面this指向RecycleContext
    如果样式使用了rpx,可以通过transformRpx来转化为px。
    为Object类型的时候,还有另外一种用法,详细情况见下面的itemSize章节的介绍。
    useInPage Boolean 是否整个页面只有recycle-view。Page的定义里面必须至少加空的onPageScroll函数,主要是用在页面级别的长列表,并且需要用到onPullDownRefresh的效果。切必须设置root参数为当前页面对象
    root Page 当前页面对象,可以通过getCurrentPages获取, 当useInPage为true必须提供

    RecycleContext 对象提供的方法有:

    方法 参数 说明
    append list, callback 在当前的长列表数据上追加list数据,callback是渲染完成的回调函数
    splice begin, count, list, callback 插入/删除长列表数据,参数同Array的splice函数,callback是渲染完成的回调函数
    update begin, list, callback 更新长列表的数据,从索引参数begin开始,更新为参数list,参数callback同splice。
    destroy 销毁RecycleContext对象,在recycle-view销毁的时候调用此方法
    forceUpdate callback, reinitSlot 重新渲染recycle-view。callback是渲染完成的回调函数,当before和after这2个slot的高度发生变化时候调用此函数,reinitSlot设置为true。当item的宽高发生变化的时候也需要调用此方法。
    getBoundingClientRect index 获取某个数据项的在长列表中的位置,返回{left, top, width, height}的Object。
    getScrollTop 获取长列表的当前的滚动位置。
    transformRpx rpx 将rpx转化为px,返回转化后的px整数。itemSize返回的宽高单位是px,可以在这里调用此函数将rpx转化为px,参数是Number,例如ctx.transformRpx(140),返回70。注意,transformRpx会进行四舍五入,所以transformRpx(20) + transformRpx(90)不一定等于transformRpx(110)
    getViewportItems inViewportPx 获取在视窗内的数据项,用于判断某个项是否出现在视窗内。用于曝光数据上报,菜品和类别的联动效果实现。参数inViewportPx表示距离屏幕多少像素为出现在屏幕内,可以为负值。
    getList 获取到完整的数据列表

    itemSize使用

    itemSize可以为包含{width, height}的Object,所有数据只有一种宽高信息。如果有多种,则可以提供一个函数,长列表组件会调用这个函数生成每条数据的宽高信息,如下所示:

    function(item, index) {
        return {
            width: 195,
            height: item.azFirst ? 130 : 120
        }
    }

    Tips

    1. recycle-view设置batch属性的值必须为{{batchSetRecycleData}}。
    2. recycle-item的宽高必须和itemSize设置的宽高一致,否则会出现跳动的bug。
    3. recycle-view设置的高度必须和其style里面设置的样式一致。
    4. createRecycleContext(options)的id参数必须和recycle-view的id属性一致,dataKey参数必须和recycle-item的wx:for绑定的变量名一致。
    5. 不能在recycle-item里面使用wx:for的index变量作为索引值的,请使用{{item.__index__}}替代。
    6. 不要通过setData设置recycle-item的wx:for的变量值,建议recycle-item设置wx:key属性。
    7. 如果长列表里面包含图片,必须保证图片资源是有HTTP缓存的,否则在滚动过程中会发起很多的图片请求。
    8. 有些数据不一定会渲染出来,使用wx.createSelectorQuery的时候有可能会失效,可使用RecycleContext的getBoundingClientRect来替代。
    9. 当使用了useInPage参数的时候,必须在Page里面定义onPageScroll事件。
  4. transformRpx会进行四舍五入,所以transformRpx(20) + transformRpx(90)不一定等于transformRpx(110)

  5. 如果一个页面有多个长列表,必须多设置batch-key属性,每个的batch-key的值和batch属性的变量必须不一致。例如

<recycle-view batch="{{batchSetRecycleData}}" batch-key="batchSetRecycleData"></recycle-view>
<recycle-view batch="{{batchSetRecycleData1}}" batch-key="batchSetRecycleData1"></recycle-view>

recycle-view's People

Contributors

cunjinli6 avatar juneandgreen avatar landn172 avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

recycle-view's Issues

useInPage=false, 使用tabBar时,默认高度错误

useInPage=false, 使用tabBar时,初始获取到的windowHeight不包括tabBar高度,当路由跳转到recycle-view页面,且本页面为非tabbar页面时,底部会有空白.
建议在页面生命周期起始时获取windowHeight

2018-11-30 11 10 23

求实现一个案例

作者可否再把文档写的详细一些,再建一个仓库实现一个渲染1000张图片或者视频小程序的案例,感谢!!!!

自动生成宽高 bug

recycle-context.js

RecycleContext.prototype._recalculateSize = function (list) {
...
    if (offsetLeft + itemSize.width > compData.width) {
      offsetLeft = itemSize.width;
      offsetTop += sizeArray[sizeArray.length - 2].height; // 加上最后一个数据的高度

加上最后一个数据的高度 应该是 sizeArray.length - 1 吧?

以下文件没有被打包上传:

以下文件没有被打包上传:
· node_modules/miniprogram-recycle-view/.babelrc
· node_modules/miniprogram-recycle-view/.eslintrc.js
· node_modules/miniprogram-recycle-view/LICENSE
· node_modules/miniprogram-recycle-view/README.md
· node_modules/miniprogram-recycle-view/gulpfile.js
· node_modules/miniprogram-recycle-view/package.json
· node_modules/miniprogram-recycle-view/images/js.png
· node_modules/miniprogram-recycle-view/images/recycle-view.bmpr
· node_modules/miniprogram-recycle-view/images/recycle-view.png
· node_modules/miniprogram-recycle-view/images/wxml.png
· node_modules/miniprogram-recycle-view/miniprogram_dist/index.js
· node_modules/miniprogram-recycle-view/miniprogram_dist/recycle-item.js
· node_modules/miniprogram-recycle-view/miniprogram_dist/recycle-item.wxml
· node_modules/miniprogram-recycle-view/miniprogram_dist/recycle-item.json
· node_modules/miniprogram-recycle-view/miniprogram_dist/recycle-item.wxss
· node_modules/miniprogram-recycle-view/miniprogram_dist/recycle-view.js
· node_modules/miniprogram-recycle-view/miniprogram_dist/recycle-view.json
· node_modules/miniprogram-recycle-view/miniprogram_dist/recycle-view.wxml
· node_modules/miniprogram-recycle-view/miniprogram_dist/recycle-view.wxss
· node_modules/miniprogram-recycle-view/src/index.js
· node_modules/miniprogram-recycle-view/src/recycle-item.js
· node_modules/miniprogram-recycle-view/src/recycle-item.json
· node_modules/miniprogram-recycle-view/src/recycle-item.wxml
· node_modules/miniprogram-recycle-view/src/recycle-item.wxss
· node_modules/miniprogram-recycle-view/src/recycle-view.js
· node_modules/miniprogram-recycle-view/src/recycle-view.wxml
· node_modules/miniprogram-recycle-view/src/recycle-view.json
· node_modules/miniprogram-recycle-view/src/recycle-view.wxss
· node_modules/miniprogram-recycle-view/src/utils/recycle-context.js
· node_modules/miniprogram-recycle-view/src/utils/recycle-data.js
· node_modules/miniprogram-recycle-view/src/utils/transformRpx.js
· node_modules/miniprogram-recycle-view/src/utils/viewport-change-func.js
· node_modules/

开发工具不能打包

为什么需要基础库2.2.2?

大致看过遍代码好像没有发现需要2.2.2才需要使用的api, 将代码下下来后有自己在1.9.X上尝试,没遇见报错,可以直接在 1.9以上办的版本使用么

抖动问题

滚动的过程中会有抖动是innerBeforeHeight值计算不准还是滚动过程中改变top值本身就会出现抖动呢?

可以暴露一下 recycleData[id].list吗

  1. 我需要在list中间插入若干数据(该list是需要sort),所以需要得知splice对应的index
 recycleData[id].list = [{id: '204'}, {id: '307'}];
此时我需要插入{id: '207'}, 则需要得知我应splice到第一个元素之后,但是目前我没法获取到该list
  1. 问一下通过bindscrolltoupper 触发 splice向头部添加数据 view层是滚动到头部还是保持当前的位置(在上面影藏部分添加数据,显示部分的数据不变动)

固定顶部

想将一段区域固定在顶部使用 slot="before",position:fixed,固定,发现数据多了,dom回收后顶部区域不可显,跑到屏幕区域外了,

dataKey能否改为支持一个字符串数组?

我现在在做一个瀑布流的组件,想集成这个recycle-view,就有一点问题了,我的思路是通过在recycle-view下多列布局,每列放置一个recycle-item循环的组件,然后每次去向最短的那一列增加数据。
实际做的时候发现createRecycleContext里的datakey似乎不支持多个,有办法解决么?
或者能否给个修改的建议呢

关于快速滚动白屏问题

发现开始的节流有问题,暂时没找到scroll-view的节流字段,然后亲测发现确实也有问题,现在我改成下面这样子发现快速滚动的时候白屏时间短了很多,不知道这样改行不行
image

初始化context时候无法直接使用 transformRpx

代码如下:
var ctx = createRecycleContext({
id: 'recycleId',
dataKey: 'recycleList',
page: this,
itemSize: { // 这个参数也可以直接传下面定义的this.itemSizeFunc函数
// 无法直接使用 transformRpx
width: ctx.transformRpx(750),
height: ctx.transformRpx(120)
}
})
需要使用 定义函数,但是函数需要对每行数据都执行一次 transformRpx,会降低性能
建议同时 直接将 transformRpx导出而不是仅仅 挂载在对象上

动态高度支持的有问题

每一项高度是动态的,这时候slot为after是有问题的, 由于高度计算的问题会导致这个after元素显示到列表的里面,而不是如期待的出现在列表的下面(最后一项)

我在这里加个after,显示会异常 。

列表项删除,index变更问题

现象:
有个总共110条数据的长列表,滑到最底下,视图对应的recycleList有60条。
现在想左滑删除一条内容,通过touchMove拿到的index是视图短列表的55,但是ctx.splice要用的index应该是完整列表的index,于是通过item.__index__拿到105,调用ctx.splice(105,1)删除;
但是删除以后,后面的__index__并不会更新,再通过item.__index__获取就不准确了。比如新的长列表实际index 105,他的item.__index__是106。

疑问:
有没有更新__index__的方法?
或者有没有更好的删除方法?

好像没有一个正确清除数组的方法

有需求是通过tab去渲染不同的list 用的其实还是一个组件 组件一直没有销毁 动态的修改list数据
尝试了用update和splice都没有办法清除数组

getList bug report

当没有数据时 调用getList会出现panic 因为此时还没有数据 应直接返回[]

禁用recycle-view组件的滚动

如果我要实现小程序控制scroll-view水平或纵向的滚动,有这样一个场景,出现弹窗蒙层时,不让蒙层之下的组件滚动,原来scroll-view可以设置true或false来实现,那现在这个recycle-view应该怎么来实现?

数据错乱问题

copy了demo的完整示例,npm引入的recycle-view库,多次上滑加载更多数据之后,再滑动到列表顶部,数据会错乱(比如一开始列表第一条【1.test测试数据】,滑动回去后会变成【37.test测试数据】),麻烦查看是示例demo有问题还是库本身的问题。谢谢。

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. 📊📈🎉

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google ❤️ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.