First Portal: https://ssshooter.com/2021-03-23-vue-optimization/
Split components
I also thought that splitting subcomponents is for abstraction, but practice tells me that splitting subcomponents is a way to improve performance (specific cases).
I have encountered such a problem in my actual work. There is a large table with multiple dialog boxes for adding new items. When there is a lot of data, filling in the new data will become a card.
The reason is that, within a component, changing the value will result in a data check and diff of the entire component. But knowing that the big form hasn't changed anything, why should I waste time checking it?
In order to solve this problem, it is a very effective optimization method to extract the dialog box separately.
When the child component is updated, the parent component will not be updated by default unless the child component changes the data of the parent component.
Let me take the dialog of element UI as an example:
open this link open the runnable case directly
Write a page, which contains two dialogs, one is directly written to the page, and the other is abstracted as a component.
<template>
<div>
<el-button type="text" @click="dialogVisible = true"
>点击打开 Dialog</el-button
>
<el-button type="text" @click="dialogData.visible = true"
>点击打开 Dialog2</el-button
>
<div>{{ renderAfter() }}</div>
<el-dialog
:append-to-body="true"
title="提示"
:visible.sync="dialogVisible"
width="30%"
>
<el-input v-model="xxx" />
<span slot="footer" class="dialog-footer">
<el-button @click="dialogVisible = false">取 消</el-button>
<el-button type="primary" @click="dialogVisible = false"
>确 定</el-button
>
</span>
</el-dialog>
<my-dialog :dialogData="dialogData" />
</div>
</template>
<script>
import MyDialog from './Dialog'
export default {
components: { MyDialog },
name: 'HelloWorld',
props: {
msg: String,
},
data() {
return {
xxx: '',
dialogVisible: false,
dialogData: {
visible: false,
},
}
},
methods: {
renderAfter() {
if (!window.renderCountTech) window.renderCountTech = 1
else window.renderCountTech++
console.log(
'%c渲染函数检测',
'color:#fff;background:red',
window.renderCountTech
)
},
},
}
</script>
Here is the content of the dialog component:
<template>
<el-dialog
:append-to-body="true"
title="提示"
:visible.sync="dialogData.visible"
width="30%"
>
<el-input v-model="xxx" />
<span slot="footer" class="dialog-footer">
<el-button @click="dialogData.visible = false">取 消</el-button>
<el-button type="primary" @click="dialogData.visible = false"
>确 定</el-button
>
</span>
</el-dialog>
</template>
<script>
export default {
props: ['dialogData'],
data() {
return {
xxx: '',
}
},
}
</script>
It can be seen from practice that when the dialog is opened and closed, and the input box modifies the data, the rendering function of the entire component will be triggered, and the parent component will not be updated when the dialog2 is opened or closed or entered. If the amount of data in the component where the dialog box is located is small, the difference is really small, but when the amount of data is large, there will be a perceptible lag when entering the dialog box. (In one sentence: the dialog box is a component by itself, and internal updates do not affect the parent component)
Not only that, but on the other hand, when the parent component is updated, it will render dialog1 but not dialog2, which really kills two birds with one stone. (In a word: the parent component does not change the child component without data change when the parent component is updated)
Even if this component is not reused, the methods used by the dialog can be separated into separate files without mixing them with the methods of the main page. If a dialog has a lot of logic, it's definitely a good idea to separate it into separate files.
However, there are of course disadvantages:
First of all, data interaction is a bit inconvenient, but it can always be solved by using $parent
and Vuex.
The second problem is that the error dialogData.visible
will be reported when Unexpected mutation of "dialogData" prop. (vue/no-mutating-props)
is modified.
As a Vue best practice, parent-to-child props must not be directly modified by the child. My point is that if you know what you're doing and the side effects aren't too strong...it's probably okay to do this, what's everyone's opinion?
If you stick to best practices, there are two scenarios:
emit
Use on and emit honestly, with a few more lines of code:
<text-document v-bind:title.sync="doc.title"></text-document>
This is equivalent to:
<text-document
v-bind:title="doc.title"
v-on:update:title="doc.title = $event"
></text-document>
Then update the parent component through this.$emit('update:title', newTitle)
in the child component.
$refs
The visibility is controlled by the dialog itself, and the visibility of the dialog is set through refs in the parent component:
this.$refs.child.visible = true
What about the other way around? You can also modify the parent in the child (this doesn't count as modifying the prop, it's a tricky trick)
this.$parent.visible = true
A more detailed explanation of the update granularity can be found here: What is the difference between the update granularity of components between Vue and React
PS: There is another hidden conclusion. If a component uses a slot, the child component will be re-rendered with the parent component.
computed
After the emphasis of the official document , it can be said that it is well known that computed can cache the calculation results and reduce the calculation time when the dependent values remain unchanged.
Also known is the KeepAlive component.
Reduce the use of heavy components and plug-ins
Many UI frameworks are very perfect, but just because they are too perfect, the interaction of various functions may make you run too slow or not debug well.
It is of course possible to directly change the original framework, but the cost of understanding is not low, and after the change, it is even more uncomfortable to merge the code after the update, so I prefer to make one myself.
I don't want to be a wheel that works all over the world. Because everyone's needs are different, if you want to make a wheel that meets everyone's requirements, the wheel will become very heavy, and I personally don't want to see it...
So the "wheel" I hope to make is a lightweight "framework" that meets the minimum functional requirements, so that everyone can modify it easily, and at the same time, they don't have to worry about getting heavier and heavier with continuous updates. For example v-vld and [tbl]. ( https://github.com/v-cpn/cpn-tbl )
One-way binding or even no binding at all
One-way binding means that the data uses defineProperty
to set configurable
to false
, so that the defineReactive
corresponding to the data will skip the responsive setting:
Object.defineProperty(data, key, {
configurable: false,
})
But you can still assign values to the bound target through v-model
, but the interface will not be updated after assignment.
This approach works when the data is deeply nested , after blocking defineReactive
, the sub-objects in the data will not be processed recursively. ( data flattening also exempts recursion)
Not binding at all is to reassign an object written by the official website Object.freeze
. In this way, the value inside the object (the first layer) cannot be changed directly, and it can be applied to pure display data.
cache ajax data
It can be encapsulated like the get of ordinary axios and directly replace the original axios object:
import axios from 'axios'
import router from './router'
import { Message } from 'element-ui'
let baseURL = process.env.VUE_APP_BASEURL
let ajax = axios.create({
baseURL,
withCredentials: true,
})
let ajaxCache = {}
ajaxCache.get = (...params) => {
let url = params[0]
let option = params[1]
let id = baseURL + url + (option ? JSON.stringify(option.params) : '')
if (sessionStorage[id]) {
return Promise.resolve(JSON.parse(sessionStorage[id]))
}
return ajax.get(...params)
}
ajax.interceptors.response.use(
function (response) {
// 其他处理
// ……
if (response.data.code === '20000') {
let params = response.config.params
let id = response.config.url + (params ? JSON.stringify(params) : '')
sessionStorage[id] = JSON.stringify(response.data.data)
return response.data.data
}
},
function (error) {
Message.error('连接超时')
return Promise.reject(error)
}
)
export default ajaxCache
functional component
<template functional>
<div class="cell">
<div v-if="props.value" class="on"></div>
<section v-else class="off"></section>
</div>
</template>
https://codesandbox.io/s/functional-t7c5p?file=/src/App.vue
PS: Because functional components are not instantiated, they will be re-rendered every time they are used. If you want to be completely static, use v-once
PS2: In Vue3, the speed difference between functional and normal components is almost negligible
reduce use of this
In short, it is necessary to pay attention to the cost of each this
value in computed, watch and render, including the code that depends on the collection. In fact, it is enough to run these codes only once.
{
computed: {
base () {
return 42
},
result ({ base, start }) {
let result = start
for (let i = 0; i < 1000; i++) {
result += Math.sqrt(Math.cos(Math.sin(base))) + base * base + base + base * 2 + base * 3
}
return result
},
},
}
To learn more about this issue, you can read here: https://mp.weixin.qq.com/s/wuNneeWA6yrVpYRteTJxkw
v-show reuses the DOM
Of course, v-show
can speed up the display speed of components, but the balance between v-show
and v-if
should be well controlled. v-if
can be used to optimize the loading speed of the first screen.
Fragmentation
- Responsive data is assigned in batches, thereby reducing the time of each rendering and improving the user-perceived fluency.
- Display of heavy components delayed using
v-if
- Available via
requestAnimationFrame
Related portals: requestAnimationFrame
PS: Personal experience, if multiple ajax involves the same pile of data, the speed of fragmented rendering may not be fast, I will choose to use Promise.all to combine rendering
Summarize
Component angle optimization:
- Split components and optimize update speed with component-level update granularity
- Use heavy-duty components with caution, and make your own when necessary. The same is true for plug-ins
- Use functional components (low priority)
Handling reactive side effects:
- Leverage reactive anti-patterns
- Reduce use of
this
in dependency collection
Reduce rendering pressure:
- Balance of
v-show
andv-if
- Fragmented rendering
Vue's own cache:
- keepalive
- computed
Other optimizations:
- data cache
- virtual scroll
- remove console.log
- Enable performance configuration
**粗体** _斜体_ [链接](http://example.com) `代码` - 列表 > 引用
。你还可以使用@
来通知其他用户。