Open honghuangdc opened 2 years ago
Thanks for the issue! This issue has been labeled as holiday triage
. With the winter holidays quickly approaching, much of the Ionic Team will soon be taking time off. During this time, issue triaging and PR review will be delayed until the team begins to return. After this period, we will work to ensure that all new issues are properly triaged and that new PRs are reviewed.
In the meantime, please read our Winter Holiday Triage Guide for information on how to ensure that your issue is triaged correctly.
Thank you!
Hello @honghuangdc thank for the issue!
Excuse my lack of familiarity with Vue, but I don't believe onMounted
is guaranteed to execute after the view has completed rendering. The DOM reference values you are accessing won't be populated until the element has completed reflow & repaint.
Looking at the Vue docs, they recommend using $nextTick
in onMounted
when needing to run code after render: https://v3.vuejs.org/api/options-lifecycle-hooks.html#mounted
this.$nextTick(function () {
// Code that will run only after the
// entire view has been rendered
})
Can you let me know if this resolves your issue or if I am incorrect in my understanding of your issue? Thanks!
thanks your reply.if add nextTick function in lifecycle onMouted, it still has the same problem. It has nothing to do with vue, because there is no such problem in a vue project without ionic-vue. for example, create a dom reference which is the child element of body, but outside the ionic-vue component, those config values can be normally getted in lifecycle onMouted.
this problem bothers me so long, because some vue plugins which need dom reference to get those config values won’t run normally
Hello @honghuangdc thanks for your patience. Followed up with Liam to understand more about this issue.
In your example, since you are attaching to a div
, you will need to use the onIonViewDidEnter
lifecycle hook. The hook is fired at the same time the element is shown, so you will need to wait a frame for the browser to re-render the contents.
Let me know if that information helps or if you have additional questions. Thanks!
oh,when I met this problem, I already tried in this way, it still had the same problem.
and I did the same action in @ionic/react, it didn't have the problem.
We can probably improve this behavior. It seems that the page elements are not shown at the moment the "did enter" lifecycle is fired: https://github.com/ionic-team/ionic-framework/blob/main/packages/vue/src/components/IonRouterOutlet.ts#L347
They are shown 1-2 frames after which is why a setTimeout works.
Firing this in a requestAnimationFrame
or after components.value
is set (or both) may help.
@liamdebeasi boy it would be great if this were addressed at the framework level. I have to jump through so many hoops and end up with brittle code because even when the component is present in the dom, its shadow dom may not be rendered. In addition, if we are showing a modal, onIonViewDidEnter is not even fired.
Relying on timeouts or an indeterminate number of animation frames is, shall we say, less than optimal.
same situation +1
now, my solution is
const PageRouteWrapper = defineComponent((props, ctx) => {
const show = ref(false)
onIonViewDidEnter(() => {
setTimeout(() => (show.value = true), 50)
}, getCurrentInstance()?.parent)
return () => (
<IonPage>
<IonContent>{show.value ? ctx.slots.default?.() : null}</IonContent>
</IonPage>
)
})
const PageRoute = defineComponent(() => {
return () => (
<PageRouteWrapper>
<div>111111</div>
</PageRouteWrapper>
)
})
and i find setTimeout 0 is not work. it is better to set 50ms
Yup, but timeouts are inherently brittle. On a slow Android phone 50 ms may not be sufficient.
@aparajita Is there a precise timing for judgment ?
It's impossible to know what the correct timing is precisely. That's why we need it to be fixed at the framework level.
@sean-perkins @liamdebeasi
Can frameworks ensure a precise lifecycle for DOM elements to obtain their size information? Many UI frameworks like vant
depend on the onMounted event to dynamically calculate additional logic based on the DOM's size.
@aparajita How did you handle it in the end?
@aparajita How did you handle it in the end?
I tried various solutions, none of which were robust. Now I just try to avoid it as much as possible, and if I can't, I use a timeout.
How long did you use the timeout @aparajita
@sean-perkins @liamdebeasi
Can frameworks ensure a precise lifecycle for DOM elements to obtain their size information? Many UI frameworks like
vant
depend on the onMounted event to dynamically calculate additional logic based on the DOM's size.
Hi,
I don't work at Ionic anymore, so I'm not able to help prioritize this bug fix.
If anyone is interested in trying to contribute their own fix, this line of code may be a good place to start your investigation. This code was added to hide a layout shift. However, I believe we've improved tabs performance such that this fix may no longer be needed. You can probably try the reproduction steps in https://github.com/ionic-team/ionic-framework/issues/22052 to verify with the linked line of code removed.
The main issue here is that elements are being shown after onMounted
, so the bounding box will be 0x0 at the time that onMounted
fires.
I don't work at Ionic anymore
@liamdebeasi major bummer! Thanks for your fantastic support while you were at Ionic. Hope you got a better gig.
@aparajita I find a perfect way to resolve this situation. we can use resizeObserver
import { useResizeObserver } from '@vueuse/core'
const IonicWrapper = defineComponent((props, ctx) => {
const contentRef = ref()
const showContent = ref(false)
let now = 0
onMounted(() => (now = Date.now()))
const rob = useResizeObserver(contentRef, entries => {
const entry = entries[0]
const { width } = entry.contentRect
if (showContent.value) return
if (width !== 0) {
console.log('rect gap time', Date.now() - now)
showContent.value = true
rob.stop()
}
})
if (!rob.isSupported.value) {
onMounted(() => setTimeout(() => (showContent.value = true), 50))
}
return () => (
<IonPage>
<IonContent>
<div ref={contentRef} class={'h-full'}>{showContent.value ? ctx.slots.default?.() : null}</div>
</IonContent>
</IonPage>
)
})
const PageRoute = defineComponent(() => {
return () => (
<IonicWrapper>
<div>this child is main logic and other component</div>
</IonicWrapper>
)
})
i test it. it works perfect !
@aparajita I find a perfect way to resolve this situation. we can use resizeObserver
Very clever solution!
I have also encountered this problem when i use IonRouterOutlet component recently.I debbuged the source code and find it may be ralated to the lifecircle of ionic web components.the ionic use stenciljs to render web components,It executes renrder after the completion of connectedCallback event,then child element will be displayed.but vue exucute OnMounted after connectedCallback immediately.so it may be not display.i did this in order vue compoenent work will.
import { IonRouterOutlet as R } from "@ionic/core/components/ion-router-outlet.js";
let source = (R!.prototype as any).connectedCallback;
(R!.prototype as any).connectedCallback = async function () {
var th = this as any;
th.shadowRoot!.innerHTML = "<slot></slot>";
source.apply(this);
}
i set slot when connectedCallback executing so that child can be display immediately .
Prerequisites
Ionic Framework Version
Current Behavior
create a reference in dom, which is inside Icon-vue component, then get the config "offsetHeight", "offsetWidth", "clientHeight", "clientWidth" of the dom in lifecycle onMouted, but their value are all zero, and when I add setTimeout function to get config, it will get real value.
Expected Behavior
it can get the real value of config "offsetHeight", "offsetWidth", "clientHeight", "clientWidth" of the dom reference in lifecycle onMouted.
Steps to Reproduce
here is the code:
Code Reproduction URL
No response
Ionic Info
No response
Additional Information
No response