-
-
Notifications
You must be signed in to change notification settings - Fork 3.5k
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Functional components always get re-rendered (Discussion) #1358
Comments
Hi, Thanks for taking an interest in performance. Did a quick test: created a page with only a QModal having a QModalLayout. From this empirical test it seems that having QModalLayout as a functional component actually has a better performance than a regular component: So it seems that by having QModalLayout as regular then this component is rendered an additional one time more. The "// ---" comment was typed directly in the browser console after each step (page initial render, type comment, click on btn to show Modal, type comment, click "Close" btn). This is QModalLayout as a regular component (as opposed to a functional as it is now): export default {
name: 'q-modal-layout',
props: {
headerStyle: [String, Object, Array],
headerClass: [String, Object, Array],
contentStyle: [String, Object, Array],
contentClass: [String, Object, Array],
footerStyle: [String, Object, Array],
footerClass: [String, Object, Array]
},
render (h) {
console.log('QModalLayout render')
const child = []
if (this.$slots.header || (__THEME__ !== 'ios' && this.$slots.navigation)) {
child.push(h('div', {
staticClass: 'q-layout-header',
style: this.headerStyle,
'class': this.headerClass
}, [
this.$slots.header,
__THEME__ !== 'ios' ? this.$slots.navigation : null
]))
}
child.push(h('div', {
staticClass: 'q-modal-layout-content col scroll',
style: this.contentStyle,
'class': this.contentClass
}, [
this.$slots.default
]))
if (this.$slots.footer || (__THEME__ === 'ios' && this.$slots.navigation)) {
child.push(h('div', {
staticClass: 'q-layout-footer',
style: this.footerStyle,
'class': this.footerClass
}, [
this.$slots.footer,
__THEME__ === 'ios' ? this.$slots.navigation : null
]))
}
return h('div', {
staticClass: 'q-modal-layout column absolute-full'
}, child)
}
} Can you please reopen with how you see things getting improved? Thanks again. |
It may be that the regular components are more suited when they have "reactive" props, even though they are more expensive to render at first. I'm still unsure which way to go, regular or functional. I feel we're missing out a metric to establish which is actually better in this case. |
A functional component are faster but are always re-redered. Normal components are a bit heavier to setup, BUT are only re-rendered if their props change. The doc is quite clear:
I am quite worried to see that most of the components have been converted to functional components which will always be re-rendered. In addition, this bypasses many optimizations that Vvue has designed. https://vuejs.org/v2/guide/render-function.html#Functional-Components Also see the coments by Linus Borg (from Vue) at https://forum.vuejs.org/t/performance-for-large-numbers-of-components/13545/10
Also note that as of Vue 2.5.0+, functional components can also be created using templates, so there is no reason to complexify the code to make functional components that are basically template based. |
It's not hard converting all functional comps to regular comps. This is not an issue. I am however buffed by the fact that in my empirical test the number of QModalLayout's render times is +1 when it's regular, as opposed to when being functional. This seems to contradict Vue authors. |
I created a test page with a bunch of QModal+QModalLayout. I ran it under 0.14.7 (Normal components), and 0.15 where QModalLayout is a functional component. During initial loading (refresh), contrary to what we would expect, the 0.14.7 loaded fastest, v0.15.0 took 50% longer to load. However, changing the screen had the same performance in both cases. In both cases, alot of time seems wasted for hidden components regardless of their type, however, I could not find the cause yet. Processing Time (ms) = Total - Idle Update v0.14.7: 9442ms - 5714.6ms = 3727.4ms It seems that using functional components or normal components does not make much difference. Also, using a template or createElement()/h() does not make a difference either. Usually, it is best to keep the code as simple as possible until something is specifically proven to be a performance problem, instead of optimizing and complexifying, just in c Eventually, I can try to build 4 versions of the same component (Functional/Normal/Template/Programmed), and 4 test pages with the component repeated 100 times within a collapsible or something similar. |
I redid the tests with the regular component you provided, so everything is under v0.15.0. In both cases, everything is being re-rendered, this is strange, I don't know why yet, this would be where the biggest performance gain can be found. As for the difference between Functional and Regular components, they are almost the same, functional components loading a bit faster, and regular components faster for changes. Since most people will spend very little time loading and most of the time using the apps, the regular components might be better in most cases. It's also better to keep the code simpler and only complexify when really neccessary. I will try test more with different scenarios. Processing Time (ms) = Total - Idle Update Regular: 9804ms - 6070.2ms = 3733.8ms |
Important to note that loading times were 50% longer in v0.15, independent of Functional/Regular components in the test. I am not sure what is the cause for this... |
Awesome work here! Thank you! Will change all functional components to regular, then we'll see where we get from there. |
Pushed:
Can you test again and see how we perform now? I'd like to see a comparison between v0.14 - v0.15 before commit above, v0.15 after commit above. Thanks again for all the help! |
I have been working on various testing code and analysing the differences between the types of components. I will report results back shortly. |
It took me a while to figure out because the Quasar components where not behaving the way Vue says they should. All functional components get re-rendered whenever there is a reactive change anywhere in the DOM and regular components only re-render when the reactive change is relevant to them. However, because most of your parent components were functional, everything was being re-rendered all the time because if the parent functional component gets re-rendered, all of its children get re-rendered or worse, re-created for regular components. This is easy to test, put the functional version of your q-card surrounding any of your demo pages with regular components (eg button.vue), and add reactive variable anywhere on the page (even hidden and outside of the q-card), and update the field. This will cause all quasar components to re-render, including the regular quasar components. I created an app to test the two types of components, created two different ways and these are the results: Functional components can be up to 5 times faster to create and up to 2 times faster to update. BUT only if they do NO work and have NO regular child components! (see screen shot 1) Regular components that do alot of work (or have many children) will take the same time to create as functional components, but can be many times faster than functional components depending on the work they do and the children underneath since they will only do work required by the applicable reactivity. (see screen shot 2) And there is no real difference between using template or programming components, so it would seem that Template components would be a better choice to reduce complexity and increase maintainability where applicable. The app is in a pull request (#1366) now so you and others can test also. I included it under “web-test -> Performance” in the demo app. |
@rstoenescu any thoughts or questions on these results and the pull request? In summary:
|
Here are some more results. This compares two versions of the QIcon component for 10,000 instances. The QIcon is a relatively simple component without much code. Percentage of processing time of regular over functional components (positive number is regular is slower) Clearly, the initial loading is alot faster for the functional component vesion, and a change to a reactive prop (in this case, changing the icon) causing a re-render is also faster. However, the regular component is 13% faster for any external reactive update (eg, all changes outside of the icon component). It's a tradeoff depending on the needs. If it is a relatively static page, or one that is reloaded often or if the icons changes often, then a functional component would be faster over the long term. If it is a dynamic app with alot of changes happening constantly on the page, and mostly external to the icon (eg many modern SPAs), then the regular component will take less resources over the long term. The best of both worlds might be SSR and regular components, but this will require further tests. |
All functional components always get re-rendered, even the hidden ones. See the video below and note the number of recurrence in the console window.
This is by design in Vue.js (functional components are cheaper), and this might be optimized in the future, but it is worth considering, since it is not always benificial to use functional components.
vuejs/vue#4037
jorgebucaran/hyperapp#373
Quasar Framework v0.15
The text was updated successfully, but these errors were encountered: