简体   繁体   English

如何在初始 vue.js / vue-router 加载时加载所有服务器端数据?

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

I'm currently making use of the WordPress REST API, and vue-router to transition between pages on a small single page site.我目前正在使用 WordPress REST API 和 vue-router 在一个小的单页面站点上的页面之间进行转换。 However, when I make an AJAX call to the server using the REST API, the data loads, but only after the page has already rendered.但是,当我使用 REST API 对服务器进行 AJAX 调用时,数据会加载,但仅在页面已呈现之后。

The vue-router documentation provides insight in regards to how to load data before and after navigating to each route, but I'd like to know how to load all route and page data on the initial page load, circumventing the need to load data each time a route is activated. vue-router 文档提供了有关如何在导航到每个路由之前和之后加载数据的见解,但我想知道如何在初始页面加载时加载所有路由和页面数据,从而避免每次加载数据的需要激活路由的时间。

Note, I'm loading my data into the acf property, and then accessing it within a .vue file component using this.$parent.acfs .请注意,我将数据加载到acf属性中,然后使用this.$parent.acfs.vue文件组件中访问它。

main.js Router Code: main.js 路由器代码:

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: ''
    },
    created() {
        $.ajax({
            url: 'http://localhost/placeholder/wp-json/acf/v2/page/2',
            type: 'GET',
            success: function(response) {
                console.log(response);
                this.acfs = response.acf;
                // this.backgroundImage = response.acf.background_image.url
            }.bind(this)
        })
    }
}).$mount('#app')

Home.vue Component Code: Home.vue 组件代码:

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

Any ideas?有任何想法吗?

My approach is to delay construction of the store and main Vue until my AJAX call has returned.我的方法是延迟 store 和 main Vue 的构建,直到我的 AJAX 调用返回。

store.js商店.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.我已经将这种方法用于其他框架,例如 Angular 和 ExtJS。

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.要记住的一件事是导航守卫是异步的,因此您需要在数据加载完成后调用next()回调。 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.在您的情况下,您当然需要在success回调中调用next()

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文件中调用一个同步的 ajax 请求,我的根 vue 实例被实例化,并为请求的数据分配一个数据属性,如下所示:

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:从这里,我可以在每个单独的.vue文件/组件中使用提取的数据,如下所示:

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

Finally, I render the data within the same .vue template with the following:最后,我使用以下内容在同一个.vue模板中渲染数据:

<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) .要带走的最重要的信息是,与每次使用beforeRouteEnter (to, from, next)类的东西访问路线时相比,所有 ACF 数据在开始时仅被调用一次。 As a result, I'm able to get silky smooth page transitions as desired.因此,我能够根据需要获得如丝般平滑的页面过渡。

Hope this helps whoever comes across the same problem.希望这可以帮助遇到同样问题的人。

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.main.js 中,我使用 async/await 调用预取服务来加载启动时必须存在的任何数据。 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.获得数据comms 后,我将其分派到 beforeCreate() 钩子中的适当 vuex 存储模块。

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.这是我执行数据加载的示例prefetch.service.js 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.一个简单的vue商店。 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) community.store.js (注意我使用 vuex 模块)

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);
        }
    }
};

Check this section in docs of Vue Router在 Vue Router 的文档中查看此部分

https://router.vuejs.org/guide/advanced/data-fetching.html 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由于您正在使用 WP Rest API,请随时查看我在 Github 上的 repo https://github.com/bedakb/vuewp/blob/master/public/app/themes/vuewp/app/views/PostView.vue#L39

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM