SSR + front-end framework project engineering core

Preface

The concept of SSR and its advantages and disadvantages have also been summarized in the previous blog Node Server-side Rendering (SSR Concept) . Currently, I have learned how to implement SSR front-end project construction in actual code. Writing this blog is also a review of the SSR + front-end framework. A summary of the project construction, plus personal understanding.

Notice

The purpose is to give a rough code structure, the basic structure and what the code is used for, and it cannot be initialized from 0 to completely completed project.

No SSR framework like Nuxt.js or Next.js is used , native node+vue explanation

Core technology stack

Node.js + Express + Vue3

Operating environment

node environment

Project Introduction

Core Implementation Principles

The implementation focus of SSR is to generate the element DOM of the page on the server side and return it to the browser .
Here we embed the front-end framework to implement SSR, which is roughly divided into the following steps:

  1. Use the front-end framework to write app.js scripts and introduce the components of the framework
  2. Write an interface for returning static html pages on the server side
  3. In static html, add components of the front-end framework
  4. In static HTML, add the client script of the front-end framework, so that the static page can be added to the front-end framework to achieve interactive functions.

project structure

Project directory

image.png

Build is a packaging file. This project uses the webpack packaging tool to package the project.

Core module introduction

We follow the steps in the "Core Implementation Principles" step to carry out the project structure and introduce the function of the core SSR configuration of the project under src:

1.app.js (the framework introduces the main component script)

Write app.js with a front-end framework and introduce the components of the framework

code
import {
    
     createSSRApp } from "vue";
import App from "./App.vue";
export default function createApp() {
    
    
  // 创建app
  const app = createSSRApp(App);
  return app;
}
// createApp(App).mount("#app");

<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 (server)

Function:
  • Write an interface for returning static html pages on the server side
  • start server
  • Configure the resource path
  • In static html, add components of the front-end framework
code
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 (client)

Function:
  • In static html, add the client-side script of the front-end framework
  • Add static pages to the front-end framework to achieve interactive functions.
code
import {
    
     createApp } from "vue";
import App from "../App.vue";
let app = createApp(App);
app.mount("#app"); // 需要在body添加id=app标签

Configuration module

config (project packaging configuration)

image.png

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"],
  },
};

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"),
  },
});

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

{
    
    
  "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"
  }
}

Guess you like

Origin blog.csdn.net/Azbtt/article/details/132395544