Express实现Vue2的SSR路由同步

  1. 一、Vue为啥要使用SSR

    本人是菜鸟一个,这个文章可能有误导性
    服务器要展示vue的页面,需要webpack编译的两类文件,html模板和main.js,main.js中包含vue框架,我们写的业务逻辑,css,图片等

在这里插入图片描述
如图 webpack编译的.js文件在dist中,然后views中引入这些文件,
原始模板文件是在这样的
views中webpack编译后的mian.html

在这里插入图片描述

这就是服务器返回浏览器端的HTML代码

浏览器加载之后的HTML
在这里插入图片描述
服务加载过来的HTML和展示的HTML不一样,是由于加载的.js 文件中的代码对当前HTML进行了操作,这个操作是在浏览器端的,如果不让.js加载,
在这里插入图片描述
然后就和服务器加载过来的模板是一样的,
在这里插入图片描述
由于内容都是本地js被添加上去的,所以当HTML加载过来的瞬间,其实就是一个没有啥内容的模板,这是不利于SEO的,百度等搜索引擎无法获取到,对于js后添加的内容,他只能看到加载过来的模板。

SSR就是提前在服务端将上面 id=“app” 的这个模板变提成HTML,后面的交互操作依然由浏览器请求的.js等文件接手,

原始模板:

 <body>

   <!--vue-ssr-outlet-->

 </body>

服务端获取编译结果,替换模板:

let { createBundleRenderer } = require("vue-server-renderer");

// 创建渲染器
let renderer = createBundleRenderer(path.join(root, "server/bundle.json"), {
  // 模板文件
  template: fs.readFileSync(path.join(root, "views/index.html"), "utf-8"), // 这里必须设置为utf-8 ,并且都必须使用绝对路径
});

// 注意.js一定要先解析,不然会被下面的覆盖,都填充为模板

app.use("/static/", express.static("./static/"));
// 定义路由

// 下面这种最好前端一样,统一为为/xxx,而不是使用*,不然所谓请求文件都会被填充为模板
app.get("*", (req, res) => {
  /// 渲染字符串
  renderer
    .renderToString({
      url: req.url,
      title: "xxxx",
      seo: `
    <meta name="keywords" content="xxxx">
    <meta name="description" content="xxx">
    `,
    })
    .then(
      (html) => {
        /// 成功
        // console.log(html);
        res.end(html);
      },
      (err) => {
        // 失败
        console.log(err);
        if (err.code == 404) {
          res.status(404).end(err.msg + "=>" + req.url);
        } else {
          res.status(500).end("服务器内部错误");
        }
      }
    );
});

服务器替换后的模板就是已经渲染的HTML字符串了:

<body><!-- <h1> HTML文件中中的 
标签 </h1>
   <div id="app">
     <h2>xxx</h2>
   </div> --><div id="app" data-server-rendered="true"><nav><h1>app part --helllo--10</h1> <a href="/" aria-current="page" class="router-link-exact-active router-link-active">Home</a>
    |
    <a href="/about">About</a>|
    <a href="/test">Test</a>|
  1. 路由同步

但是还有一个问题,就是如果是使用了SSR,那么必须使用history模式,默认模式是不刷新页面,渲染好的HTML数据无法从服务器加载过来,没有seo的效果。如果使用history模式,当前前端请求一个路由的时候,后端是不知道这个路由该返回这个路径对应渲染的HTML数据的,所以服务端渲染HTML时要得到请求的路径,对这个路径的页面HTML字符串渲染返回,

// 下面这种最好前端一样,统一为为/xxx,而不是使用*,不然所有请求文件都会被填充
app.get("*", (req, res) => {
  /// 渲染字符串
  renderer
    .renderToString({
      url: req.url,//传入对应的url
      title: "xxxx",
      seo: `
    <meta name="keywords" content="xxxx">
    <meta name="description" content="xxx">
    `,
    })
    .then(
      (html) => {
        /// 成功
        console.log(html);
        res.end(html);// 返回最终填充好的HTML
      },
      (err) => {
        // 失败
        console.log(err);
        if (err.code == 404) {
          res.status(404).end(err.msg + "=>" + req.url);
        } else {
          res.status(500).end("服务器内部错误");
        }
      }
    );
});

获得url后,查看url是否在路由中,

import app from "./main";

// 引入路由实例化对象
import router from "./router/index";

///export default app;

// 暴露接口
export default (data) =>
  new Promise((resolve, reject) => {
    // data就在rederTo中传递的url
    console.log("url", data.url);

    // 解析路由
    router.onReady(() => {
      // 查看是否有匹配的页面
      console.log(router.getMatchedComponents());

      // 如果有匹配的页面
      if (router.getMatchedComponents().length) {
        // 成功
        resolve(app);
      } else {
        // 没有匹配页面
        reject({
          code: 404,
          msg: "没有找到页面",
        });
      }
    });

    // 解析路由
    router.push(data.url);
  });
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值