-
-
Notifications
You must be signed in to change notification settings - Fork 8.4k
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
perf: lazy init hoisted vnodes #10959
Conversation
Size ReportBundles
Usages
|
Co-authored-by: sxzz <[email protected]> Co-authored-by: Doctor-wu <[email protected]> Co-authored-by: ShenQingchuan <[email protected]>
I am wondering if it is worth it to init hoisted v-nodes lazily🥺
I don't know compilers and optimization at all and these are just my personal thoughts. Please point out if I'm wrong🫡. |
Actually, this PR will not fix #5256 export function render(_ctx, _cache) {
return (_openBlock(), _createElementBlock("div", null, [
_cache[1] || (_cache[1] = _hoisted_1()),
_createTextVNode(_toDisplayString(_ctx.foo), 1 /* TEXT */),
_cache[2] || (_cache[2] = _hoisted_2())
]))
} |
After a second thought, I think having a flag to opt in is probably better. The lazies might have benefit only when many components bundled into a single entry, where in normal app structure, components especially routes are splitter intro dynamic chunks, which by itself is lazy already. Maybe it can be an optimization for component libraries that really need this, but I also doubt if this would be useful to users' daily app |
It wasn't immediately clear to me from the title and description, but this change affects all VNode-related hoisting, not just the hoisting of the VNodes themselves. e.g. Hoisted prop objects and dynamic prop arrays. So this template code: <div :data-a="msg"> would now generate: const _hoisted_1 = /*#__PURE__*/ _hoistLazy(() => (["data-a"])) instead of: const _hoisted_1 = ["data-a"] I'm wondering whether there's any real benefit here. Is creating a function really cheaper than creating the array directly? I did a very small (not very scientific) benchmark and it seemed that the function would be much slower. That was just measuring the cost of creating the function, not even taking into account the cost of calling it. Has any benchmarking been done to confirm that this change actually yields a performance benefit? I had a look through a few popular libraries to see how much hoisting they do. Element Plus had the most hoisting of the libraries I checked. It seems to have 186 hoisted 'things', but only 3 of those are VNodes. The majority are dynamic prop lists, so just arrays of strings. |
Yeah, good points @skirtles-code, I shared the concerns as well. I didn't do much benchmark, but this was just a quick idea during my discussion with @sxzz, in which he might have better context for this). I think at least this PR should not be merged as-is, and we either need to introduce a flag to opt-in, or/and to have a smart detection based on how heavy the hosted element is (in cases like markdown rendering might be useful, but I would also doubt it as VitePress already doing some optimization to remove static notes), not maybe this is not needed at all. Making the PR as draft for now. But let's keep discussing / evaluations. |
For a bit more context, this was brought up when we were discussing about the There this PR only addressed the As typing my comments, I just came up with another idea: Maybe instead of turning each hoisted object into a function, we might wrap the entire component along with the hoisted into a lazy function? Persudo code: let __component
export default defineLazyComponent(() => {
// use cache
if (__component) return __component
// imaginal hook
onAllInstancedUnmounted(() => {
// feel the memory
__component = undefined
})
// the rest are normal component dist
// and we put hoisted values here
const _hoisted_1 = /*#__PURE__*/ _createElementVNode('div', null, 'Hi', -1 /* HOISTED */)
const _hoisted_2 = ['data-id']
__component = {
__name: 'App',
setup(__props) {
const msg = ref('Hello World!')
return (_ctx, _cache) => {
return (
_openBlock(),
_createElementBlock(
_Fragment,
null,
[_createElementVNode('h1', null, _toDisplayString(msg.value), 1 /* TEXT */), _hoisted_1, _createElementVNode('div', { 'data-id': msg.value }, 'Hello World', 8 /* PROPS */, _hoisted_2)],
64 /* STABLE_FRAGMENT */,
)
)
}
},
}
return __component
}) That said, I suppose it still deps on if we really this and probably not by default for everything. |
In large projects with too many hoisted parts bundled into a single file, end users need to pay the cost of initiating the hosted nodes upfront, which might ideal and might lead to slow init loading. This PR introduces a lazy utility to initiate those hosted vnodes on demand.
Thanks to @sxzz @Doctor-wu @ShenQingchuan
Fixed #5256