vue-cli3搭建的vue改造成SSR项目

2023-05-16

vue-cli3搭建的vue改造成SSR项目

  • 一、文章简介
  • 二、搭建vue项目
  • 三、改造成SSR
    • 首先安装ssr的所需相关依赖
    • 改造router.ts
    • 改造store.ts
    • 改造main.ts
    • 创建entry-client.ts
    • 创建entry-server.ts
    • 创建index.template.html
    • 创建server.ts
    • 构建配置
    • 修改package.json
  • 四、启动项目

一、文章简介

本文是本人一个随记,主要是讲述的是vue+typescript+ssr+koa2项目基础搭建。

二、搭建vue项目

1、创建项目

vue create vue-ssr

在这里插入图片描述
由于我的工程比较大,所以需要的东西比较多,请按各自项目需求选择;
后面主要的选择node-sass、ESLint + Prettier、Lint and fix on commit、Jest;之后就自动开始下载所需依赖;

2、目录结构
在这里插入图片描述
3、运行项目

npm run serve

在这里插入图片描述
如果出现以上情况,说明项目依赖下载成功;

三、改造成SSR

首先安装ssr的所需相关依赖

安装 vue-server-renderer
安装 webpack-node-externals
安装 cross-env
安装 koa koa-static
安装 vuex-router-sync(用于连接store和router,这样就可以在组件中直接访问 this.$store.state.route)

npm install vue-server-renderer webpack-node-externals cross-env --save-dev
npm install koa koa-static vuex-router-sync --save

改造router.ts

import Vue from "vue";
import VueRouter, { RouteConfig } from "vue-router";

Vue.use(VueRouter);

const routes: Array<RouteConfig> = [
  {
    path: "/",
    name: "home",
    component: () => import("../views/Home.vue"),
  },
  {
    path: "/about",
    name: "about",
    component: () => import("../views/About.vue"),
  },
];

export default function createRouter() {
  return new VueRouter({
    mode: "history",
    base: process.env.BASE_URL,
    routes,
  });
}

改造store.ts

import Vue from "vue";
import Vuex from "vuex";

Vue.use(Vuex);

export default function createStore() {
  return new Vuex.Store({
    state: {},
    mutations: {},
    actions: {},
    modules: {},
  });
}

改造main.ts

import Vue from "vue";
import App from "./App.vue";
import createRouter from "./router";
import createStore from "./store";
import { sync } from "vuex-router-sync";

Vue.config.productionTip = false;

export default function createApp() {
  // 创建 router 和 store 实例
  const router = createRouter();
  const store = createStore();

  // 同步路由状态(route state)到 store
  sync(store, router);

  const app = new Vue({
    router,
    store,
    render: (h) => h(App),
  });
  return { app, router, store };
}

创建entry-client.ts

在src根目录下创建entry-client.ts文件;

import createApp from "./main";

const { app, router, store } = createApp();

if ((window as any).__INITIAL_STATE__) {
  store.replaceState((window as any).__INITIAL_STATE__);
}

router.onReady(() => {
  // 添加路由钩子函数,用于处理 asyncData.
  // 在初始路由 resolve 后执行,
  // 以便我们不会二次预取(double-fetch)已有的数据。
  // 使用 `router.beforeResolve()`,以便确保所有异步组件都 resolve。
  router.beforeResolve((to, from, next) => {
    const matched: any = router.getMatchedComponents(to);
    const prevMatched: any = router.getMatchedComponents(from);

    // 我们只关心非预渲染的组件
    // 所以我们对比它们,找出两个匹配列表的差异组件
    let diffed = false;
    const activated = matched.filter((c: any, i: number) => {
      return diffed || (diffed = prevMatched[i] !== c);
    });

    if (!activated.length) {
      return next();
    }

    // 这里如果有加载指示器 (loading indicator),就触发

    Promise.all(
      activated.map((c: any) => {
        if (c.asyncData) {
          return c.asyncData({ store, route: to });
        }
      })
    )
      .then(() => {
        // 停止加载指示器(loading indicator)

        next();
      })
      .catch(next);
  });

  app.$mount("#app");
});

