简单的Vue SSR的示例代码(3)

  • 把全部 Ajax 数据埋在 window.INITIAL_STATE 中,通过 HTML 传递到浏览器端;
  • 浏览器端通过 Vuex 将 window.INITIAL_STATE 里面的 Ajax 数据分别注入到各个组件中。
  • 下面谈几个重点。

    我们知道,在常规的 Vue 前端渲染中,组件请求 Ajax 一般是这么写的:“在 mounted 中调用 this.fetchData,然后在回调里面把返回数据写到实例的 data 中,这就 ok 了。”

    在 SSR 中,这是不行的,因为服务器并不会执行 mounted 周期。那么我们是否可以把 this.fetchData

    提前到 created 或者 beforeCreate 这两个生命周期中执行?同样不行。原因是:this.fetchData 是异步请求,请求发出去之后,没等数据返回呢,后端就已经渲染完了,无法把 Ajax 返回的数据也一并渲染出来。

    所以,我们得提前知道都有哪些组件有 Ajax 请求,等把这些 Ajax 请求都返回了数据之后,才开始组件的渲染。

    // store.js
    function fetchBar() {
     return new Promise(function (resolve, reject) {
     resolve('bar ajax 返回数据');
     });
    }
    
    export default function createStore() {
     return new Vuex.Store({
     state: {
      bar: '',
     },
     actions: {
      fetchBar({commit}) {
      return fetchBar().then(msg => {
       commit('setBar', {msg})
      })
      }
     },
     mutations:{
      setBar(state, {msg}) {
      Vue.set(state, 'bar', msg);
      }
     }
     })
    }
    
    
    // Bar.uve
    asyncData({store}) {
     return store.dispatch('fetchBar');
    },
    computed: {
     bar() {
     return this.$store.state.bar;
     }
    }
    

    组件的 asyncData 方法已经定义好了,但是怎么索引到这个 asyncData 方法呢?先看我的根组件 App.vue 是怎么写的。

    // App.vue
    <template>
     <div>
     <h1>App.vue</h1>
     <p>vue with vue </p>
     <hr>
     <foo1 ref="foo_ref"></foo1>
     <bar1 ref="bar_ref"></bar1>
     <bar2 ref="bar_ref2"></bar2>
     </div>
    </template>
    <script>
     import Foo from './components/Foo.vue';
     import Bar from './components/Bar.vue';
    
     export default {
     components: {
      foo1: Foo,
      bar1: Bar,
      bar2: Bar
     }
     }
    </script>
    

    从根组件 App.vue 我们可以看到,只需要解析其 components 字段,便能依次找到各个组件的 asyncData 方法了。

    // entry-server.js 
    export default function (context) {
     // context 是 vue-server-render 注入的参数
     const store = createStore();
     let app = new Vue({
     store,
     render: h => h(App)
     });
    
     // 找到所有 asyncData 方法
     let components = App.components;
     let prefetchFns = [];
     for (let key in components) {
     if (!components.hasOwnProperty(key)) continue;
     let component = components[key];
     if(component.asyncData) {
      prefetchFns.push(component.asyncData({
      store
      }))
     }
     }
    
     return Promise.all(prefetchFns).then((res) => {
     // 在所有组件的 Ajax 都返回之后,才最终返回 app 进行渲染
     context.state = store.state;
     // context.state 赋值成什么,window.__INITIAL_STATE__ 就是什么
     return app;
     });
    };
          

    内容版权声明:除非注明,否则皆为本站原创文章。

    转载注明出处:http://www.heiqu.com/193.html