> 文章列表 > SSR初体验-结合Vue3全家桶

SSR初体验-结合Vue3全家桶

SSR初体验-结合Vue3全家桶

SSR初体验

基础搭建

安装依赖
SSR初体验-结合Vue3全家桶
先开启一个服务器

let express = require("express");let server = express();server.get("/", (req, res) => {res.send(`Hello Node Server`);
});server.listen(3000, () => {console.log("start node server on 3000");
});

配置wp.config.js

let path = require("path");
let nodeExternals = require("webpack-node-externals");module.exports = {target: "node", //fs path就不会打包了mode: "development",entry: "./src/server/index.js",output: {filename: "server_bundle.js",path: path.resolve(__dirname, "../build/server"),},externals: [nodeExternals()], //排除掉node_module中的包
};

引入vue

SSR初体验-结合Vue3全家桶
创建App.vue

<template><div class="app" style="border: 1px solid red"><h2>Vue3 app</h2><div>{{ count }}</div><button @click="addCounter">+1</button></div>
</template><script setup>import { ref } from "vue";const count = ref(100);function addCounter() {count.value++;}
</script>

app.js

import { createSSRApp } from "vue";import App from "./App.vue";//这里为什么写一个函数来返回app实例
//通过函数返回app实例 可以保证每个请求都会返回一个新的app实例 避免跨请求状态的污染
export default function createApp() {let app = createSSRApp(App);return app;
}

index.js引入vue

let express = require("express");
let server = express();
import createApp from "../app";
import { renderToString } from "@vue/server-renderer";server.get("/", async (req, res) => {let app = createApp();let appStringHtml = await renderToString(app);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" /><title>Document</title></head><body><h1>Vue Serve Side Render</h1><div id="app">${appStringHtml}</div></body></html>`);
});server.listen(3000, () => {console.log("start node server on 3000");
});

修改server.config.js

let path = require("path");
let nodeExternals = require("webpack-node-externals");
let { VueLoaderPlugin } = require("vue-loader/dist/index");module.exports = {target: "node", //fs path就不会打包了mode: "development",entry: "./src/server/index.js",output: {filename: "server_bundle.js",path: path.resolve(__dirname, "../build/server"),},module: {rules: [{test: /.\\js$/,loader: "babel-loader",options: {presets: ["@babel/preset-env"],},},{test: /\\.vue$/,loader: "vue-loader",},],},plugins: [new VueLoaderPlugin()], //对vue文件的打包resolve: {//添加了这些扩展明名之后 项目中导包如下的扩展名文件就不需要编写文件的后缀extensions: [".js", ".json", ".wasm", ".jsx", ".vue"],},externals: [nodeExternals()], //排除掉node_module中的包
};

npm run build:server 打包
npm run start 开启
访问http://localhost:3000/
SSR初体验-结合Vue3全家桶但此时是静态页面 无法交互 需要激活

hydration

import { createApp } from "vue";
import App from "../App.vue";let app = createApp(App);app.mount("#app");

client.config.js

let path = require("path");
let { VueLoaderPlugin } = require("vue-loader");
let { DefinePlugin } = require("webpack");module.exports = {target: "web",mode: "development",entry: "./src/client/index.js",output: {filename: "client_bundle.js",path: path.resolve(__dirname, "../build/client"),},module: {rules: [{test: /.\\js$/,loader: "babel-loader",options: {presets: ["@babel/preset-env"],},},{test: /\\.vue$/,loader: "vue-loader",},],},plugins: [new VueLoaderPlugin(),new DefinePlugin({__VUE_OPTIONS_API__: false,__VUE_PROD_DEVTOOLS__: false,}),], //对vue文件的打包resolve: {//添加了这些扩展明名之后 项目中导包如下的扩展名文件就不需要编写文件的后缀extensions: [".js", ".json", ".wasm", ".jsx", ".vue"],},
};

index.js

let express = require("express");
let server = express();
import createApp from "../app";
import { renderToString } from "@vue/server-renderer";//部署静态资源
server.use(express.static("build"));server.get("/", async (req, res) => {let app = createApp();let appStringHtml = await renderToString(app);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" /><title>Document</title></head><body><h1>Vue Serve Side Render</h1><div id="app">${appStringHtml}</div><script src="/client/client_bundle.js"></script></body></html>`);
});server.listen(3000, () => {console.log("start node server on 3000");
});

SSR初体验-结合Vue3全家桶
npm run build:server 打包
npm run build:client打包
npm run start 开启
访问http://localhost:3000/ 可以交互

SSR初体验-结合Vue3全家桶

结合vue-router

SSR初体验-结合Vue3全家桶

router/index.js

import { createRouter } from "vue-router";const routes = [{path: "/",component: () => import("../views/home.vue"),},{path: "/about",component: () => import("../views/about.vue"),},
];export default function (history) {return createRouter({history,routes,});
}

App.vue

<template><div class="app" style="border: 1px solid red"><h2>Vue3 app</h2><div>{{ count }}</div><button @click="addCounter">+1</button><div><router-link to="/"><button>home</button></router-link><router-link to="/about"><button>about</button></router-link></div><!-- 路由的占位 --><router-view></router-view></div>
</template><script setup>import { ref } from "vue";const count = ref(100);function addCounter() {count.value++;}
</script>

views/home.vue

<template><div class="app" style="border: 1px solid green; margin: 10px"><h2>home</h2><div>{{ count }}</div><button @click="addCounter">+1</button></div>
</template><script setup>import { ref } from "vue";const count = ref(200);function addCounter() {count.value++;}
</script>

client/index.js

import { createApp } from "vue";
import App from "../App.vue";
import createRouter from "../router";
import { createWebHistory } from "vue-router";let app = createApp(App);//安装路由插件
let router = createRouter(createWebHistory());
app.use(router);
router.isReady().then(() => {app.mount("#app");
});

server.js

let express = require("express");
let server = express();
import createApp from "../app";
import { renderToString } from "@vue/server-renderer";
import createRouter from "../router";
// 内存路由 -> node用
import { createMemoryHistory } from "vue-router";//部署静态资源
server.use(express.static("build"));server.get("/*", async (req, res) => {let app = createApp();//app 安装路由插件let router = createRouter(createMemoryHistory());app.use(router);await router.push(req.url || "/"); // / or /about 等待页面跳转好await router.isReady(); // 等待(异步)路由加载完成 再渲染页面let appStringHtml = await renderToString(app);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" /><title>Document</title></head><body><h1>Vue Serve Side Render</h1><div id="app">${appStringHtml}</div><script src="/client/client_bundle.js"></script></body></html>`);
});server.listen(3000, () => {console.log("start node server on 3000");
});

SSR初体验-结合Vue3全家桶

结合pinia

SSR初体验-结合Vue3全家桶
store/home.js

import { defineStore } from "pinia";export const useHomeStore = defineStore("home", {state() {return {count: 1000,};},actions: {increment() {this.count++;},decrement() {this.count--;},// async fetchHomeData() {// 	let res = await axios.get();// 	this.homeInfo = res.data;// },},
});

client/index.js

import { createApp } from "vue";
import App from "../App.vue";
import createRouter from "../router";
import { createWebHistory } from "vue-router";
import { createPinia } from "pinia";let app = createApp(App);//安装路由插件
let router = createRouter(createWebHistory());
let pinia = createPinia();
app.use(router);
app.use(pinia);
router.isReady().then(() => {app.mount("#app");
});

server/index.js

let express = require("express");
let server = express();
import createApp from "../app";
import { renderToString } from "@vue/server-renderer";
import createRouter from "../router";
// 内存路由 -> node用
import { createMemoryHistory } from "vue-router";
import { createPinia } from "pinia";//部署静态资源
server.use(express.static("build"));server.get("/*", async (req, res) => {let app = createApp();//app 安装路由插件let router = createRouter(createMemoryHistory());app.use(router);await router.push(req.url || "/"); // / or /about 等待页面跳转好await router.isReady(); // 等待(异步)路由加载完成 再渲染页面//app 安装pinia插件let pinia = createPinia();app.use(pinia);let appStringHtml = await renderToString(app);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" /><title>Document</title></head><body><h1>Vue Serve Side Render</h1><div id="app">${appStringHtml}</div><script src="/client/client_bundle.js"></script></body></html>`);
});server.listen(3000, () => {console.log("start node server on 3000");
});

views/home.vue

<template><div class="app" style="border: 1px solid green; margin: 10px"><h2>home</h2><div>{{ count }}</div><button @click="addCounter">+1</button></div>
</template><script setup>import { storeToRefs } from "pinia";import { useHomeStore } from "../store/home";let homeStore = useHomeStore();let { count } = storeToRefs(homeStore);function addCounter() {count.value++;}
</script>

views/about.vue

<template><div class="app" style="border: 1px solid blue; margin: 10px"><h2>about</h2><div>{{ count }}</div><button @click="addCounter">+1</button></div>
</template><script setup>import { storeToRefs } from "pinia";import { useHomeStore } from "../store/home";let homeStore = useHomeStore();let { count } = storeToRefs(homeStore);function addCounter() {count.value++;}
</script>

SSR初体验-结合Vue3全家桶