window.INITIAL_STATE 保存的是服务端返回的 context.state,客户端在挂载之前,将其替换到 store.state 中。

创建entry-server.ts

在src根目录下创建entry-server.ts文件;

import createApp from "./main";

export default (context: any) => {
  return new Promise((resolve, reject) => {
    const { app, router, store } = createApp();

    router.push(context.url);

    router.onReady(() => {
      const matchedComponents = router.getMatchedComponents();
      if (!matchedComponents.length) {
        return reject({ code: 404 });
      }

      // 对所有匹配的路由组件调用 `asyncData()`
      Promise.all(
        matchedComponents.map((Component: any) => {
          if (Component.asyncData) {
            return Component.asyncData({
              store,
              route: router.currentRoute,
            });
          }
        })
      )
        .then(() => {
          // 在所有预取钩子(preFetch hook) resolve 后,
          // 我们的 store 现在已经填充入渲染应用程序所需的状态。
          // 当我们将状态附加到上下文,
          // 并且 `template` 选项用于 renderer 时,
          // 状态将自动序列化为 `window.__INITIAL_STATE__`,并注入 HTML。
          context.state = store.state;

          resolve(app);
        })
        .catch(reject);
    }, reject);
  });
};

创建index.template.html

在src根目录下创建index.template.html文件;

<!DOCTYPE html>
<html lang="en">
  <head>
    <meta charset="UTF-8" />
    <meta name="viewport" content="width=device-width, initial-scale=1.0" />
    <title>{{title}}</title>
  </head>
  <body>
    <!--vue-ssr-outlet-->
  </body>
</html>

创建server.ts

由于我的项目需求写一些业务逻辑,所以我这里面创建了server文件夹,在server下创建了index.ts;
所以在创建文件以及配置的注意文件路径

const fs = require("fs");
const Koa = require("koa");
const path = require("path");
const koaStatic = require("koa-static");
const app = new Koa();

const resolve = (file) => path.resolve(__dirname, file);
// 开放dist目录
app.use(koaStatic(resolve("../dist/client")));

// 第 2 步:获得一个createBundleRenderer
const { createBundleRenderer } = require("vue-server-renderer");
const serverBundle = require("../dist/server/vue-ssr-server-bundle.json");
const clientManifest = require("../dist/client/vue-ssr-client-manifest.json");

const renderer = createBundleRenderer(serverBundle, {
  runInNewContext: false,
  template: fs.readFileSync(
    path.resolve(__dirname, "../src/index.template.html"),
    "utf-8"
  ),
  clientManifest,
});

function renderToString(context) {
  return new Promise((resolve, reject) => {
    renderer.renderToString(context, (err, html) => {
      err ? reject(err) : resolve(html);
    });
  });
}

// 第 3 步:添加一个中间件来处理所有请求
app.use(async (ctx, next) => {
  const context = {
    title: "Hello SSR",
    url: ctx.url,
  };
  // 将 context 数据渲染为 HTML
  const html = await renderToString(context);
  ctx.body = html;
});

/*服务启动*/
const port = 3000;
app.listen(port, function() {
  console.log(`server started at localhost:${port}`);
});

构建配置

在项目根目录下创建vue.config.js

const VueSSRServerPlugin = require("vue-server-renderer/server-plugin");
const VueSSRClientPlugin = require("vue-server-renderer/client-plugin");
const nodeExternals = require("webpack-node-externals");
const env = process.env;
const isServer = env.RUN_ENV === "server";

