In addition to Weibo, there is also WeChat
Please pay attention
WeChat public account
Shulou
2025-02-25 Update From: SLTechnology News&Howtos shulou NAV: SLTechnology News&Howtos > Development >
Share
Shulou(Shulou.com)06/02 Report--
In this issue, the editor will bring you what are the performance optimization methods of vue. The article is rich in content and analyzes and describes for you from a professional point of view. I hope you can get something after reading this article.
Performance optimization methods are: 1, use "v-slot:slotName" instead of "slot=" slotName "; 2, avoid using" v-for "and" v-if "at the same time; 3, always add key to" v-for ", and do not use index as key;4, use deferred rendering, and so on.
The operating environment of this tutorial: windows7 system, vue2.9.6 version, DELL G3 computer.
In the daily development of using Vue or other frameworks, we will encounter some performance problems more or less, although Vue has helped us to do a lot of optimization, but there are still some problems that we need to take the initiative to avoid. I have summarized some scenarios that are prone to performance problems and optimization techniques for these problems in my daily opening, as well as articles by various online leaders. I hope this article will be helpful to you.
Use v-slot:slotName instead of slot= "slotName"
V-slot is a new syntax for 2.6. see: it was almost two years ago that Vue2.6,2.6 was released, but there are still many people who still use the syntax of slot= "slotName". Although both grammars can achieve the same effect, the internal logic is indeed different. Let's take a look at the differences between the two ways.
Let's first take a look at what these two grammars will be compiled into:
Using the new writing, for the following templates in the parent component:
{{name}}
Will be compiled into:
Function render () {with (this) {return _ c ('child', {scopedSlots: _ u ([{key:' name', fn: function () {return [_ v (_ s (name))]}, proxy: true}])}
Using the old way of writing, for the following templates:
{{name}}
Will be compiled into:
Function render () {with (this) {return _ c ('child', [_ c (' template', {slot: 'name'}, [_ v (_ s (name)])],)}}
Through the compiled code, it can be found that the old writing method renders the slot content as children and is created in the parent component's render function, and the dependency of the slot content is collected by the parent component (name's dep collects the parent component's rendering watcher), while the new method puts the slot content in scopedSlots and is called in the child component's render function. The dependency of the slot content is collected by the child component (the dep of name collects the rendering watcher of the child component). The end result is that when we modify the name property, the old way of writing is to call the update of the parent component (call the rendering watcher of the parent component), and then call the update of the child component during the update process of the parent component (prePatch = > updateChildComponent), while the new way is to directly call the update of the child component (call the rendering watcher of the child component).
In this way, the old writing method has an extra process of updating the parent component when it is updated, while the new writing method will be more efficient and perform better because it updates the child components directly, so it is recommended to always use the v-slot:slotName syntax.
Use calculation properties
This point has been mentioned many times. One of the biggest features of a computational attribute is that it can be cached. This cache means that as long as its dependency does not change, it will not be re-evaluated. When accessed again, it will directly get the cached value, which can greatly improve performance when doing some complex calculations. You can look at the following code:
{{superCount}} export default {data () {return {count: 1}}, computed: {superCount () {let superCount = this.count / / suppose there is a complex calculation for (let I = 0; I)
< 10000; i++) { superCount++ } return superCount } } } 这个例子中,在 created、mounted 以及模板中都访问了 superCount 属性,这三次访问中,实际上只有第一次即created时才会对 superCount 求值,由于 count 属性并未改变,其余两次都是直接返回缓存的 value。 使用函数式组件 对于某些组件,如果我们只是用来显示一些数据,不需要管理状态,监听数据等,那么就可以用函数式组件。函数式组件是无状态的,无实例的,在初始化时不需要初始化状态,不需要创建实例,也不需要去处理生命周期等,相比有状态组件,会更加轻量,同时性能也更好。具体的函数式组件使用方式可参考官方文档:函数式组件 我们可以写一个简单的 demo 来验证下这个优化: // UserProfile.vue {{ name }} export default { props: ['name'], data() { return {} }, methods: {} } // App.vue import UserProfile from './components/UserProfile' export default { name: 'App', components: { UserProfile }, data() { return { list: Array(500) .fill(null) .map((_, idx) =>'Test' + idx)}}, beforeMount () {this.start = Date.now ()}, mounted () {console.log (' usage:', Date.now ()-this.start)}}
The UserProfile component only renders the name of props, and then calls it 500 times in App.vue to count the time spent from beforeMount to mounted, that is, the initialization time for 500 subcomponents (UserProfile).
After many attempts, I found that the time consuming has been around 30ms, so now let's change it to UserProfile and change it to a functional component:
{{props.name}}
After many attempts at this point, the initialization time has been 10-15ms, which is enough to show that functional components have better performance than stateful components.
Use v-show and v-if in conjunction with the scene
Here are two templates that use v-show and v-if
Toggle toggle
Both of these functions are used to control the display / hiding of certain components or DOM, and before discussing their performance differences, let's analyze the differences between the two. Where the template for v-if is compiled into:
Function render () {with (this) {return _ c ('div', [visible? _ c (' UserProfile', {attrs: {user: user1}}): _ e (), _ c ('button') {on: {click: function ($event) {visible =! visible}, [_ v ('toggle')]],)}}
You can see that the part of v-if is converted into a ternary expression. When visible is true, create a vnode of UserProfile, otherwise create an empty vnode. When the old and new nodes are different in patch, the old nodes will be removed or new nodes will be created. In this case, UserProfile will also be created / destroyed. If there is a lot of DOM in the UserProfile component, or if there is a lot of initialization / destruction logic to be performed, a lot of performance is bound to be wasted as the visible switches. At this point, you can optimize with v-show. Let's take a look at the compiled code from v-show:
Function render () {with (this) {return _ c ('div', [_ c) (' UserProfile', {directives: [{name: 'show', rawName:' Vmura showcase, value: visible, expression: 'visible'}] Attrs: {user: user1}), _ c ('button', {on: {click: function ($event) {visible =! visible}) [_ v ('toggle')]],)}}
V-show is compiled into directives. In fact, v-show is an instruction within Vue. In the code of this instruction, the following logic is mainly executed:
El.style.display = value? El.__vOriginalDisplay: 'none'
It is actually controlled by switching the display attribute of the element. Compared with v-if, it does not need to create / remove nodes in the patch phase, but only controls the style.display attribute of the DOM element according to the bound value on the v-show, which can save a lot of performance in frequent switching scenarios.
However, this does not mean that v-show can replace v-if in any case. If the initial value is false, v-if will not create hidden nodes, but v-show will create and hide it by setting style.display='none'. Although the DOM is hidden on the outside, v-show has gone through the creation process completely, resulting in a waste of performance.
So, the advantage of v-if is that it is initialized and v-show is updated. Of course, it does not require you to do it this way absolutely. For example, some components will request data when initializing, but if you want to hide the component first, and then you can see the data immediately when displaying it, you can use v-show, or you can use v-if to display the latest data every time you display the component. So we have to combine the specific business scenarios to choose an appropriate way.
Use keep-alive
In the scenario of dynamic components:
At this time, multiple components switch back and forth. Each time the currentComponent changes, the related components will be destroyed / created. If these components are complex, it will cause some performance pressure. In fact, we can use keep-alive to cache these components:
The function of keep-alive is to cache the wrapped components after the first rendering, and then take them directly from the cache when needed next time, thus avoiding unnecessary performance waste. When discussing the previous issue, we said that v-show has great performance pressure at the beginning, because it needs to create all the components. In fact, it can be optimized with keep-alive:
In this way, the UserProfileB component will not be rendered during initialization, but the UserProfileB component will only be rendered when visible is switched, and will be cached by keep-alive at the same time. When switching frequently, it will save a lot of performance because it is taken directly from the cache, so this method has better performance in initialization and update.
However, keep-alive is not without shortcomings. Components will occupy memory when they are cached, which is a trade-off between space and time. In actual development, it is necessary to choose the appropriate way according to the scene.
Avoid the simultaneous use of v-for and v-if
This is a point clearly pointed out in Vue's official style guide: Vue style guide
Such as the following template:
{{user.name}}
Will be compiled into:
/ / simplified version function render () {return _ c ('ul', this.users.map ((user) = > {return user.isActive? _ c (' li', {key: user.id}, [_ v (_ s (user.name)]): _ e ()}),)}
As you can see, here is v-for, and then v-if. There is a problem here: if you have 10, 000 pieces of data, of which only 100 are in isActive status, you only want to display them, but in fact, in rendering, these 10, 000 pieces of data will be traversed every time you render. For example, when you change some responsive data elsewhere in this component, it will trigger re-rendering, call the rendering function, and execute the above code to traverse the 10,000 pieces of data, even if your users has not changed.
To avoid this problem, you can use computational attributes instead in this scenario:
{{user.name}} export default {/ /... Computed: {activeUsers () {return this.users.filter ((user) = > user.isActive)}} copy the code
This will only execute the logic of this traversal when the users changes, avoiding unnecessary performance waste compared to before.
Always add key for v-for, and do not use index as key for
This point is clearly pointed out in the Vue style guide, but also a point often asked in interviews, many people are used to index as a key, which is actually not very good, index as a key, will let diff algorithm produce wrong judgment, which will bring some performance problems, you can take a look at the ssh boss's article, in-depth analysis, why Vue should not use index as key. Here I will also use an example to briefly illustrate how index affects performance when it is used as a key.
Take a look at this example:
Const Item = {name: 'Item', props: [' message', 'color'], render (h) {debugger console.log (' render' where Item was executed) return h ('div', {style: {color: this.color}}, [this.message])} new Vue ({name:' Parent', template: ``, components: {Item}) Data () {return {list: [{id: 'this.list.reverse ()}}). $mount (' # app')).
Here is a list, which will render a / b, and when clicked, it will execute the reverse method to make the list fall down. You can copy this example and see the effect on your computer.
Let's first analyze what happens when you click when you use id as a key
Because the list has changed, it will trigger the re-rendering of the Parent component and get the new vnode and the old vnode to execute the patch. We are mainly concerned with the updateChildren logic in the patch process. UpdateChildren is to execute the diff algorithm on the new and old children to reuse the nodes as much as possible. For our example, the old children is:
; [{tag: 'Item', key:' asides, propsData: {color:'# f00', message: 'red'}}, {tag: 'Item', key:' baked, propsData: {color:'# 0f0', message: 'green'}]
The new children after reverse is:
; [{tag: 'Item', key:' baked, propsData: {color:'# 0f0ft, message: 'green'}}, {tag: 'Item', key:' asides, propsData: {color:'# f00', message: 'red'}}]
Executing updateChildren,updateChildren at this point compares the loops between the old and new sets of children nodes:
While (oldStartIdx updates props. At this time, the old propsData is {color:'# f0f0 updated message: 'red'}, and the new propsData is {color:'# 0f0 updated message: 'green'}. After the update, the props of Item will be changed, triggering the re-rendering of the Item component.
This is the difference between index as key and id as key. When id is used as key, it only moves the node and does not trigger the re-rendering of Item. When index is used as key, it triggers the re-rendering of Item. It is conceivable that when Item is a complex component, it will inevitably cause performance problems.
The above process is complicated and involves a lot of things, so you can take it apart and write several articles. In some places, I just briefly said that if you don't quite understand, you can copy the above example and adjust it on your own computer. I added print log and debugger to the rendering function of Item. You can try using id and index as key, and you will find that when id is used as key. The rendering function of Item is not executed, but when index is used as key, the rendering function of Item is executed, which is the difference between the two ways.
Delayed rendering
Delayed rendering is batch rendering. Suppose we have some components on a page that need to perform complex logic during initialization:
This will take a long time, resulting in a decrease in the number of frames and stutters, which can actually be optimized by batch rendering, that is, one part is rendered first, and then another part:
Refer to the code in the nine Vue.js performance optimization techniques revealed by teacher Huang Yi:
Export default {data () {return {displayPriority: 0}}, mounted () {this.runDisplayPriority ()}, methods: {runDisplayPriority () {const step = () = > {requestAnimationFrame (()) > {this.displayPriority++ if (this.displayPriority)
< 10) { step() } }) } step() }, defer(priority) { return this.displayPriority >= priority}
In fact, the principle is very simple, mainly to maintain the displayPriority variable, which increases itself when each frame is rendered through requestAnimationFrame, and then we can increase the displayPriority to a certain value on the component by VFT if = "defer (n)". In this way, we can avoid the stutter problem caused by too long js execution time.
Use non-responsive data
When the Vue component initializes data, it recursively traverses every piece of data defined in data, and changes the data to responsive mode through Object.defineProperty, which means that if the amount of data in data is large, it will take a long time to execute Object.defineProperty during initialization, which will lead to performance problems. At this time, we can force the data to become non-responsive, thus saving time. Take a look at this example:
{{item.name}}
/ 10,000 data const heavyData = Array (10000) .fill (null) .map ((_, idx) = > ({name: 'test', message:' test', id: idx})) export default {data () {return {heavyData: heavyData}}, beforeCreate () {this.start = Date.now ()}, created () {console.log (Date.now ()-this.start)}}
There are 10, 000 pieces of data in heavyData. Here is the time from beforeCreate to created. For this example, this time is basically the time to initialize the data.
I tested it many times on my personal computer, and this time has been 40-50ms, and then we use the Object.freeze () method to change the heavyData into non-responsive and try again:
/ /... data () {return {heavyData: Object.freeze (heavyData)}} / /.
After the change, try again, the time to initialize the data becomes 0-1ms, soon there is 40ms, this 40ms is the time to recursively traverse the heavyData to execute the Object.defineProperty.
So why does Object.freeze () have this effect? After you use Object.freeze () on an object, you cannot add new properties to the object, delete existing properties, modify the enumerability, configurability, and writability of existing properties of the object, and modify the values of existing properties.
Vue has a judgment before transforming the data into a responsive one:
Export function observe (value, asRootData) {/ /. Omit other logic if (shouldObserve & & isServerRendering () & & (Array.isArray (value) | | isPlainObject (value)) & & Object.isExtensible (value) & &! value._isVue) {ob = new Observer (value)} / /. Omit other logic}
In this judgment condition, there is an Object.isExtensible (value), this method is to determine whether an object is extensible, because we use Object.freeze (), we must return false, so we skip the following process, which naturally saves a lot of time.
In fact, not only does initializing the data have an impact, you can use the above example to count the time from created to mounted. When Object.freeze () is not used on my computer, the time is 60-70ms. After using Object.freeze (), it is reduced to 40-50ms. This is because when reading the data in heavyData in the rendering function, it is executed to the getter method defined by Object.defineProperty, and Vue does some collection dependency processing here. It will definitely take up some time, and since the data after using Object.freeze () is non-responsive, there is no process of collecting dependencies, which naturally saves performance.
Since accessing responsive data will go to custom getter and collect dependencies, you should avoid frequently accessing responsive data before traversing, for example, storing this data in local variables before traversing, especially in computing properties and rendering functions. For a more specific explanation of this, you can see teacher Huang Yi's article: Local variables.
However, this is not without any problems. As a result, the data under heavyData are not responsive data, and you will not have any effect if you use computed, watch, etc., but generally speaking, this large amount of data is used for display. If you have special needs, you can only use Object.freeze () for a certain layer of this data, together with the delay rendering, functional components and so on mentioned above. Can greatly improve performance.
Performance differences between template compilation and rendering functions and JSX
Vue projects can be developed not only using SFC, but also using rendering functions or JSX. Many people think that it is just different development methods, but they do not know that there are performance differences, or even great differences between these development methods. In this section, I will find some examples to illustrate. I hope you will have more standards to measure when choosing a development method in the future.
In fact, there are not many performance optimizations in Vue2 template compilation, there are many in Vue3, Vue3 improves the performance greatly through the combination of compilation and runtime, but because this article is about Vue2 performance optimization, and Vue2 is still used by a lot of people, so I will choose a point in Vue2 template compilation.
Static node
Here is the template:
Hello! Hello
Will be compiled into:
Function render () {with (this) {return _ m (0)}}
You can see that it is a little different from ordinary rendering functions, so let's take a look at why it is compiled into this kind of code.
The compilation of Vue will go through the optimize process, in which static nodes will be marked. The specific content can be seen in the document written by teacher Huang Yi: Vue2 compilation-optimize marking static nodes.
In the codegen phase, it is determined that the tag of the static node will go to the branch of genStatic:
Function genStatic (el, state) {el.staticProcessed = true const originalPreState = state.pre if (el.pre) {state.pre = el.pre} state.staticRenderFns.push (`with (this) {return ${genElement (el, state)} `) state.pre = originalPreState return` _ m (${state.staticRenderFns.length-1} ${el.staticInFor?', true':''}) `}
Here is the key logic for generating the code, where we save the rendering function in staticRenderFns, and then get the subscript of the current value to generate the _ m function, which is why we get _ m (0).
This _ m is actually an abbreviation for renderStatic:
Export function renderStatic (index, isInFor) {const cached = this._staticTrees | | (this._staticTrees = []) let tree = cached [index] if (tree & &! isInFor) {return tree} tree = cached [index] = this.$ options.staticRenderFns [index] .call (this._renderProxy, null, this) markStatic (tree, `_ static__$ {index}`, false) return tree} function markStatic (tree) Key) {if (Array.isArray (tree)) {for (let I = 0) I < tree.length; iTunes +) {if (tree [I] & & typeof tree [I]! = 'string') {markStaticNode (tree [I], `${key} _ {I}`, isOnce)}} else {markStaticNode (tree, key, isOnce)}} function markStaticNode (node, key, isOnce) {node.isStatic = true node.key = key node.isOnce = isOnce}
The internal implementation of renderStatic is relatively simple. First, get the _ staticTrees of the component instance. If not, create one, then try to get the previously cached node from _ staticTrees, and return it directly if you get it. Otherwise, you will get the corresponding rendering function from staticRenderFns and cache the result on _ staticTrees, so that the result will be returned directly from the cache the next time you enter this function.
After getting the node, the node will be marked with isStatic and other tags through markStatic. The node marked as isStatic will skip the patchVnode phase directly. Because the static node will not change, there is no need for patch. Skipping patch can save performance.
The combination of compiler and runtime can help us improve application performance, which is difficult to achieve by rendering function / JSX. Of course, it does not mean that JSX can not be used. Compared with templates, JSX is more flexible, and the two have their own usage scenarios. The purpose of writing these here is to provide you with some criteria for technical selection.
Vue2 compiler optimization in addition to static nodes, there are slots, createElement and so on.
These are the performance optimization methods of vue shared by the editor. If you happen to have similar doubts, you might as well refer to the above analysis to understand. If you want to know more about it, you are welcome to follow the industry information channel.
Welcome to subscribe "Shulou Technology Information " to get latest news, interesting things and hot topics in the IT industry, and controls the hottest and latest Internet news, technology news and IT industry trends.
Views: 0
*The comments in the above article only represent the author's personal views and do not represent the views and positions of this website. If you have more insights, please feel free to contribute and share.
Continue with the installation of the previous hadoop.First, install zookooper1. Decompress zookoope
"Every 5-10 years, there's a rare product, a really special, very unusual product that's the most un
© 2024 shulou.com SLNews company. All rights reserved.