美文网首页
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

    上一节,已经完成了一套完整的ssr流程,但是server-entry.js的代码还是有些不健壮 因此修改如下: 解...

网友评论

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

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