mirror of
https://github.com/tenrok/vue-select.git
synced 2026-06-22 10:30:34 +03:00
add tutorial on working with large datasets
This commit is contained in:
@@ -0,0 +1,116 @@
|
|||||||
|
<template>
|
||||||
|
<div class="flex">
|
||||||
|
<div class="airport-select default">
|
||||||
|
<table>
|
||||||
|
<tr>
|
||||||
|
<th colspan="2">Without Optimization 😬</th>
|
||||||
|
</tr>
|
||||||
|
<tr>
|
||||||
|
<td>Dropdown Render</td>
|
||||||
|
<td :class="{ slow: !! timing.default }">{{ timing.default }}ms</td>
|
||||||
|
</tr>
|
||||||
|
</table>
|
||||||
|
<observed-select
|
||||||
|
data-type="default"
|
||||||
|
placeholder="Search for a destination..."
|
||||||
|
:options="options"
|
||||||
|
:get-option-label="({icao, name}) => `${icao} - ${name}`"
|
||||||
|
></observed-select>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="airport-select optimized">
|
||||||
|
<table>
|
||||||
|
<tr>
|
||||||
|
<th colspan="2">With Optimization 🤓</th>
|
||||||
|
</tr>
|
||||||
|
<tr>
|
||||||
|
<td>Dropdown Render</td>
|
||||||
|
<td :class="{ fast: !! timing.optimized }">{{ timing.optimized }}ms</td>
|
||||||
|
</tr>
|
||||||
|
</table>
|
||||||
|
<observed-select
|
||||||
|
data-type="optimized"
|
||||||
|
placeholder="Search for a destination..."
|
||||||
|
:options="options"
|
||||||
|
:filter="filter"
|
||||||
|
:loading="loading"
|
||||||
|
:get-option-label="({icao, name}) => `${icao} - ${name}`"
|
||||||
|
></observed-select>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script>
|
||||||
|
import Vue from 'vue';
|
||||||
|
import airports from 'airport-data';
|
||||||
|
import vSelect from '../../../src/components/Select'
|
||||||
|
|
||||||
|
const timings = Vue.observable({
|
||||||
|
optimized: 0,
|
||||||
|
default: 0
|
||||||
|
});
|
||||||
|
|
||||||
|
const ObservedSelect = {...vSelect};
|
||||||
|
|
||||||
|
ObservedSelect.watch.dropdownOpen = function (open) {
|
||||||
|
if (open) {
|
||||||
|
const start = performance.now();
|
||||||
|
this.$nextTick(() => {
|
||||||
|
timings[this.$el.dataset.type] = performance.now() - start;
|
||||||
|
})
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
export default {
|
||||||
|
name: "AirportCodes",
|
||||||
|
components: {ObservedSelect},
|
||||||
|
data: () => ({loading: false}),
|
||||||
|
methods: {
|
||||||
|
filter (airports, search) {
|
||||||
|
if (search.length === 0) {
|
||||||
|
return airports.slice(0, 50);
|
||||||
|
}
|
||||||
|
return airports.filter(airport => {
|
||||||
|
const keys = ['name', 'city', 'country', 'iata', 'icao'];
|
||||||
|
return keys.some(key => String(airport[key]).toLowerCase().includes(search.toLowerCase()))
|
||||||
|
}).slice(0, 50);
|
||||||
|
}
|
||||||
|
},
|
||||||
|
computed: {
|
||||||
|
options: () => airports,
|
||||||
|
timing: () => timings,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<style scoped>
|
||||||
|
table {
|
||||||
|
display: table;
|
||||||
|
width: 100%;
|
||||||
|
}
|
||||||
|
|
||||||
|
.flex {
|
||||||
|
display: flex;
|
||||||
|
}
|
||||||
|
|
||||||
|
.airport-select {
|
||||||
|
width: 50%;
|
||||||
|
}
|
||||||
|
|
||||||
|
.airport-select + .airport-select {
|
||||||
|
margin-left: 1rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
.fast,
|
||||||
|
.slow {
|
||||||
|
font-weight: bold;
|
||||||
|
}
|
||||||
|
|
||||||
|
.slow {
|
||||||
|
color: red;
|
||||||
|
}
|
||||||
|
|
||||||
|
.fast {
|
||||||
|
color: #3eaf7c;
|
||||||
|
}
|
||||||
|
</style>
|
||||||
@@ -122,6 +122,7 @@ module.exports = {
|
|||||||
['guide/vuex', 'Vuex'],
|
['guide/vuex', 'Vuex'],
|
||||||
['guide/ajax', 'AJAX'],
|
['guide/ajax', 'AJAX'],
|
||||||
['guide/loops', 'Using in Loops'],
|
['guide/loops', 'Using in Loops'],
|
||||||
|
['guide/bigdata', 'Large Datasets'],
|
||||||
],
|
],
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
|
|||||||
@@ -1,6 +1,9 @@
|
|||||||
import vSelect from '../../src/components/Select';
|
import vSelect from '../../src/components/Select';
|
||||||
|
|
||||||
export default ({Vue, options, router, siteData}) => {
|
export default ({Vue, options, router, siteData}) => {
|
||||||
|
// Vue.config.performance = true;
|
||||||
|
Vue.config.devtools = true;
|
||||||
|
|
||||||
Vue.component('v-select', vSelect);
|
Vue.component('v-select', vSelect);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|||||||
@@ -0,0 +1,38 @@
|
|||||||
|
# Working with Big Datasets
|
||||||
|
|
||||||
|
When your `options` array gets into the thousands, you will start to see performance dip. If you
|
||||||
|
have 2,000 options, you have 2,000 dom nodes being rendered into the dropdown. It's very difficult
|
||||||
|
to render that many nodes at once and maintain 60fps in the browser.
|
||||||
|
|
||||||
|
Regardless of whether your options are primitives or objects, the performance hit comes from
|
||||||
|
rendering their text labels (or slot) to the dom. The path to keeping things snappy is then to limit
|
||||||
|
the amount of nodes that get rendered to the dropdown.
|
||||||
|
|
||||||
|
## Optimized Filtering
|
||||||
|
|
||||||
|
You can optimize performance by tweaking Vue Select's filtering and implementing it yourself.
|
||||||
|
Consider a list of airport codes with roughly ~7,500 entries. Rendering out 7,500 airports doesn't
|
||||||
|
actually help the user much, they're just looking for one in particular.
|
||||||
|
|
||||||
|
<AirportCodes />
|
||||||
|
|
||||||
|
The only difference between these two examples is the `filter` prop. Here's the `filter` for the
|
||||||
|
optimized example:
|
||||||
|
|
||||||
|
```js
|
||||||
|
filter (airports, search) {
|
||||||
|
if (search.length === 0) {
|
||||||
|
return airports.slice(0, 50);
|
||||||
|
}
|
||||||
|
return airports.filter(airport => {
|
||||||
|
const keys = ['name', 'city', 'country', 'iata', 'icao'];
|
||||||
|
return keys.some(key => String(airport[key]).toLowerCase().includes(search.toLowerCase()))
|
||||||
|
}).slice(0, 50);
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
If there's no search text, we only display the first 50 options. If there is a search query, we'll
|
||||||
|
check if any keys on the object include the search string. Again, we'll limit this to 50 options.
|
||||||
|
|
||||||
|
The key to have Vue Select perform well with thousands of options is to limit the number of options
|
||||||
|
that are displayed to the end user, without hindering their ability to select the right option.
|
||||||
@@ -14,6 +14,7 @@
|
|||||||
"@vuepress/plugin-pwa": "^1.0.0-alpha.47",
|
"@vuepress/plugin-pwa": "^1.0.0-alpha.47",
|
||||||
"@vuepress/plugin-register-components": "^1.0.0-alpha.47",
|
"@vuepress/plugin-register-components": "^1.0.0-alpha.47",
|
||||||
"@vuepress/plugin-search": "^1.0.0-alpha.47",
|
"@vuepress/plugin-search": "^1.0.0-alpha.47",
|
||||||
|
"airport-data": "^1.0.1",
|
||||||
"cross-env": "^5.2.0",
|
"cross-env": "^5.2.0",
|
||||||
"fuse.js": "^3.4.4",
|
"fuse.js": "^3.4.4",
|
||||||
"gh-pages": "^0.11.0",
|
"gh-pages": "^0.11.0",
|
||||||
|
|||||||
@@ -1161,6 +1161,11 @@ agentkeepalive@^2.2.0:
|
|||||||
resolved "https://registry.yarnpkg.com/agentkeepalive/-/agentkeepalive-2.2.0.tgz#c5d1bd4b129008f1163f236f86e5faea2026e2ef"
|
resolved "https://registry.yarnpkg.com/agentkeepalive/-/agentkeepalive-2.2.0.tgz#c5d1bd4b129008f1163f236f86e5faea2026e2ef"
|
||||||
integrity sha1-xdG9SxKQCPEWPyNvhuX66iAm4u8=
|
integrity sha1-xdG9SxKQCPEWPyNvhuX66iAm4u8=
|
||||||
|
|
||||||
|
airport-data@^1.0.1:
|
||||||
|
version "1.0.1"
|
||||||
|
resolved "https://registry.yarnpkg.com/airport-data/-/airport-data-1.0.1.tgz#41c743c48fc5b37765a1bd3cf0617c1de8151316"
|
||||||
|
integrity sha1-QcdDxI/Fs3dlob088GF8HegVExY=
|
||||||
|
|
||||||
ajv-errors@^1.0.0:
|
ajv-errors@^1.0.0:
|
||||||
version "1.0.1"
|
version "1.0.1"
|
||||||
resolved "https://registry.yarnpkg.com/ajv-errors/-/ajv-errors-1.0.1.tgz#f35986aceb91afadec4102fbd85014950cefa64d"
|
resolved "https://registry.yarnpkg.com/ajv-errors/-/ajv-errors-1.0.1.tgz#f35986aceb91afadec4102fbd85014950cefa64d"
|
||||||
|
|||||||
Reference in New Issue
Block a user