answer:
In large-scale enterprise-level projects, a large amount of data is often rendered. This kind of long list is a very common scenario. When the content of the list increases, it will lead to problems such as page sliding freeze, white screen , and slow data rendering ;
This situation mainly occurs in small programs, mobile terminals or background management pages;
Usually we use paging to gradually acquire content , but when there are more and more content;
For example, the pull-down refresh on the mobile terminal keeps flipping up, and more content will be loaded at the bottom, so that many elements will be added to the list . Occupancy or GPU rendering will bring a lot of performance loss, resulting in page sliding freezes and slow data rendering ;
Different processing should be done according to the situation:
- Avoid large amounts of data: take pagination to obtain
- Avoid rendering large amounts of data: vue-virtual-scroller plug-ins and other virtual list solutions only render data within the viewport range
- Avoid updates: use v-once to render only once
- Optimized update: Cache subtrees through v-memo , conditionally update, improve reuse, and avoid unnecessary updates
- Load data on demand: use lazy loading, such as lazy loading of tree component subtrees
In short, it still depends on the specific needs. First, avoid large data acquisition and rendering from the design ; if you really need to do this , you can optimize the number of renderings by using a virtual list ; finally optimize the update. V-memo can be used to further optimize the performance of big data updates. Other methods that can be used are interactive optimization, infinite scrolling, and lazy loading solutions.
Causes of page lag
Root cause: reflow and repaint of a large number of DOM elements
Modification is a modification to the current DOM element, while updating is updating all DOM elements
Optimization idea:
1. Lazy rendering
- Lazy loading, a common long list optimization solution, is common on mobile terminals
- Principle: Only a part is rendered at a time, and when the rendered data is about to be scrolled, the following part is rendered
- Advantages: Render a part of data each time, fast
- Disadvantages: When the amount of data is large, there are still a large number of DOM nodes in the page, which takes up too much memory, reduces the rendering performance of the browser, and causes the page to freeze
- Usage scenario: When the amount of data is not large (for example, 1000 pieces, depending on the complexity of each piece of data)
2. Page rendering
Generally, the backend gives us the data, we only need to give the number of pages and the amount of data displayed on each page to the backend, and the backend gives us the data for us to display.
3. Visual area rendering
Principle: Only the list items in the visible area of the page are rendered, and the data in the non-visible area is "not rendered at all" (the first few items and the next few items are preloaded), and the list items are dynamically updated when the list is scrolled. In order to prevent the white screen, so A few more pieces of data will actually be loaded
Implement viewport rendering yourself
in the parent component
- Use computed cache to simulate 10,000 pieces of data,
- Pass item (data), size (height of each piece of data) and showNumber (number of pieces of data rendered each time) to subcomponents through property binding
in subcomponent
- Receive the properties passed by the parent component through props;
- The outermost box is set overflow-y: scroll; to achieve vertical scrolling;
- Outermost box fixed height (outermost box height = size * showNumber);
- Listen to the scrolling event for the outer box, and calculate the height of the data being rolled up;
- Calculate the starting index of the data in the visible area, start = rolled height/height of a single piece of data;
- Calculate the end index of the data in the viewable area, end = start index + the number of items that can be displayed in the viewable area;
- Take the data of the start index start and the end index end of the visible area and display them in the visible area;
parent component
Simulate 10,000 pieces of data
Create an empty array with 10,000 elements, use the fill() function to initialize, all the values are empty, and then use map to return the id and content to the array, which realizes the array assignment;
computed: {
item () {
return Array(10000).fill('').map((item, index) => ({
id: index,
content: `列表内容`+ index
}))
}
}
Subassembly
ui structure
list is the visible area
bar is to open the box so that it can scroll vertically
- Visual area container: It can be seen as a box at the bottom that holds all elements.
- Scrollable area: It can be regarded as the middle layer. Suppose there are 10000 pieces of data and the height of each list item is 50, then the height of the scrollable area is 10000 * 50. The elements of this layer are invisible, and the purpose is to produce scroll bars exactly like the real list.
- Visual area list: It can be regarded as the top layer, displaying the currently processed data, and the height is the same as the visual area container. The position of the visible area list is changed dynamically, in order to make it always appear in the visible area.
After understanding the above concepts, let's look at what we need to do when the scroll bar scrolls:
- According to the scrolling distance and
item
height, calculate the current list that needs to be displayedstartIndex
startIndex
Calculate the height of the list currently to be displayed according to the height of the viewable areaendIndex
- According to
startIndex
andendIndex
intercepting the corresponding list data, assign it to the viewable area list and render it on the page - According to the scrolling distance and
item
height, calculate the offset distance of the visible area liststartOffset
and set it on the list
Receive the data passed by the parent component, define the initial subscript and end subscript
Calculate container height and rolled data height
Monitor the scrolling event for the container, the initial subscript is the number of data strips rolled, rounded down
The end subscript is the start subscript + the number of data items to be displayed
Implemented using virtual list vue-virtual-scroller
For long lists, most of our operations are:
1. Lazy loading, pagination,
2. Object.freeze freezes the array to cancel the response, because most of the time it is displayed
3. Replace high-definition pictures with thumbnails, because many times the size of pictures in long lists is relatively small, so you can use small pictures instead
The above can solve most of the long list problems, in the case of pagination
When pagination is not possible, render in the visible area
The height of items is fixed (RecycleScroller)
download plugin
yarn add vue-virtual-scroller --save
Register in main.js
import "vue-virtual-scroller/dist/vue-virtual-scroller.css";
import VueVirtualScroller from "vue-virtual-scroller";
Vue.use(VueVirtualScroller);
The following are required
Props |
explain |
Defaults |
items |
List of data to be rendered in the visible area | -- |
item-size |
The height of each data | null |
style |
The outer box height of the visible area | -- |
v-slot = { item } |
Slot, get each data item | -- |
Modify as needed
Props |
explain |
Defaults |
prerender |
Tell the server (SSR) how many pieces of data to render each time | 0 |
buffer |
The data height of multi-rendering outside the visible area to avoid scrolling blank | 200 |
keyField |
Fields for identifying items and optimally managing rendered views | id |
Fixed-height scoped slot parameters
Slot |
explain |
Defaults |
item |
each data | -- |
index |
The subscript of each data | -- |
active |
Whether the view is active. The active view is considered a visible view and is positioned by the RecycleScroller. Inactive views are not considered visible and are hidden from the user. If the view is inactive, any rendering-related calculations should be skipped. | -- |
The height of items is fixed (RecycleScroller), which can be pulled up and loaded
Events |
explain |
Defaults |
resize |
Triggered when the size is recalculated | -- |
visible |
Fired when the scrollbar thinks it is visible in the page | -- |
hidden |
Triggered when the scrollbar is hidden in the page | -- |
update (startIndex, endIndex) |
Emitted every time the view is updated, only if : emitUpdate="true" | false |
Implementation idea:
- Add :emitUpdate="true" and @update events;
- The update function passes in two formal parameters, start and end, and judges that when end is equal to the length of the array list, it requests the interface to obtain new data, and then adds it to the array.
The height of items is not fixed (DynamicScroller), which can be pulled up and loaded
Props(参数) |
解释 |
默认值 |
item(必填) |
每项数据 | -- |
active(必填) |
保持视图,数据处于 active 状态,将防止不必要的大小重新计算。 | -- |
sizeDependencies |
影响高度的值,如果发生变化,则重新计算 | -- |
watchData |
深入监视更改以重新计算大小(不推荐,可能会影响性能) | false |
tag |
组件要呈现的元素 | div |
emitResize |
每次重新计算大小时发出事件(可能会影响性能) | false |
minItemSize |
列表项初次渲染使用的最小高度 |
Events(事件) |
解释 |
默认值 |
resize |
重新计算大小时触发,仅当 :emitUpdate=“true”时 | false |