module.exports = {
  lintOnSave: false,
  publicPath: "./",
  outputDir: `dist/${env.RUN_ENV}`,
  configureWebpack: {
    // 将 entry 指向应用程序的 server / client 文件
    entry: `./src/entry-${env.RUN_ENV}.ts`,
    devtool: "eval",
    // 这允许 webpack 以 Node 适用方式(Node-appropriate fashion)处理动态导入(dynamic import),
    // 并且还会在编译 Vue 组件时,
    // 告知 `vue-loader` 输送面向服务器代码(server-oriented code)。
    target: isServer ? "node" : "web",
    // 此处告知 server bundle 使用 Node 风格导出模块(Node-style exports)
    output: {
      libraryTarget: isServer ? "commonjs2" : undefined,
    },
    // https://webpack.js.org/configuration/externals/#function
    // https://github.com/liady/webpack-node-externals
    // 外置化应用程序依赖模块。可以使服务器构建速度更快,
    // 并生成较小的 bundle 文件。
    externals: isServer
      ? nodeExternals({
          // 不要外置化 webpack 需要处理的依赖模块。
          // 你可以在这里添加更多的文件类型。例如,未处理 *.vue 原始文件,
          // 你还应该将修改 `global`(例如 polyfill)的依赖模块列入白名单
          whitelist: /\.css$/,
        })
      : undefined,
    optimization: { splitChunks: isServer ? false : undefined },
    // 这是将服务器的整个输出
    // 构建为单个 JSON 文件的插件。
    // 服务端默认文件名为 `vue-ssr-server-bundle.json`
    // 客户端默认文件名为 `vue-ssr-client-manifest.json`
    plugins: [isServer ? new VueSSRServerPlugin() : new VueSSRClientPlugin()],
  },
};

修改package.json

在文件中的script对象中添加以下命令:

"start": "npm run build:server && npm run build:client && npm run service",
"build:client": "cross-env RUN_ENV=client vue-cli-service build",
"build:server": "cross-env RUN_ENV=server vue-cli-service build --mode server",
"service": "node server\\index.ts"

在这里插入图片描述
添加完后就可以开始跑项目了;

四、启动项目

npm run start

执行以上命令会先build server 环境后build client环境 之后开始启动项目
在这里插入图片描述

注:以上步骤为初稿,后续会持续更新完善

本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

vue-cli3搭建的vue改造成SSR项目 的相关文章

  • 用git命令行向gitee推送代码

    目录 1 生成个人公钥 2 在gitee上添加个人公钥 3 添加远程主机到本机SSH可信列表 4 提交本地文件 1 生成个人公钥 xff08 参考生成 添加SSH公钥 Gitee com xff09 打开git命令行 xff0c 输入命令

随机推荐

  • 【Anaconda环境】安装gym+pytorch

    1 创建conda新环境 conda create name gymTorch python 61 3 7 conda activate gymTorch xff08 进入新环境 xff09 python如果为3 6版本 xff0c 在导入
  • 【解决git报错 10054】OpenSSL SSL_read: Connection was reset, errno 10054

    使用git获取github上代码时报错 xff1a OpenSSL SSL read Connection was reset errno 10054 此时又必须开着vpn才能访问到github 参考网上的回答 xff0c 成功解决问题 x
  • 解决torch.from_numpy报错 (ValueError)

    变量screen的类型是numpy ndarray xff0c 使用如下代码想要将numpy数组转化为torch Tensor xff1a screen 61 torch from numpy screen 运行报错 xff1a Value
  • 【gym】env.render三种mode

    最近使用gym提供的小游戏做强化学习DQN算法的研究 xff0c 首先就是要获取游戏截图 xff0c 并且对截图做一些预处理 screen 61 env render mode 61 39 rgb array 39 上述代码是将游戏截图转换
  • python slice()函数

    slice函数会返回一个切片对象 xff0c 让数组按照该切片对象获取数据 参考自网站 xff1a Python slice 函数 菜鸟教程 slice的语法 xff1a class slice stop class slice start
  • 软件工程概论,课后作业1

    1 网站系统开发需要掌握的技术 在看了网上一些前辈的文章后 xff0c 我认为大体上有以下几点 至少熟悉一种建站程序 对空间和域名的知识有一定的了解 有一些美工基础 对编程有一些了解 代码知识基本的要懂 css 43 div会一点 简单的网
  • Debian11设置屏幕分辨率

    问题现象 xff1a 新的华硕计算机安装debian11后屏幕分辨率是1920 748并且无法修改 xff0c 换了显示屏和HDMI接口一样无法修改 使用 xrandr命令查询反馈 xrandr failed to get size of
  • matplotlib.pyplot.imshow()

    用matplotlib pyplot imshow x 函数将一个x矩阵转化为可显示的图像 这里需要注意的是x的数据类型 xff1a x为float xff0c 那么x的值要在0 1之间 xff1b x为int xff0c 那么x的值要在0
  • 使用telnet通过SMTP协议发送邮件

    1 确认本地电脑的telnet服务是否打开 2 查看邮箱授权码是否设置 在图片中有提示 xff0c 授权码是用于登录第三方邮件客户端的专用密码 xff0c 这样可以保护自己的密码不被泄露 xff0c 还能委托其他客户端进行邮件的操作 如果没
  • 在Linux环境下使用命令行编译运行C源文件

    1 安装gcc 首先如何确定是否已经安装gcc了呢 xff1f 如果有一个hello c的源文件 xff0c 那么使用命令gcc hello c 如果报出提示 xff0c command gcc not found就是代表没有安装这个程序
  • C语言的指针传递和C++的引用传递

    首先 xff0c C语言没有引用传递 C 43 43 中使用引用传递的方式是在函数的参数前加 amp 号 xff0c 如 xff1a void Delete X LinkList amp L ElemType x 声明 Delete X L
  • python 安装pandas失败的解决办法

    python 安装pandas失败的解决办法 1 首先用CMD进行安装 xff0c 安装失败 2 然后用pycharm进行安装 xff0c 同样也失败 图片省略 3 最后在pycharm中添加清华源网址 https pypi tuna ts
  • docker 容器的启动、停止和删除

    1 查看所有docker容器 查看所有在运行的容器 xff1a docker ps 查看所有容器 包括停止的 docker ps a 来看看他们的区别 xff1a 2 启动容器 这里我来启动第二个Redis容器 xff08 因为我已经有一个
  • 用real vnc连接服务器

    用real vnc连接服务器 xff08 一 xff09 在无法访问服务器内部网络 首先 xff0c 本地电脑需要可以ping通服务器的IP xff0c 像学校的服务器 xff0c 一般只能用学校的网络才能访问 xff0c 若在校外 xff
  • nginx配置ssl证书实现https访问

    配置ssl证书之前 xff0c 先准备SSL证书 xff0c 至于获取的途径很多 xff08 阿里云的服务 xff0c 第三方服务购买 xff09 这里不详细解释 以下是我的SSL证书 准备好证书后 xff0c 找到nginx的安装目录 x
  • 棋牌游戏算法——麻将系列总结

    麻将介绍 麻将的基本规则都是一样的 xff0c 我就不累赘了 我从事棋牌工作五年了 xff0c 开发过无数的麻将玩法 xff0c 如柳州麻将 xff0c 转转麻将 xff0c 红中麻将 xff0c 来宾麻将 xff0c 广东麻将 xff0c
  • Docker run 命令详解

    命令格式 xff1a docker run OPTIONS IMAGE COMMAND ARG Usage Run a command in a new container 中文意思为 xff1a 通过run命令创建一个新的容器 xff08
  • ROS笔记——创建简单的主题发布节点和主题订阅节点

    在安装好ROS后 xff0c 接着学习如何创建节点和节点之间的通信方式 xff0c 以一个简单的主题发布节点和主题订阅节点说明 节点是连接ROS网络等可执行文件 xff0c 是实现某些功能的软件包 xff0c 也是一个主要计算执行的进程 一
  • SVN常用命令总结

    svn使用总结 SVN检出操作 svn checkout path svn checkout svn 192 168 0 1 project 简写 xff1a svn co SVN查看状态 span class token string 3
  • vue-cli3搭建的vue改造成SSR项目

    vue cli3搭建的vue改造成SSR项目 一 文章简介二 搭建vue项目三 改造成SSR首先安装ssr的所需相关依赖改造router ts改造store ts改造main ts创建entry client ts创建entry serve