{"id":13563994,"url":"https://github.com/starkwang/vue-virtual-collection","last_synced_at":"2026-03-05T21:45:44.084Z","repository":{"id":42078089,"uuid":"121747014","full_name":"starkwang/vue-virtual-collection","owner":"starkwang","description":"Vue component for efficiently rendering large collection data","archived":false,"fork":false,"pushed_at":"2023-04-12T12:37:25.000Z","size":522,"stargazers_count":642,"open_issues_count":9,"forks_count":78,"subscribers_count":5,"default_branch":"master","last_synced_at":"2025-11-27T09:32:17.599Z","etag":null,"topics":["collection","performance","scroll","slot","vue-components"],"latest_commit_sha":null,"homepage":"https://starkwang.github.io/vue-virtual-collection/demo/dist/index.html","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/starkwang.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}},"created_at":"2018-02-16T12:12:32.000Z","updated_at":"2025-11-26T07:50:35.000Z","dependencies_parsed_at":"2023-10-21T07:00:33.424Z","dependency_job_id":null,"html_url":"https://github.com/starkwang/vue-virtual-collection","commit_stats":{"total_commits":60,"total_committers":9,"mean_commits":6.666666666666667,"dds":"0.41666666666666663","last_synced_commit":"f8f5b2e201ad4a1b8556f71c395fdd58febc7a62"},"previous_names":[],"tags_count":0,"template":false,"template_full_name":null,"purl":"pkg:github/starkwang/vue-virtual-collection","repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/starkwang%2Fvue-virtual-collection","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/starkwang%2Fvue-virtual-collection/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/starkwang%2Fvue-virtual-collection/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/starkwang%2Fvue-virtual-collection/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/starkwang","download_url":"https://codeload.github.com/starkwang/vue-virtual-collection/tar.gz/refs/heads/master","sbom_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/starkwang%2Fvue-virtual-collection/sbom","scorecard":{"id":846406,"data":{"date":"2025-08-11","repo":{"name":"github.com/starkwang/vue-virtual-collection","commit":"f8f5b2e201ad4a1b8556f71c395fdd58febc7a62"},"scorecard":{"version":"v5.2.1-40-gf6ed084d","commit":"f6ed084d17c9236477efd66e5b258b9d4cc7b389"},"score":3.4,"checks":[{"name":"Dangerous-Workflow","score":-1,"reason":"no workflows found","details":null,"documentation":{"short":"Determines if the project's GitHub Action workflows avoid dangerous patterns.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#dangerous-workflow"}},{"name":"Code-Review","score":3,"reason":"Found 7/22 approved changesets -- score normalized to 3","details":null,"documentation":{"short":"Determines if the project requires human code review before pull requests (aka merge requests) are merged.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#code-review"}},{"name":"Maintained","score":0,"reason":"0 commit(s) and 0 issue activity found in the last 90 days -- score normalized to 0","details":null,"documentation":{"short":"Determines if the project is \"actively maintained\".","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#maintained"}},{"name":"Token-Permissions","score":-1,"reason":"No tokens found","details":null,"documentation":{"short":"Determines if the project's workflows follow the principle of least privilege.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#token-permissions"}},{"name":"Packaging","score":-1,"reason":"packaging workflow not detected","details":["Warn: no GitHub/GitLab publishing workflow detected."],"documentation":{"short":"Determines if the project is published as a package that others can easily download, install, easily update, and uninstall.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#packaging"}},{"name":"Binary-Artifacts","score":10,"reason":"no binaries found in the repo","details":null,"documentation":{"short":"Determines if the project has generated executable (binary) artifacts in the source repository.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#binary-artifacts"}},{"name":"Pinned-Dependencies","score":-1,"reason":"no dependencies found","details":null,"documentation":{"short":"Determines if the project has declared and pinned the dependencies of its build process.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#pinned-dependencies"}},{"name":"CII-Best-Practices","score":0,"reason":"no effort to earn an OpenSSF best practices badge detected","details":null,"documentation":{"short":"Determines if the project has an OpenSSF (formerly CII) Best Practices Badge.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#cii-best-practices"}},{"name":"Security-Policy","score":0,"reason":"security policy file not detected","details":["Warn: no security policy file detected","Warn: no security file to analyze","Warn: no security file to analyze","Warn: no security file to analyze"],"documentation":{"short":"Determines if the project has published a security policy.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#security-policy"}},{"name":"Vulnerabilities","score":10,"reason":"0 existing vulnerabilities detected","details":null,"documentation":{"short":"Determines if the project has open, known unfixed vulnerabilities.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#vulnerabilities"}},{"name":"License","score":10,"reason":"license file detected","details":["Info: project has a license file: LICENSE:0","Info: FSF or OSI recognized license: MIT License: LICENSE:0"],"documentation":{"short":"Determines if the project has defined a license.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#license"}},{"name":"Fuzzing","score":0,"reason":"project is not fuzzed","details":["Warn: no fuzzer integrations found"],"documentation":{"short":"Determines if the project uses fuzzing.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#fuzzing"}},{"name":"Signed-Releases","score":-1,"reason":"no releases found","details":null,"documentation":{"short":"Determines if the project cryptographically signs release artifacts.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#signed-releases"}},{"name":"Branch-Protection","score":0,"reason":"branch protection not enabled on development/release branches","details":["Warn: branch protection not enabled for branch 'master'"],"documentation":{"short":"Determines if the default and release branches are protected with GitHub's branch protection settings.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#branch-protection"}},{"name":"SAST","score":0,"reason":"SAST tool is not run on all commits -- score normalized to 0","details":["Warn: 0 commits out of 15 are checked with a SAST tool"],"documentation":{"short":"Determines if the project uses static code analysis.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#sast"}}]},"last_synced_at":"2025-08-23T21:33:21.809Z","repository_id":42078089,"created_at":"2025-08-23T21:33:21.809Z","updated_at":"2025-08-23T21:33:21.809Z"},"host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":286080680,"owners_count":30151030,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2026-03-05T21:15:50.531Z","status":"ssl_error","status_checked_at":"2026-03-05T21:15:11.173Z","response_time":93,"last_error":"SSL_connect returned=1 errno=0 peeraddr=140.82.121.6:443 state=error: unexpected eof while reading","robots_txt_status":"success","robots_txt_updated_at":"2025-07-24T06:49:26.215Z","robots_txt_url":"https://github.com/robots.txt","online":false,"can_crawl_api":true,"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":["collection","performance","scroll","slot","vue-components"],"created_at":"2024-08-01T13:01:25.362Z","updated_at":"2026-03-05T21:45:44.047Z","avatar_url":"https://github.com/starkwang.png","language":"JavaScript","funding_links":[],"categories":["JavaScript","UI布局","Components \u0026 Libraries","UI Layout","UI Layout [🔝](#readme)"],"sub_categories":["游览","UI Layout","Tour"],"readme":"# vue-virtual-collection\n\n[![npm version](https://badge.fury.io/js/vue-virtual-collection.svg)](https://badge.fury.io/js/vue-virtual-collection)\n[![Build Status](https://travis-ci.org/starkwang/vue-virtual-collection.svg?branch=master)](https://travis-ci.org/starkwang/vue-virtual-collection)\n[![styled with prettier](https://img.shields.io/badge/styled_with-prettier-ff69b4.svg)](https://github.com/prettier/prettier)\n[![npm](https://img.shields.io/npm/dt/vue-virtual-collection.svg)](https://www.npmjs.com/package/vue-virtual-collection)\n\n\nVue component for efficiently rendering large collection data. Inspired by [react-virtualize](https://github.com/bvaughn/react-virtualized).\n\n[Demo](https://starkwang.github.io/vue-virtual-collection/demo/dist/index.html)\n\n![Demo](https://starkwang.github.io/vue-virtual-collection/img/demo.png)\n\n\u003c!-- START doctoc generated TOC please keep comment here to allow auto update --\u003e\n\u003c!-- DON'T EDIT THIS SECTION, INSTEAD RE-RUN doctoc TO UPDATE --\u003e\n**Table of Contents**  *generated with [DocToc](https://github.com/thlorenz/doctoc)*\n\n- [Usage](#usage)\n  - [Install](#install)\n  - [Import](#import)\n  - [Use it!](#use-it)\n    - [Default Usage](#default-usage)\n    - [Item Grouping](#item-grouping)\n  - [Props](#props)\n    - [cellSizeAndPositionGetter](#cellsizeandpositiongetter)\n    - [collection](#collection)\n    - [width](#width)\n    - [height](#height)\n    - [scrollToBottomRange](#scrolltobottomrange)\n    - [containerPaddingBottom](#containerpaddingbottom)\n    - [headerSlotHeight](#headerslotheight)\n    - [sectionSize](#sectionsize)\n  - [Events](#events)\n    - [scrolled-to-top](#scrolled-to-top)\n    - [scrolled-to-bottom](#scrolled-to-bottom)\n    - [scrolled-to-bottom-range](#scrolled-to-bottom-range)\n  - [Slots](#slots)\n    - [header](#header)\n    - [cell](#cell)\n\n\u003c!-- END doctoc generated TOC please keep comment here to allow auto update --\u003e\n\n# Usage\n\n## Install\n```\nnpm i vue-virtual-collection\n```\n\n## Import\n```js\nimport Vue from 'vue'\nimport VirtualCollection from 'vue-virtual-collection'\n\nVue.use(VirtualCollection)\n```\n\n## Use it!\n\n`vue-virtual-collection` offers two ways of using a `VirtualCollection`.\n\n### Default Usage\n\nThe standard way of using a `VirtualCollection` is to to pass the entire collection of items to it as a single group. This is suitable for most cases, especially if all the items in the collection are either very similar or very different from one another.\n\nIn the sample below, the collection is instantiated as an `Array` and passed directly to the `VirtualCollection` in that form.\n\n```html\n\u003ctemplate\u003e\n    \u003cdiv\u003e\n        \u003cVirtualCollection\n            :cellSizeAndPositionGetter=\"cellSizeAndPositionGetter\"\n            :collection=\"items\"\n            :height=\"500\"\n            :width=\"330\"\n        \u003e\n            \u003cdiv slot=\"cell\" slot-scope=\"props\"\u003e{{props.data}}\u003c/div\u003e\n        \u003c/VirtualCollection\u003e\n    \u003c/div\u003e\n\u003c/template\u003e\n\n\u003cscript\u003e\n    export default {\n        data () {\n            return {\n                /**\n                 * This will create 1000 items like:\n                 * [\n                 *   { data: '#0' },\n                 *   { data: '#1' },\n                 *   ...\n                 *   { data: '#999' }\n                 * ]\n                 */\n                items: Array.from({length: 1000}, (_, index) =\u003e ({ data: '#' + index }))\n            }\n        },\n        methods: {\n            cellSizeAndPositionGetter(item, index) {\n                // compute size and position\n                return {\n                    width: 100,\n                    height: 150,\n                    x: (index % 2) * 110,\n                    y: parseInt(index / 2) * 160\n                }\n            }\n        }\n    }\n\u003c/script\u003e\n```\n\n### Item Grouping\n\nItem grouping is an optional feature that can be used to potentially improve performance, and is ideally suited to cases where the collection of items to render are logically divisable into a small number of different categories that may be updated, added or removed at different rates.\n\nInstead of passing the entire collection as an array to the `VirtualCollection`, the collection is passed as an array of \"groups\" - objects that each contain a subset of the entire collection's items that should logically be handled the same way.\n\nAn example of where this might be appropriate is using `VirtualCollection` to represent a chess game. In this scenario, there might be separate items within the collection to represent the chessboard tiles and the chess pieces. Chessboard tiles and chess pieces are logically distinct categories of items, and each category would update at different rates; the collection of chessboard tiles would never change after being initially rendered, whereas the collection of pieces would change after every move.\n\nWithout grouping, changing the position of a piece (or adding/removing one) would cause everything in the collection to be re-processed and potentially re-rendered, including the chessboard tiles. However, if we pass the pieces and the tiles as separate groups, then `VirtualCollection` will only reprocess the collection of chess pieces after each turn, and skip reprocessing the chessboard tiles.\n\n```html\n\u003ctemplate\u003e\n    \u003cdiv\u003e\n        \u003cVirtualCollection\n            :cellSizeAndPositionGetter=\"cellSizeAndPositionGetter\"\n            :collection=\"collectionGroups\"\n            :height=\"500\"\n            :width=\"500\"\n        \u003e\n            \u003cdiv class=\"grid-cell\" slot=\"cell\" slot-scope=\"{data}\"\u003e\n                \u003cChessTile v-if=\"data.isTile\" /\u003e\n                \u003cChessPiece v-else /\u003e\n            \u003c/div\u003e\n        \u003c/VirtualCollection\u003e\n    \u003c/div\u003e\n\u003c/template\u003e\n\n\u003cscript\u003e\n    export default {\n        data () {\n            return {\n                chessTiles: [ { data: { isTile: true } }, /* ... */ ],\n                chessPieces: [ { data: { isPiece: true } }, /* ... */ ],\n                collectionGroups: [ { group: [] }, { group: [] } ]\n            }\n        },\n        methods: {\n            cellSizeAndPositionGetter(item, itemIndex, groupIndex) {\n                switch (groupIndex)\n                {\n                    case 0:\n                        // Compute size and position for chessboard tile\n                        return { width: 0, height: 0, x: 0, y: 0 }\n                    \n                    case 1:\n                        // Compute size and position for chess piece\n                        return { width: 0, height: 0, x: 0, y: 0 }\n\n                    default:\n                        throw new Error(\"Not a tile or piece\")\n                }\n            }\n        },\n        watch: {\n            chessTiles: function (chessTiles) {\n                this.collectionGroups[0].group = chessTiles;\n            },\n            chessPieces: function (chessPieces) {\n                this.collectionGroups[1].group = chessPieces\n            }\n        }\n    }\n\u003c/script\u003e\n```\n\n## Props\n\n### cellSizeAndPositionGetter\n\nType: `Function`\n\n`(item: object, index: number) -\u003e ({ height: number, width: number, x: number, y: number })`\n\n**Required: ✓**\n\nCallback responsible for returning size and offset/position information for a given cell\n\n```js\nfunction cellSizeAndPositionGetter(item, index) {\n    return {\n        width: item.width,\n        height: item.height,\n        x: item.x,\n        y: item.y\n    }\n}\n```\n\n**Alternate usage**\n\nIf using the item grouping feature, the second parameter is the index of the item within the group it belongs to and a third parameter represents the index of the group within the collection of groups passed to `VirtualCollection`.\n\n`(item: object, itemIndex: number, groupIndex: number) -\u003e ({ height: number, width: number, x: number, y: number })`\n\n```js\nfunction cellSizeAndPositionGetter(item, itemIndex, groupIndex) {\n    return {\n        width: item.width,\n        height: item.height,\n        x: item.x,\n        y: item.y\n    }\n}\n```\n\n### collection\n\nType: `Array`\n\n**Required: ✓**\n\nThe data for cells to render. Each object in array must contain a `data` property, which will be passed into slot scope.\n\n```js\nconst collection = [\n    { data: { text: \"#1\" } },\n    { data: { text: \"#2\" } },\n    { data: { text: \"#3\" } },\n    // ...\n]\n```\n\n**Alternate usage**\n\nIf using the item grouping feature, each element of the collection should be an object with a `group` property representing a group of logically-related items. Each item of each group must contain a `data` property, which will be passed into slot scope.\n\n```js\nconst collection = [\n    {\n        group: [\n            { data: { text: \"#1\" } },\n            { data: { text: \"#2\" } },\n            { data: { text: \"#3\" } },\n            // ...\n        ]\n    },\n    {\n        group: [\n            { data: { text: \"#A\" } },\n            { data: { text: \"#B\" } },\n            { data: { text: \"#C\" } },\n            // ...\n        ]\n    },\n    // ...\n]\n```\n\n### width\n\nType: `number`\n\n**Required: ✓**\n\nThe width of collection viewport\n\n### height\n\nType: `number`\n\n**Required: ✓**\n\nThe height of collection viewport\n\n### scrollToBottomRange\n\nType: `number`\n\nDefault: undefined \n\nWhen present the component will emit `scrolled-to-bottom-range` when the bottom is \u003e= 1 and the number provided.\nThe `scrolled-to-bottom` will still be fired, and the 2 events will not be emitted at the same time.\n\n### containerPaddingBottom\n\nType: `number`\n\nDefault: 0\n\nOptionally extend the calculated height of the collection container, the affect is apparent padding-bottom\n\n### headerSlotHeight\n\nType: `number`\n\nDefault: 0\n\nWhen injecting content into the header slot, you should also add the px height of the slot. This ensure items are removed from view at the right time.\n\n### sectionSize\n\nType: `number`\n\nDefault: 300\n\nOptionally override the size of the sections a Collection's cells are split into. This is an advanced option and should only be used for performance tuning purposes.\n\n## Events\n\n### scrolled-to-top\nThis event is emitted when the container scrollTop is reduced to 0.\n\n### scrolled-to-bottom\nThis event is emitted when the container scrollTop has reach the bottom.\n\n### scrolled-to-bottom-range\nThis event is emitted only when a `scrollToBottomRange` value is provided, and is fired when the container is scroller with range of the bottom, the range defined by the said prop.\n\n## Slots\n\n### header\nThe header slot allows you to simulate a full-page mode for the virtual-scroller content. By setting the VirtualCollection height and width to be that of the browser window, the header will then sit on top of the scrollable items however will move out of view when the VirtualCollection is scrolled down.\n\n```html\n\u003cVirtualCollection\n    :cellSizeAndPositionGetter=\"item =\u003e { return { width: item.width, height: item.height, x: item.x, y: item.y }}\"\n    :collection=\"items.items\"\n    :height=\"items.boxHeight\"\n    :width=\"items.boxWidth\"\n    :containerHeightSpacer=\"50\"\n    v-on:scrolled-to-top=\"scrollTop\"\n    v-on:scrolled-to-bottom=\"scrollBottom\"\u003e\n  \u003ctemplate v-slot:header\u003e\n    \u003cdiv\u003e\n        This content will sit on top of the scrollable items acting as a header.\n    \u003c/div\u003e\n  \u003c/template\u003e\n  \u003cdiv slot=\"cell\" slot-scope=\"props\"\u003e\n      {{props.data}}\n  \u003c/div\u003e\n\u003c/VirtualCollection\u003e\n```\n\n### cell\n```html\n\u003cdiv slot=\"cell\" slot-scope=\"yourOwnScope\"\u003e{{yourOwnScope.data.text}}\u003c/div\u003e\n```\n\n*The `data` property in items of `collection` will be passed into the slot scope.*\n```js\nconst collection = [\n    { data: { text: \"#1\" } },\n    { data: { text: \"#2\" } },\n    { data: { text: \"#3\" } },\n    // ...\n]\n```\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fstarkwang%2Fvue-virtual-collection","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fstarkwang%2Fvue-virtual-collection","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fstarkwang%2Fvue-virtual-collection/lists"}