美文网首页
vuex+vue-router-ssr

vuex+vue-router-ssr

作者: 成熟稳重的李先生 | 来源:发表于2020-07-13 22:30 被阅读0次

    上一节,已经完成了一套完整的ssr流程,但是server-entry.js的代码还是有些不健壮

    import createApp from "./app.js";
    
    export default (context) => {
      let { app, router } = createApp();
      router.push(context.url); // 如果这个路由到的组件为异步组件,那么接下来的return app拿到的应用不一定会有完整的组件
      // router路由对象
      return app;
    };
    

    因此修改如下:

    import createApp from "./app.js";
    
    // 这里服务端渲染要求打包后的结果需要返回一个函数
    
    export default (context) => {
      // 因为有可能会是异步路由钩子函数或组件,所以我们将返回一个 Promise,
      // 以便服务器能够等待所有的内容在渲染前,
      // 就已经准备就绪。
      return new Promise((resolve, reject) => {
        const { app, router } = createApp();
        router.push(context.url);
        router.onReady(() => {
          const matchedComponents = router.getMatchedComponents();
          // 匹配不到的路由,执行 reject 函数,并返回 404
          if (!matchedComponents.length) {
            return reject({ code: 404 });  // 这里reject后,就会走到server.js中的render.renderToString中的reject
          }
    
          // Promise 应该 resolve 应用程序实例,以便它可以渲染
          resolve(app);
        }, reject);
      });
    };
    
    //----------server.js
    router.get("(.*)", async (ctx) => {
      try {
        console.log(ctx.path);
        ctx.body = await new Promise((resolve, reject) => {
          render.renderToString({ url: ctx.path }, (err, data) => {
            if (err) {
              reject(err);  // 404时
            } else {
              resolve(data);
            }
          });
        });
      } catch (e) {  //捕获到404
        ctx.body = "Page Not Found";
      }
    });
    

    解决问题!
    下一步,添加vuex
    新建store.js

    import Vue from "vue";
    
    import Vuex from "vuex";
    
    Vue.use(Vuex);
    
    export default () => {
      let store = new Vuex.Store({
        state: {
          name: "",
        },
        mutations: {
          changeName(state) {
            state.name = "zf";
          },
        },
        actions: {
          changeName({ commit }) {
            // 模拟的数据请求 axios
            return new Promise((resolve, reject) => {
              setTimeout(() => {
                commit("changeName");
                resolve();
              }, 5000);
            });
          },
        },
      });
      if (typeof window !== "undefined") {
        // 服务环境没有window属性 只有客户端具备window属性
        if (window.__INITIAL_STATE__) {  // 这里是在server-entry中改变完state数据后, context.state自动回转化为weindow.__INITIAL_STATE__
          store.replaceState(window.__INITIAL_STATE__);
        }
      }
      return store;
    };
    //--------------store.js结束------------
    //--------------server.entry.js
    import createApp from "./app.js";
    
    // 这里服务端渲染要求打包后的结果需要返回一个函数
    
    // 服务端稍后会调用函数 传递一些参数到这个函数中
    export default (context) => {
      // 后端执行的
      return new Promise((resolve, reject) => {
        let { app, router, store } = createApp();
        router.push(context.url);
    
        // 等待路由中的钩子函数 执行完毕后才执行渲染逻辑
        router.onReady(() => {
          // 我们应该看一下 到底有没有这个路径 如果有这个路径 才去渲染
          let matchComponents = router.getMatchedComponents(); // 查看是否路由对应有匹配到的组件
          if (!matchComponents.length) {
            return reject({ code: 404 }); // 配置404页面
          }
    
          // matchComponents 是当前路由匹配到的组件 这个组件里可能会写asyncData方法
          Promise.all(
            matchComponents.map((comp) => {
              return comp.asyncData && comp.asyncData(store); // 这里你可能更改了状态。最终渲染的时候 我希望拿到更改的状态
            })
          ).then(
            () => {
              // 默认渲染时window.__initState__
              context.state = store.state; // 将刚才在服务端调用的vuex的结果放到当前的上下汶上
              resolve(app);
            },
            (err) => {
              reject(err); // 如果请求失败了 会不挂
            }
          );
        }, reject);
      });
    };
    //------------server-entry.js结束
    //------------bar.vue---------- 
    <template>
      <div>bar {{$store.state.name}}</div>
    </template>
    <script>
    export default {
      mounted() {
        // mounted 服务端不会触发此逻辑 因为服务端没有dom结构
        this.$store.dispatch("changeName");
      },
      // ssr 只有首屏 如果已经运行起来了 后续逻辑 都是单页应用里的
      asyncData(store) {
        // 规定这个方法可以在后端执行 (只能在页面级组件使用)
        return store.dispatch("changeName");
      }
    };
    </script>
    <style scoped>
    div {
      background: red;
    }
    </style>

    相关文章

      网友评论

          本文标题:vuex+vue-router-ssr

          本文链接:https://www.haomeiwen.com/subject/fzewcktx.html