SSR+前端框架 项目工程核心

前言

关于SSR的概念和其优缺点在之前的博客Node服务器端渲染(SSR概念篇)也做过总结,目前学习了如何在实际的代码中实现SSR的前端项目搭建,写此博客也是对 SSR+前端框架 项目工程搭建的一个总结,加上个人的理解。

注意

旨在讲个大概的 代码 结构,基本的结构和代码是用来干什么的,不能做到从 0 基础 到 完全完成项目的初始化。

没有用到 Nuxt.js 或者 Next.js 这种SSR框架,原生 node+vue 讲解

核心技术栈

Node.js + Express + Vue3

运行环境

node环境

项目介绍

核心实现原理

SSR的实现重点是在 服务端 生成页面的元素 DOM等返回给 浏览器

这里我们嵌入前端框架实现SSR,大概分下面几个步骤:

  1. 用前端框架编写app.js脚本,引入框架的组件
  2. 编写服务器端返回静态html页面的接口
  3. 在静态html中,加入前端框架 的组件
  4. 在静态html中,加入前端框架的客户端脚本,使静态页面加入前端框架,实现交互功能。

项目结构

项目目录

build是打包文件,本项目使用webpack打包工具进行的项目打包

核心模块介绍

咱们按照"核心实现原理"步骤中的几步中,来进行项目结构,功能的介绍src下的项目核心SSR配置:

1.app.js(框架引入主组件脚本)

用前端框架编写app.js,引入框架的组件

代码
javascript 复制代码
import { createSSRApp } from "vue";
import App from "./App.vue";
export default function createApp() {
  // 创建app
  const app = createSSRApp(App);
  return app;
}
// createApp(App).mount("#app");
vue 复制代码
<template>
  <div class="app" style="border: 1px solid red">
    <h1>App</h1>
    <div>count: {{ count }}</div>
    <button @click="addNumber">+1</button>
  </div>
</template>

<script setup>
  import { ref } from "vue";
  const count = ref(100);
  function addNumber() {
    count.value += 1;
  }
</script>

2.server(服务端)

功能:
  • 编写服务器端返回静态html页面的接口
  • 开启服务器
  • 配置资源路径
  • 在静态html中,加入前端框架 的组件
  • ...
代码
javascript 复制代码
import express from "express";
import createApp from "../app";
import { renderToString } from "@vue/server-renderer";

let app = express();
let App = createApp();

// 部署 静态资源
app.use(express.static("build"));

// 和注册的顺序有关系,优先匹配第一个符合的路径
app.get("/*", async (req, res) => {
  const AppContent = await renderToString(App);
  console.log(AppContent);
  res.send(`
    <!DOCTYPE html>
    <html lang="en">
      <head>
        <meta charset="UTF-8" />
        <meta http-equiv="X-UA-Compatible" content="IE=edge" />
        <meta name="viewport" content="width=device-width, initial-scale=1.0" />
        <link rel="icon" href="data:;base64,=">
        <title>Document</title>
      </head>
      <body>
        <h1> Vue + Server Side Render</h1>
        <div id="app">${AppContent}</div>
        <script src="/client/client_bundle.js"></script>
      </body>
    </html>
  `);
});
app.listen(3000, () => {
  console.log("Node Server Start On 3000");
});

3.client(客户端)

功能:
  • 在静态html中,加入前端框架的客户端脚本
  • 使静态页面加入前端框架,实现交互功能。
  • ...
代码
javascript 复制代码
import { createApp } from "vue";
import App from "../App.vue";
let app = createApp(App);
app.mount("#app"); // 需要在body添加id=app标签

配置模块

config(项目打包配置)

javascript 复制代码
let path = require("path");
let nodeExternals = require("webpack-node-externals");
let { DefinePlugin } = require("webpack");
let { VueLoaderPlugin } = require("vue-loader/dist/index");
module.exports = {
  mode: "development",
  module: {
    rules: [
      {
        test: /\.js$/,
        use: [
          {
            loader: "babel-loader",
          },
        ],
      },
      {
        test: /\.vue$/,
        loader: "vue-loader",
      },
    ],
  },
  plugins: [
    new VueLoaderPlugin(),
    // 定义两个环境变量,关闭option api 和 pro 调试的提示
    new DefinePlugin({
      __VUE_OPTIONS_API__: false,
      __VUE_PROD_DEVTOOLS__: false,
    }),
  ],
  resolve: {
    extensions: [".js", ".json", ".wasm", ".jsx", ".jsx", "vue"],
  },
};
javascript 复制代码
let path = require("path");
let baseConfig = require("./base.config");
let { merge } = require("webpack-merge");
module.exports = merge(baseConfig, {
  target: "web", // 构建目标环境
  entry: "./src/client/index.js",
  output: {
    filename: "client_bundle.js",
    path: path.resolve(__dirname, "../build/client"),
  },
});
javascript 复制代码
let path = require("path");
let nodeExternals = require("webpack-node-externals");
let baseConfig = require("./base.config");
let { merge } = require("webpack-merge");
module.exports = merge(baseConfig, {
  target: "node",
  entry: "./src/server/index.js",
  output: {
    filename: "server_bundle.js",
    path: path.resolve(__dirname, "../build/server"),
  },
  externals: [nodeExternals()],
});

package.json

json 复制代码
{
  "name": "01-node-server",
  "version": "1.0.0",
  "description": "",
  "main": "index.js",
  "scripts": {
    "test": "echo \"Error: no test specified\" && exit 1",
    "build:server": "webpack --config ./config/server.config.js --watch",
    "build:client": "webpack --config ./config/client.config.js --watch",
    "start:server": "nodemon ./build/server/server_bundle.js"
  },
  "keywords": [],
  "author": "",
  "license": "ISC",
  "dependencies": {
    "axios": "^1.2.1",
    "express": "^4.18.2",
    "nodemon": "^2.0.20",
    "pinia": "^2.0.26",
    "vue": "^3.2.45",
    "vue-loader": "^17.0.1",
    "vue-router": "^4.1.6",
    "webpack-node-externals": "^3.0.0"
  },
  "devDependencies": {
    "@babel/preset-env": "^7.20.2",
    "babel-loader": "^9.1.0",
    "webpack": "^5.75.0",
    "webpack-cli": "^5.0.0",
    "webpack-merge": "^5.8.0"
  }
}
相关推荐
cup1127 分钟前
[Full Clock 技术复盘] 二、SvelteKit 实战避坑指南:PWA、SSR 样式断裂、持久化防抖
i18n·ssr·svelte·localstorage·pwa
Python私教1 小时前
把开源 Agent 打包成"解压双击即用"的 Windows 便携包:一条命令的完整实现
node.js
没事别瞎琢磨4 小时前
十一、审计与 Run Session——每一步操作都被记录
人工智能·node.js
没事别瞎琢磨4 小时前
十六、AgentSandbox——把所有模块串起来的编排类
人工智能·node.js
没事别瞎琢磨4 小时前
十二、网络代理与白名单规则引擎
人工智能·node.js
没事别瞎琢磨4 小时前
十四、Git Worktree 隔离执行
人工智能·node.js
没事别瞎琢磨5 小时前
十、统一 Runner 入口——能力检测与模式回退
人工智能·node.js
没事别瞎琢磨5 小时前
八、环境隔离——构建安全的子进程环境
人工智能·node.js
没事别瞎琢磨6 小时前
六、输出捕获与截断
人工智能·node.js
没事别瞎琢磨6 小时前
七、敏感路径预检——Protected Paths
人工智能·node.js