universal-vue / uvue

Vue CLI plugin to create universal Vue applications with ease
https://universal-vue.github.io/docs/
MIT License
127 stars 13 forks source link

Vue 2.6 / serverPrefetch hook #25

Closed yabab-dev closed 4 years ago

yabab-dev commented 5 years ago

DEPRECATED

Below component and mixin have been removed, please see new prefetch hook

Hey folks, Vue 2.6 is here with a great new feature for SSR: serverPrefetch() hook

Native approach

You can find this example in official SSR documentation:

<template>
  <div v-if="item">{{ item.title }}</div>
  <div v-else>...</div>
</template>

<script>
  export default {
    computed: {
      // display the item from store state.
      item() {
        return this.$store.state.items[this.$route.params.id];
      },
    },

    serverPrefetch() {
      return this.fetchItem();
    },

    // Client-side only
    mounted() {
      if (!this.item) {
        this.fetchItem();
      }
    },

    methods: {
      fetchItem() {
        // return the Promise from the action
        return store.dispatch('fetchItem', this.$route.params.id);
      },
    },
  };
</script>

~~It's very simple to setup prefetching on server side with this, and this hook is available on all your components (not only views/pages components)!~~

Second advantage is you have access to this and you can call computed, methods and plugins in it!

~~But compared to asyncData() plugin you have one downside with serverPrefetch hook: you have to duplicate your code in serverPrefetch and in mounted hooks.~~

Prefetch: renderless component

I've made some little experiments with this new serverPrefetch, here is the first one:

<Prefetch :promise="() => $http.get('/api/data')" v-slot="{ pending, result, error }">
  <template v-if="pending">
    Loading...
  </template>
  <template v-else-if="result">
    Here is your data: {{ result }}
  </template>
  <template v-else>
    Error!
  </template>
</Prefetch>

~~As you can see is a renderless component which can resolve any promise and return results in scoped slot. Of course this component is SSR ready, so on a HTTP request, promise will be resolved and rendered on server side, then client side will mount it with prefetched data.~~

~~There is a little dirty thing in there: you have to pass a function that return a promise as prop of this component. This trick avoid to execute promise twice, one time when the component is created on server side, second time when component is created on client side.~~

To use it:

import { Prefetch } from '@uvue/core';

Vue.component('Prefetch', Prefetch);

~~This is great, but some of you don't want to see Prefetch components everywhere in their code, so come the second one:~~

Prefetch: mixin

<template>
  <div>
    Here is your data: {{ result }}
  </div>
</template>

<script>
  import { PrefetchMixin } from '@uvue/core';

  export default {
    mixins: [PrefetchMixin],

    data: () => ({
      result: null,
    }),

    async prefetch() {
      this.result = await this.$http.get('/api/data');
    },
  };
</script>

~~This mixin will do the same has serverPrefetch hook but here you declare your code only once!~~

To make this available everywhere:

import { PrefetchMixin } from '@uvue/core';

Vue.mixin(PrefetchMixin)

So, asyncData and fetch will be removed ?

~~Nope! Mainly because these two hooks use beforeResolve router navigation guard. Main advantage of this method is that page change only occur when data is fully loaded.~~

~~Second reason to keep these plugins is that usage of serverPrefetch can be dangerous: imagine you have 10 components in one page that does server prefetching: this can bloat your page load time and can be difficult to debug.~~

I think it's good practice to keep your data loading on a view/page level to avoid this issue.

camwhite commented 5 years ago

A+ thinking with the mixin, actually cleans the native approach a lot!