I previously wrote “Lazy loading Optimization: Whether JavaScript IntersectionObserver API listening elements are visible”. Based on the previous article, doing lazy scrolling is not a problem at all.
However, if the page is refreshed periodically and automatically, refreshing in the invisible area is a waste of resources on the front and back ends.
Based on the previous part, this paper reviews how IntersectionObserver+ResizeObserver+getBoundingClientReact+Object. Freeze improves the overall performance and user experience of the project through an example of its own modification
Examples are as follows:
This page is not just a simple scrolling load so load. The chart is also complicated
-
Page refresh operations: Switch to the directory list on the right, search ok, query search, manually refresh the panel, and periodically refresh the panel
-
Refresh chart matters: parent-child chart, association chart, combination chart (chart set chart)
-
Resizing matters: browser page resizing, sidebar retracting, sidebar dragging, edit mode: group resizing, chart resizing
The previous implementation of this page was quite complex, and it was bugger from time to time (complex code is bound to increase the probability of problems).
Let’s see if your project can also have the following deadly problems:
-
Multi-chart list, multi-user set timed automatic refresh, server requests are particularly large, serious resource consumption (if the window refresh is limited, ten screen scrolling, resources are reduced by 90%)
-
When the chart list is too large, the page freezes or even crashes (
-
BUS, echarts event components are unbound when logged out — the function is repeated multiple times
-
Vue Deep Watch — Big data chart, CPU, memory overflow, page directly crash
-
Slow overall event response on the page — the parent container is constantly iterating through notifying child components, which is a performance drain.
-
Echarts charts refresh slowly – many times echarts instances are rebuilt instead of calling the original instance setOption
-
The timed refresh time is inaccurate and memory leaks. SetInterval directly sets the timed refresh time
-
Windows global manual management of echarts instances, the project memory footprint is huge, even memory leaks, page crashes
Open the dry version directly
Container scroll notifying container components that they need to be rerendered; Intra-group then invokes intra-component refresh.
Similarly, when the size of the parent container changes; Or edit the list when sizing; Do the same thing.
So this is the original implementation
Panel page components: The post is definitely a simplified version, the actual business is much more complex…
<template> <div class="list-box"> <Group v-for="group in list" :key="group.id" ref="group" /> </div> </template> <script> export default { watch: { isSidebarOpen() { this.handleRenderDebounce(); }}, mounted () {/ / debounce optimize performance, will be redundant rendering enclosing handleRenderDebounce = view debounce (this handleRender, 200); / / parent container scroll event monitoring, trigger the rendering function enclosing $refs. ListBox. AddEventListener (' scroll ', enclosing handleRenderDebounce); / / page size adjustment, the trigger rolling function window. The onresize = this. HandleRenderDebounce; $on('reloadGroupChart', () => { if (this.$refs.group) { this.$refs.group.forEach((group) => { if (group.resetLoadMap) { group.resetLoadMap(true); }}); this.$nextTick(() => { this.handleRender(); }); }}); // Create a new group, $on('eventScrollToNewGroup', () => { if (this.$refs.group && this.$refs.group.length > 0) { this.$refs.group[this.$refs.group.length - 1].$el.scrollIntoView(); }}); }, methods: {handleRender() {this.$refs.group.foreach ((el) => {el.handlerender (); }); ,}}}; </script> <style lang="scss"> </style>Copy the code
Then feel gang-raped again for each chart component
<template> <div class="list-box"> <chart-item v-for="chart of group.charts" ref="chart" :key="chart.id" /> </div> </template> <script> export default { mounted() { Bus.$on('eventRefreshCharts', () => { for (const chart of this.$refs.chart || []) { chart.initChartItem(); }}); Bus.$on('eventRefreshTargetChart', (chartId) => { this.$forceUpdate(); this.$nextTick(() => { for (const chart of this.$refs.chart || []) { if (chart.chart.id === chartId) { chart.clickRefreshChart(); }}}); }); Bus.$on('eventRefreshCharts', (_) => { for (const chart of this.$refs.chart || []) { chart.initChartItem() } }) if (this.group.charts) { this.group.charts.forEach((chart) => { this.$set(this.reloadMap, chart.id, false); }); } this.$nextTick(() => {setTimeout() => {this.handlerender (); }, 300); }); }, methods: $refs.chart) {this.$refs.chart. ForEach ((chart) => {if (chart.handleRepeater) { chart.handleRepeater(); }}); }}, // resetLoadMap(isNeed, chartId) {if (chartId) {this.reloadMap[chartId] = isNeed; } else { for (const key in this.reloadMap) { this.reloadMap[key] = isNeed; ToggleGroup () {this.group.isexpand =! this.group.isExpand; this.$nextTick(() => { this.$emit('handleRender'); }); }, // When grouping size is adjusted...... }; </script>Copy the code
It then re-renders the child components within each diagram component.
This code will not be posted…
The above code basically achieves the above functions, but certainly does not conform to the high cohesion low coupling, are Russian nesting dolls.
Self-management edition
First, to summarize the optimization idea:
-
For rolling loading, IntersectionObserver API is available. During rolling, the component determines whether it is visible and loads. However, we must pay attention to the following conditions
-
When it’s uninitialized, when it scrolls, it just loads. And stores the currently loaded request parameters for later verification when loading
-
It is being loaded (when the component is loading).
-
You need to check whether the query conditions are changed. If they are changed, you need to load them again, such as query parameters and periodic refresh time
-
For size changes, there is ResizeObserver. No matter the page size changes, or the size changes of its parent component or grandfather component, it will feed back to the size changes of the component itself, and it is good to directly monitor the component itself.
-
For the refresh event, the component stores the last loaded parameters and takes over what it thinks it can do after the refresh event.
-
For memory CPU+ memory explosion, it is forbidden for chart configuration items (option parameter) to be bound and monitored on vUE, and data can be sampled. Echarts instances, all kinds of binding events, timely destruction.
On a VUE implementation, it can be a common base class that other chart components inherit from. It can also be an abstract component.
This is at the pseudo code level
<template> <div v-bkloading="{isLoading:loading&&uninitialized}" class="chart-box" ref="chart"> <! -- Icon title, first loading, whole chart loading, loading again, Loading Gif --> <chart-title :loading="loading" @refreshchart ="clickRefreshChart" /> <! - If echarts diagrams are encapsulated as components, it is not recommended to pass option parameters through prop (as well as object-freeze (option)). --> <e-chart ref="eChartRef" /> <! <img V-else > </div> </template> <script> export default {data() {return {// The component is not initialized. Echarts setOption can be uninitialized: true, // During data loading loading: true, // You can specify echarts chart model data, display it first, and display option after data request is completed: null, clickRefreshChart: null, intersectionObserver: null, resizeObserver: null, }; }, BeforeDestroy () {/ / attention to the problem of memory leaks. / / this intersectionObserver. Unobserve (enclosing $el) / / advice directly using the disconnect this.intersectionObserver.disconnect(); this.intersectionObserver = null; this.resizeObserver.disconnect(); this.resizeObserver = null; If (this.echart) {this.echart. Clear (); this.eChart.dispose(); this.eChart = null; }}, mounted() { this.intersectionObserver = new IntersectionObserver((entries) => { if (entries[0].intersectionRatio > 0) { this.initChartItem(); }}); this.observeIo.observe(this.$el); this.resizeObserver = new ResizeObserver((entries) => { this.BWidth = document.getElementById('A').clientWidth - 20; }); this.resizeObserver.observe(this.$el); /* const isElementNotInViewport = function (el) { const rect = el.getBoundingClientRect(); return ( rect.top >= (window.innerHeight || document.documentElement.clientHeight) || rect.bottom <= 0 ); }; */ Bus.$on('clickRefreshChart', (data) => {if (! isElementNotInViewport(this.$el)) { this.clickRefreshChart(data); }}); This.clickrefreshchart = debounce((data) => {// TODO different refresh operations, logic this.initchartitem (data); }, 700); }, methods: {// trigger chart loading, update icon async initChartItem() {// in loading, not reload if (this.loading) {return; } // if (! this.uninitialized) { if (this.context.start_time === this.contextBak.start_time && this.context.end_time === this.contextBak.end_time) { return false; } // TODO other conditions, etc.} // TODO loads data, renders graphs},},}; </script>Copy the code
Optimization, X effect I am quite satisfied.
Feel the article is not very clear, but the project code is not directly exposed, first such it, and then add
Welcome road friends to discuss together, poor road polite……
Reprint the home station article list chart performance optimization: minimum resource consumption, visual area, please indicate the source: www.zhoulujun.cn/html/webfro…