【问题标题】:How to load all server side data on initial vue.js / vue-router load?如何在初始 vue.js / vue-router 加载时加载所有服务器端数据?
【发布时间】:2017-05-25 04:29:35
【问题描述】:

我目前正在使用 WordPress REST API 和 vue-router 在小型单页网站上的页面之间进行转换。但是,当我使用 REST API 对服务器进行 AJAX 调用时,数据会加载,但仅在页面已经呈现之后。

vue-router documentation 提供了有关如何在导航到每条路线之前和之后加载数据的见解,但我想知道如何在初始页面加载时加载所有路线和页面数据,从而避免需要加载每次激活路线时的数据。

注意,我将数据加载到 acf 属性中,然后使用 this.$parent.acfs.vue 文件组件中访问它。

ma​​in.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 组件代码:

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

有什么想法吗?

【问题讨论】:

    标签: javascript jquery ajax wordpress vue.js


    【解决方案1】:

    您可以使用navigation guards

    在特定组件上,它看起来像这样:

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

    您还可以为所有组件添加导航保护:

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

    要记住的一点是导航守卫是异步的,因此您需要在数据加载完成时调用next() 回调。我的应用程序中的一个真实示例(其中保护功能位于单独的文件中):

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

    在您的情况下,您当然需要在success 回调中调用next()

    【讨论】:

    • 如果我使用router.beforeEach(),我应该在哪里存储我的AJAX响应数据?是否有我应该将其存储在$route.params 之类的路由对象?
    • 另外,有没有办法确保数据只加载一次,而不是在页面之间来回切换时多次加载?
    • 我为此使用服务类(IngredientTypes 等)。它们存储数据,任何组件都可以使用它们。当他们已经存储了数据时,他们只是不发送另一个请求,而是立即返回一个已解决的承诺。我也知道有一个状态管理工具叫vuex,理论上也可以帮你实现你想要的,但是我没用过,所以不能提供任何可靠的信息。
    • 友情提示;这变得非常混乱,非常快
    • @ZacJacob ...希望我在 2 年前看到你的警告,因为你是对的。搞得一团糟
    【解决方案2】:

    在 Vue Router 的文档中查看此部分

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

    所以首先你们必须编写从端点获取数据的方法,然后使用 watcher 来观察路由。

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

    由于您正在使用 WP Rest API,请随时查看我在 Github 上的 repo https://github.com/bedakb/vuewp/blob/master/public/app/themes/vuewp/app/views/PostView.vue#L39

    【讨论】:

      【解决方案3】:

      好吧,我终于想通了。我所做的就是在我的 main.js 文件中调用同步 ajax 请求,在该文件中实例化了我的根 vue 实例,并为请求的数据分配数据属性:

      ma​​in.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')
      

      从这里,我可以像这样在每个 .vue 文件/组件中使用提取的数据:

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

      最后,我在同一个 .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>
      

      要带走的最重要的信息是,与每次使用beforeRouteEnter (to, from, next) 之类的方式访问路线相比,所有 ACF 数据只在一开始就被调用一次。因此,我能够根据需要获得如丝般平滑的页面过渡。

      希望这对遇到同样问题的人有所帮助。

      【讨论】:

        【解决方案4】:

        我的方法是延迟构建 store 和 main Vue,直到我的 AJAX 调用返回。

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

        我已经将此方法用于其他框架,例如 Angular 和 ExtJS。

        【讨论】:

        • 这很聪明。
        • 太棒了-谢谢。我在 12 小时内学会了 Webpack/Vue/vue-cli,但三天都无法理解 Vuex 并正常工作。直到这个。很好的答案@Miguel
        • 不错!我也这样做了……我来这里只是因为我不确定这是否是好的做法,但很高兴看到其他人认为这是个好主意。
        • 如果您不想让store 成为构建器,您可以在您的ajax 承诺解决后调用store.commit('set_exams', data)。这假设您有相关的突变来执行此操作。
        • @NtwariClaranceLiberiste 您可以在浏览器中使用fetch() 或导入Axios 之类的东西来进行API 调用,而不是Vue.http()
        【解决方案5】:

        我根据对这篇文章的所有好评组成了我自己的版本。几年过去了,也给了我更多的工具。

        ma​​in.js 中,我使用 async/await 调用预取服务来加载启动时必须存在的任何数据。我发现这增加了可读性。在我得到数据 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');
        })();
        

        我不得不警告那些预取的东西要小心。尽量谨慎地执行此操作,因为它会延迟初始应用加载,这对于良好的用户体验来说并不理想。

        这是我的示例 prefetch.service.js,它负责数据加载。这当然可以更复杂。

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

        一个简单的 Vue 商店。该商店维护应用程序启动前需要加载的“社区”列表。

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

        【讨论】:

          猜你喜欢
          • 2022-06-15
          • 2016-08-27
          • 2017-09-28
          • 1970-01-01
          • 1970-01-01
          • 2018-11-19
          • 1970-01-01
          • 1970-01-01
          相关资源
          最近更新 更多