{"id":18938196,"url":"https://github.com/wechat-miniprogram/recycle-view","last_synced_at":"2025-04-07T17:08:16.877Z","repository":{"id":44894726,"uuid":"142132008","full_name":"wechat-miniprogram/recycle-view","owner":"wechat-miniprogram","description":"recycle-view: a wechat miniprogram custom component","archived":false,"fork":false,"pushed_at":"2023-09-11T11:40:37.000Z","size":556,"stargazers_count":635,"open_issues_count":37,"forks_count":72,"subscribers_count":15,"default_branch":"master","last_synced_at":"2024-10-29T21:20:36.864Z","etag":null,"topics":[],"latest_commit_sha":null,"homepage":"","language":"JavaScript","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"mit","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/wechat-miniprogram.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":null,"license":"LICENSE","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null}},"created_at":"2018-07-24T08:55:16.000Z","updated_at":"2024-10-24T02:58:18.000Z","dependencies_parsed_at":"2024-06-18T17:01:14.880Z","dependency_job_id":"0446ede5-29b0-4f6a-b1a7-cf6f69e04185","html_url":"https://github.com/wechat-miniprogram/recycle-view","commit_stats":null,"previous_names":[],"tags_count":0,"template":false,"template_full_name":null,"repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/wechat-miniprogram%2Frecycle-view","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/wechat-miniprogram%2Frecycle-view/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/wechat-miniprogram%2Frecycle-view/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/wechat-miniprogram%2Frecycle-view/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/wechat-miniprogram","download_url":"https://codeload.github.com/wechat-miniprogram/recycle-view/tar.gz/refs/heads/master","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":247694876,"owners_count":20980733,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2022-07-04T15:15:14.044Z","host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":[],"created_at":"2024-11-08T12:13:41.700Z","updated_at":"2025-04-07T17:08:16.834Z","avatar_url":"https://github.com/wechat-miniprogram.png","language":"JavaScript","readme":"# recycle-view\n\n小程序自定义组件\n\n\u003e 使用此组件需要依赖小程序基础库 2.2.2 版本，同时依赖开发者工具的 npm 构建。具体详情可查阅[官方 npm 文档](https://developers.weixin.qq.com/miniprogram/dev/devtools/npm.html)。\n\n## 背景\n\n​ 电商小程序往往需要展示很多商品，当一个页面展示很多的商品信息的时候，会造成小程序页面的卡顿以及白屏。原因有如下几点：\n\n1. 商品列表数据很大，首次 setData 的时候耗时高\n2. 渲染出来的商品列表 DOM 结构多，每次 setData 都需要创建新的虚拟树、和旧树 diff 操作耗时都比较高\n3. 渲染出来的商品列表 DOM 结构多，占用的内存高，造成页面被系统回收的概率变大。\n\n因此实现长列表组件来解决这些问题。\n\n## 实现思路\n\n​ 核心的思路就是只渲染显示在屏幕的数据，基本实现就是监听 scroll 事件，并且重新计算需要渲染的数据，不需要渲染的数据留一个空的 div 占位元素。\n\n​ 假设列表数据有100个 item，知道了滚动的位置，怎么知道哪些 item 必须显示在页面？因为 item 还没渲染出来，不能通过 getComputedStyle 等 DOM 操作得到每个 item 的位置，所以无法知道哪些 item 需要渲染。为了解决这个问题，需要每个 item 固定宽高。item 的宽高的定义见下面的 API 的`createRecycleContext()`的参数 itemSize 的介绍。\n\n​ 滚动过程中，重新渲染数据的同时，需要设置当前数据的前后的 div 占位元素高度，同时是指在同一个渲染周期内。页面渲染是通过 setData 触发的，列表数据和 div 占位高度在2个组件内进行 setData 的，为了把这2个 setData 放在同一个渲染周期，用了一个 hack 方法，所以定义 recycle-view 的 batch 属性固定为`batch=\"{{batchSetRecycleData}}\"`。\n\n​ 在滚动过程中，为了避免频繁出现白屏，会多渲染当前屏幕的前后2个屏幕的内容。\n\n## 包结构\n\n长列表组件由2个自定义组件 recycle-view、recycle-item 和一组 API 组成，对应的代码结构如下\n\n```yaml\n├── miniprogram-recycle-view/\n    └── recycle-view 组件\n    └── recycle-item 组件\n    └── index.js\n```\n\n包结构详细描述如下：\n\n| 目录/文件          | 描述                     |\n| ----------------- | ------------------------ |\n| recycle-view 组件 | 长列表组件                |\n| recycle-item 组件 | 长列表每一项 item 组件     |\n| index.js          | 提供操作长列表数据的API    |\n\n## 使用方法\n\n1. 安装组件\n\n```\nnpm install --save miniprogram-recycle-view\n```\n\n2. 在页面的 json 配置文件中添加 recycle-view 和 recycle-item 自定义组件的配置\n\n   ```json\n   {\n     \"usingComponents\": {\n       \"recycle-view\": \"miniprogram-recycle-view/recycle-view\",\n       \"recycle-item\": \"miniprogram-recycle-view/recycle-item\"\n     }\n   }\n   ```\n\n3. WXML 文件中引用 recycle-view\n\n   ```xml\n   \u003crecycle-view batch=\"{{batchSetRecycleData}}\" id=\"recycleId\"\u003e\n     \u003cview slot=\"before\"\u003e长列表前面的内容\u003c/view\u003e\n     \u003crecycle-item wx:for=\"{{recycleList}}\" wx:key=\"id\"\u003e\n       \u003cview\u003e\n           \u003cimage style='width:80px;height:80px;float:left;' src=\"{{item.image_url}}\"\u003e\u003c/image\u003e\n         {{item.idx+1}}. {{item.title}}\n       \u003c/view\u003e\n     \u003c/recycle-item\u003e\n     \u003cview slot=\"after\"\u003e长列表后面的内容\u003c/view\u003e\n   \u003c/recycle-view\u003e\n   ```\n\n   **recycle-view 的属性介绍如下：**\n\n   | 字段名                | 类型    | 必填 | 描述                                      |\n   | --------------------- | ------- | ---- | ----------------------------------------- |\n   | id                    | String  | 是   | id必须是页面唯一的字符串                  |\n   | batch                 | Boolean | 是   | 必须设置为{{batchSetRecycleData}}才能生效 |\n   | height                | Number  | 否   | 设置recycle-view的高度，默认为页面高度    |\n   | width                 | Number  | 否   | 设置recycle-view的宽度，默认是页面的宽度  |\n   | enable-back-to-top    | Boolean | 否   | 默认为false，同scroll-view同名字段        |\n   | scroll-top            | Number  | 否   | 默认为false，同scroll-view同名字段        |\n   | scroll-y              | Number  | 否   | 默认为true，同scroll-view同名字段        |\n   | scroll-to-index       | Number  | 否   | 设置滚动到长列表的项                      |\n   | placeholder-image     | String  | 否   | 默认占位背景图片，在渲染不及时的时候显示，不建议使用大图作为占位。建议传入SVG的Base64格式，可使用[工具](https://codepen.io/jakob-e/pen/doMoML)将SVG代码转为Base64格式。支持SVG中设置rpx。 |\n   | scroll-with-animation | Boolean | 否   | 默认为false，同scroll-view的同名字段      |\n   | lower-threshold       | Number  | 否   | 默认为false，同scroll-view同名字段        |\n   | upper-threshold       | Number  | 否   | 默认为false，同scroll-view同名字段        |\n   | bindscroll            | 事件    | 否   | 同scroll-view同名字段                     |\n   | bindscrolltolower     | 事件    | 否   | 同scroll-view同名字段                     |\n   | bindscrolltoupper     | 事件    | 否   | 同scroll-view同名字段                     |\n\n   **recycle-view 包含3个 slot，具体介绍如下：**\n\n   | 名称      | 描述                                                      |\n   | --------- | --------------------------------------------------------- |\n   | before    | 默认 slot 的前面的非回收区域                              |\n   | 默认 slot | 长列表的列表展示区域，recycle-item 必须定义在默认 slot 中 |\n   | after     | 默认 slot 的后面的非回收区域                              |\n\n   ​  长列表的内容实际是在一个 scroll-view 滚动区域里面的，当长列表里面的内容，不止是单独的一个列表的时候，例如我们页面底部都会有一个 copyright 的声明，我们就可以把这部分的内容放在 before 和 after 这2个 slot 里面。\n\n   **recycle-item 的介绍如下：**\n\n   ​  需要注意的是，recycle-item 中必须定义 wx:for 列表循环，不应该通过 setData 来设置 wx:for 绑定的变量，而是通过`createRecycleContext`方法创建`RecycleContext`对象来管理数据，`createRecycleContext`在 index.js 文件里面定义。建议同时设置 wx:key，以提升列表的渲染性能。\n\n4. 页面 JS 管理 recycle-view 的数据\n\n   ```javascript\n   const createRecycleContext = require('miniprogram-recycle-view')\n   Page({\n       onReady: function() {\n           var ctx = createRecycleContext({\n             id: 'recycleId',\n             dataKey: 'recycleList',\n             page: this,\n             itemSize: { // 这个参数也可以直接传下面定义的this.itemSizeFunc函数\n               width: 162,\n               height: 182\n             }\n           })\n           ctx.append(newList)\n           // ctx.update(beginIndex, list)\n           // ctx.destroy()\n       },\n       itemSizeFunc: function (item, idx) {\n           return {\n               width: 162,\n               height: 182\n           }\n       }\n   })\n   ```\n\n   `typescript`支持,使用如下方式引入\n   ```typescript\n   import * as createRecycleContext from 'miniprogram-recycle-view';\n   ```\n\n   ​  页面必须通过 Component 构造器定义，页面引入了`miniprogram-recycle-view`包之后，会在 wx 对象下面新增接口`createRecycleContext`函数创建`RecycleContext`对象来管理 recycle-view 定义的的数据，`createRecycleContext`接收类型为1个 Object 的参数，Object 参数的每一个 key 的介绍如下：\n\n   | 参数名    | 类型            | 描述                                                             |\n   | -------- | --------------- | --------------------------------------------------------------- |\n   | id       | String          | 对应 recycle-view 的 id 属性的值                                  |\n   | dataKey  | String          | 对应 recycle-item 的 wx:for 属性设置的绑定变量名                   |\n   | page     | Page/Component  | recycle-view 所在的页面或者组件的实例，页面或者组件内可以直接传 this |\n   | itemSize | Object/Function | 此参数用来生成recycle-item的宽和高，前面提到过，要知道当前需要渲染哪些item，必须知道item的宽高才能进行计算\u003cbr /\u003eObject必须包含{width, height}两个属性，Function的话接收item, index这2个参数，返回一个包含{width, height}的Object\u003cbr /\u003eitemSize如果是函数，函数里面`this`指向RecycleContext\u003cbr /\u003e如果样式使用了rpx，可以通过transformRpx来转化为px。\u003cbr /\u003e为Object类型的时候，还有另外一种用法，详细情况见下面的itemSize章节的介绍。 |\n   | useInPage | Boolean | 是否整个页面只有recycle-view。Page的定义里面必须至少加空的onPageScroll函数，主要是用在页面级别的长列表，并且需要用到onPullDownRefresh的效果。切必须设置`root`参数为当前页面对象 |\n   | root | Page | 当前页面对象，可以通过getCurrentPages获取, 当useInPage为true必须提供 |\n\n   RecycleContext 对象提供的方法有：\n\n   | 方法                  | 参数                         | 说明                                                         |\n   | --------------------- | ---------------------------- | ------------------------------------------------------------ |\n   | append                | list, callback               | 在当前的长列表数据上追加list数据，callback是渲染完成的回调函数 |\n   | splice                | begin, count, list, callback | 插入/删除长列表数据，参数同Array的[splice](http://www.w3school.com.cn/js/jsref_splice.asp)函数，callback是渲染完成的回调函数 |\n   | update                | begin, list, callback        | 更新长列表的数据，从索引参数begin开始，更新为参数list，参数callback同splice。 |\n   | destroy               | 无                           | 销毁RecycleContext对象，在recycle-view销毁的时候调用此方法   |\n   | forceUpdate           | callback, reinitSlot         | 重新渲染recycle-view。callback是渲染完成的回调函数，当before和after这2个slot的高度发生变化时候调用此函数，reinitSlot设置为true。当item的宽高发生变化的时候也需要调用此方法。 |\n   | getBoundingClientRect | index                        | 获取某个数据项的在长列表中的位置，返回{left, top, width, height}的Object。 |\n   | getScrollTop          | 无                           | 获取长列表的当前的滚动位置。                                 |\n   | transformRpx          | rpx                          | 将rpx转化为px，返回转化后的px整数。itemSize返回的宽高单位是px，可以在这里调用此函数将rpx转化为px，参数是Number，例如ctx.transformRpx(140)，返回70。注意，transformRpx会进行四舍五入，所以transformRpx(20) + transformRpx(90)不一定等于transformRpx(110) |\n   | getViewportItems      | inViewportPx                 | 获取在视窗内的数据项，用于判断某个项是否出现在视窗内。用于曝光数据上报，菜品和类别的联动效果实现。参数inViewportPx表示距离屏幕多少像素为出现在屏幕内，可以为负值。 |\n    | getList      |       无          | 获取到完整的数据列表 |\n\n   ## itemSize使用\n\n   itemSize可以为包含{width, height}的Object，所有数据只有一种宽高信息。如果有多种，则可以提供一个函数，长列表组件会调用这个函数生成每条数据的宽高信息，如下所示：\n\n   ```javascript\n   function(item, index) {\n       return {\n           width: 195,\n           height: item.azFirst ? 130 : 120\n       }\n   }\n   ```\n\n   \n\n   ## Tips\n\n   1. recycle-view设置batch属性的值必须为{{batchSetRecycleData}}。\n   2. recycle-item的宽高必须和itemSize设置的宽高一致，否则会出现跳动的bug。\n   3. recycle-view设置的高度必须和其style里面设置的样式一致。\n   4. `createRecycleContext(options)`的id参数必须和recycle-view的id属性一致，dataKey参数必须和recycle-item的wx:for绑定的变量名一致。\n   5. 不能在recycle-item里面使用wx:for的index变量作为索引值的，请使用{{item.\\_\\_index\\_\\_}}替代。\n   6. 不要通过setData设置recycle-item的wx:for的变量值，建议recycle-item设置wx:key属性。\n   7. 如果长列表里面包含图片，必须保证图片资源是有HTTP缓存的，否则在滚动过程中会发起很多的图片请求。\n   8. 有些数据不一定会渲染出来，使用wx.createSelectorQuery的时候有可能会失效，可使用RecycleContext的getBoundingClientRect来替代。\n   9. 当使用了useInPage参数的时候，必须在Page里面定义onPageScroll事件。\n  10. transformRpx会进行四舍五入，所以`transformRpx(20) + transformRpx(90)`不一定等于`transformRpx(110)`\n  11. 如果一个页面有多个长列表，必须多设置batch-key属性，每个的batch-key的值和batch属性的变量必须不一致。例如\n  ```html\n  \u003crecycle-view batch=\"{{batchSetRecycleData}}\" batch-key=\"batchSetRecycleData\"\u003e\u003c/recycle-view\u003e\n  \u003crecycle-view batch=\"{{batchSetRecycleData1}}\" batch-key=\"batchSetRecycleData1\"\u003e\u003c/recycle-view\u003e\n  ```","funding_links":[],"categories":[],"sub_categories":[],"project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fwechat-miniprogram%2Frecycle-view","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fwechat-miniprogram%2Frecycle-view","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fwechat-miniprogram%2Frecycle-view/lists"}