How to load all server side data on initial vue.js / vue-router load?

后端 未结 5 493
长发绾君心
长发绾君心 2020-11-29 22:42

I\'m currently making use of the WordPress REST API, and vue-router to transition between pages on a small single page site. However, when I make an AJAX call to the server

相关标签:
5条回答
  • 2020-11-29 22:45

    Alright, I finally figured this thing out. All I'm doing is calling a synchronous ajax request within my main.js file where my root vue instance is instantiated, and assigning a data property the requested data as so:

    main.js

    let acfData;
    
    $.ajax({
        async: false,
        url: 'http://localhost/placeholder/wp-json/acf/v2/page/2',
        type: 'GET',
        success: function(response) {
            console.log(response.acf);
            acfData = response.acf;
        }.bind(this)
    })  
    
    const router = new VueRouter({
        routes: [
            { path: '/', component: Home },
            { path: '/about', component: About },
            { path: '/tickets', component: Tickets },
            { path: '/sponsors', component: Sponsors },
        ],
        hashbang: false
    });
    
    exports.router = router;
    
    const app = new Vue({
        router,
        data: {
            acfs: acfData 
        },
        created() {
    
        }
    }).$mount('#app')
    

    From here, I can use the pulled data within each individual .vue file / component like so:

    export default {
        name: 'app',
        data () {
        return {
            acf: this.$parent.acfs,
        }
    },
    

    Finally, I render the data within the same .vue template with the following:

    <template>
      <transition
          name="home"
          v-on:enter="enter"
          v-on:leave="leave"
          v-bind:css="false"
          mode="out-in"
        >
        <div class="full-height-container background-image home" v-bind:style="{backgroundImage: 'url(' + this.acf.home_background_image.url + ')'}">
          <div class="content-container">
            <h1 class="white bold home-title">{{ acf.home_title }}</h1>
            <h2 class="white home-subtitle">{{ acf.home_subtitle }}</h2>
            <div class="button-block">
              <a href="#/about"><button class="white home-button-1">{{ acf.link_title_1 }}</button></a>
              <a href="#/tickets"><button class="white home-button-2">{{ acf.link_title_2 }}</button></a>
            </div>
          </div>
        </div>
      </transition>
    </template>
    

    The most important piece of information to take away, is that all of the ACF data is only being called ONCE at the very beginning, compared to every time a route is visited using something like beforeRouteEnter (to, from, next). As a result, I'm able to get silky smooth page transitions as desired.

    Hope this helps whoever comes across the same problem.

    0 讨论(0)
  • 2020-11-29 22:54

    I've comprised my own version based on all the great responses to this post.. and several years having passed by as well giving me more tools.

    In main.js, I use async/await to call a prefetch service to load any data that must be there on startup. I find this increases readability. After I get the data comms, I then dispatch it to the appropriate vuex store module in the beforeCreate() hook.

    import Vue from 'vue';
    import App from './App.vue';
    import router from './router';
    import store from './store';
    
    import { prefetchAppData } from '@/services/prefetch.service';
    
    (async () => {
        let comms = await prefetchAppData();
    
        new Vue({
            router,
            store,
            beforeCreate() {
                store.dispatch('communityModule/initialize', comms);
            },
            mounted() {},
            render: h => h(App)
        }).$mount('#app');
    })();
    

    I feel compelled to warn those be careful what you prefetch. Try to do this sparingly as it does delay initial app loading which is not ideal for a good user experience.

    Here's my sample prefetch.service.js which does the data load. This of course could be more sophisticated.

    import api from '@api/community.api';
    export async function prefetchAppData() {
        return await api.getCommunities();
    }
    

    A simple vue store. This store maintains a list of 'communities' that the app requires to be loaded before application start.

    community.store.js (note im using vuex modules)

    export const communityModule = {
        namespaced: true,
        state: {
            communities: []
        },
        getters: {
            communities(state) {
                return state.communities;
            },
        },
        mutations: {
            SET_COMMUNITIES(state, communities) {
                state.communities = communities;
            }
        },
        actions: {
            // instead of loading data here, it is passed in 
            initialize({ commit }, comms) {
                commit('SET_COMMUNITIES', comms);
            }
        }
    };
    
    0 讨论(0)
  • 2020-11-29 22:58

    My approach is to delay construction of the store and main Vue until my AJAX call has returned.

    store.js

    import Vue from 'vue';
    import Vuex from 'vuex';
    import actions from './actions';
    import getters from './getters';
    import mutations from './mutations';
    
    Vue.use(Vuex);
    
    function builder(data) {
      return new Vuex.Store({
        state: {
          exams: data,
        },
        actions,
        getters,
        mutations,
      });
    }
    
    export default builder;
    

    main.js

    import Vue from 'vue';
    import VueResource from 'vue-resource';
    import App from './App';
    import router from './router';
    import store from './store';
    
    Vue.config.productionTip = false;
    
    Vue.use(VueResource);
    
    Vue.http.options.root = 'https://miguelmartinez.com/api/';
    
    Vue.http.get('data')
      .then(response => response.json())
      .then((data) => {
        /* eslint-disable no-new */
        new Vue({
          el: '#app',
          router,
          store: store(data),
          template: '<App/>',
          components: { App },
        });
      });
    

    I have used this approach with other frameworks such as Angular and ExtJS.

    0 讨论(0)
  • 2020-11-29 23:00

    You can use navigation guards.

    On a specific component, it would look like this:

    export default {
        beforeRouteEnter (to, from, next) {
            // my ajax call
        }
    };
    

    You can also add a navigation guard to all components:

    router.beforeEach((to, from, next) => {
        // my ajax call
    });
    

    One thing to remember is that navigation guards are async, so you need to call the next() callback when the data loading is finished. A real example from my app (where the guard function resides in a separate file):

    export default function(to, from, next) {
        Promise.all([
            IngredientTypes.init(),
            Units.init(),
            MashTypes.init()
        ]).then(() => {
            next();
        });
    };
    

    In your case, you'd need to call next() in the success callback, of course.

    0 讨论(0)
  • 2020-11-29 23:05

    Check this section in docs of Vue Router

    https://router.vuejs.org/guide/advanced/data-fetching.html

    So first of you have to write method that would fetch data from your endpoint, and then use watcher to watch route.

    export default {
        watch: {
            '$route': 'fetchItems'
        },
        methods: {
            fetchItems() {
              // fetch logic
            }
        }
    }
    

    Since you are working with WP Rest API, feel free to check my repo on Github https://github.com/bedakb/vuewp/blob/master/public/app/themes/vuewp/app/views/PostView.vue#L39

    0 讨论(0)
提交回复
热议问题