AlexandreBonaventure / vue-mq

📱 💻 Define your breakpoints and build responsive design semantically and declaratively in a mobile-first way with Vue.
https://alexandrebonaventure.github.io/vue-mq
MIT License
537 stars 59 forks source link

The value of $mq is not correct on mouted #39

Open bravelincy opened 5 years ago

bravelincy commented 5 years ago
// main.js
Vue.use(VueMq, { 
  breakpoints: {
    xs: 468,
    sm: 768,
    md: 1080,
    lg: Infinity
  }
})

// App.js
{
  mounted () {
    console.log(this.$mq) // sm, but the viewport size was matched `lg`
  }
}
do-adams commented 5 years ago

I am having this same issue but on a computed property that references $mq. The value comes across as 'sm' despite the browser window being visibly larger than the 'sm' width.

do-adams commented 5 years ago

Actually, upon further testing I have determined that the value of $mq for me is correct at mounted() time.

However, peeking into the source code a bit, the vue-mq plugin defaults to breakpoint size sm for all of the Vue lifecycle instance hooks up until mounted(), which causes elements that rely on v-if="$mq === 'sm'" to actually be rendered initially before being removed from the DOM.

In my case, setting the defaultBreakpoint for the plugin to lg fixed the issue.

supremeo commented 5 years ago

In my case, setting the defaultBreakpoint for the plugin to lg fixed the issue.

That doesn't fix the issue. Resize your browser down and reload the page. You'll see the same issue persist. For the issue to be resolved, defaultBreakpoint would need to be deviceWidth aware and render the appropriate breakpoint at SSR

flyingL123 commented 4 years ago

@do-adams I am having the exact same problem. I am switching layouts depending on the value of $mq:

<template>
    <div class="tw-pt-4">
        <product-body-mobile v-if="$mq == 'sm'"/>
        <product-body-desktop v-else/>
    </div>
</template>

I am finding then when I load the page on a md screen, the mounted hooks are being called for components within the product-body-mobile component. This is really frustrating. Have you tried using the <mq-layout> components instead of v-if to see if that resolves the problem?

flyingL123 commented 4 years ago

That doesn't seem to make a difference based on my testing. I'm pretty confused by this. Isn't this a pretty major issue with the library?

flyingL123 commented 4 years ago

It definitely seems like this shouldn't be necessary, and I'm guessing there are probably a bunch of performance-related reasons not to do this, but in case it helps anyone else, I was able to solve the problem by wrapping everything in a conditional that depends on whether or not the mounted hook has been called:

<template>
    <div class="tw-pt-4">
        <template v-if="didMount">
            <product-body-mobile v-if="$mq == 'sm'"/>
            <product-body-desktop v-else/>
        </template>
    </div>
</template>
data () {
    return {
        didMount: false
    };
},

mounted () {
    this.didMount = true;
}

Again, not thrilled this is necessary, but it seems to work for me and I'm only using this conditional rendering in a few places, so it will have to do for now.

If anyone sees any issues with this, please do let me know.

Aztriltus commented 4 years ago

Hi @flyingL123, I came to the same conclusion as you did, by using the mounted hook to set the correct layout since $mq defaults to something and changes after it is mounted. So when my web page loads on a tablet+ resolution, it loaded the mobile layout before it changed to the tablet layout after it is mounted, causing the page to look broken at the start.

However, there is still a problem with this 'solution'. When using pre-rendered pages such as the 'nuxt generate' command, the didMount property will stay false as there is no mounted() when generating. Thus, the generated html are all empty since v-if is always false.

I think the best implementation might be a $mq.isLoading feature to check if the mq has fully loaded, before setting the $mq property. I have tried other ways but nuxt doesn't seem to allow me to set a new mode i.e. nuxt generate -- --mode generate. I thought I could do didMount: process.env.NODE_ENV === 'generate' but it still doesn't work. Man... I'm out of ideas here.

DennisMaass commented 4 years ago

I fixed it for clientside rendering with

Vue.use(VueMq, { 
  breakpoints: {
    xs: 468,
    sm: 768,
    md: 1080,
    lg: Infinity
  },
  defaultBreakpoint: ""
})

So the initial value is not "sm" and in principle it does the same as

mounted () {
    this.didMount = true;
}
flyingL123 commented 4 years ago

Hi @DennisMaass this actually didn't work for me. It solved one problem but then created the same problem in reverse. With your method, when I load the page on a desktop screen, the mobile mounted hooks are correctly not called. However, now when I load the page on a mobile screen, the desktop hooks get called. So it's the same problem as before, but in reverse.

Are you seeing that same behavior?

Aztriltus commented 4 years ago

I have the same outcome as @flyingL123. Defaulting to mobile or tablet will cause the other breakpoints to have issues as described by his comment.

On 14 Dec 2019, at 1:38 AM, flyingL123 notifications@github.com wrote:

Hi @DennisMaass https://github.com/DennisMaass this actually didn't work for me. It solved one problem but then created the same problem in reverse. With your method, when I load the page on a desktop screen, the mobile mounted hooks aren't called. So it solved that problem. However, now when I load the page on a mobile screen, the desktop hooks get called. So it's the same problem as before, but in reverse.

Are you seeing that same behavior?

— You are receiving this because you commented. Reply to this email directly, view it on GitHub https://github.com/AlexandreBonaventure/vue-mq/issues/39?email_source=notifications&email_token=ABNZS6DBPJ7UEZCSX77227DQYPCDHA5CNFSM4IFULVMKYY3PNVWWK3TUL52HS4DFVREXG43VMVBW63LNMVXHJKTDN5WW2ZLOORPWSZGOEG2WEYI#issuecomment-565535329, or unsubscribe https://github.com/notifications/unsubscribe-auth/ABNZS6CD3FCHMGXP5KVB5RDQYPCDHANCNFSM4IFULVMA.

Aztriltus commented 4 years ago

So I've been thinking about this issue for sometime now.

My problem was when I use my 'mobile' breakpoint as defaultBreakpoint, loading my webpage in 'tablet' or 'desktop' breakpoints would appear broken before $mq is correctly set to 'tablet' or 'desktop'. So it'd flash the 'mobile' layout for about 1-2s before my layout changes to the correct 'tablet or 'desktop' layouts.

My solution to this problem was using v-show on the root-most element that is NOT \<template>

<div id="app" v-show="isLoaded">
  <template v-if="$mq === 'mobile'> ... </template>
  <template v-else> ... </template>
</div>
data() {
  return {
    isLoaded: false
  }
},
mounted() {
  this.isLoaded = true
}

When using 'nuxt generate', the mounted hook will not be called, thus isLoaded will always be false. Since I used v-show instead of v-if, the entire app will simply be hidden and still be available in the DOM, allowing my meta-tags to work. Previously, I tried v-if="isLoaded" and my webpages would come up empty.

I hope this would help those who statically generate their apps while using breakpoints.