SSR初体验

基础搭建

安装依赖

先开启一个服务器

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


创建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/
但此时是静态页面 无法交互 需要激活

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


npm run build:server 打包
npm run build:client打包
npm run start 开启
访问http://localhost:3000/ 可以交互

结合vue-router

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

结合pinia


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全家桶相关推荐

  1. 推荐一个 Vue3 全家桶 + TS+ Vite2 + element-plus 的网站实战项目

    五一期间,花了 3 天时间,边学 Vue3 和 Vite2,边重构自己的项目,终于都用 Vue3 + TypeScript + Vite2 + Vuex4 + Vue-Router4 + elemen ...

  2. Vue3 全家桶 + Element Plus + Vite + TypeScript + Eslint 项目配置最佳实践

    尤大的 Vue3.0 已经发布有一阵子了,已经很成熟了. 而且 Element Plus + Vite 也出了一段时间了,是时候该上手体验分享一波了. 主要是要熟练一下 Vue3,好准备用 Vue3 ...

  3. 助你上手Vue3全家桶之Vue3教程

    目录 前言 1,setup 1.1,返回值 1.2,注意点 1.3,语法 1.4,setup的参数 2,ref 创建响应式数据 3,reactive 创建响应式数据 4,computed 计算属性 5 ...

  4. 猿创征文 | 开箱即用 yyg-cli:快速创建 vue3 组件库和vue3 全家桶项目

    1 yyg-cli 是什么 yyg-cli 是优雅哥开发的快速创建 vue3 项目的脚手架.在 npm 上发布了两个月,11月1日进行了大升级,发布 1.1.0 版本:支持创建 vue3 全家桶项目和 ...

  5. 玩转Vue3全家桶开篇词丨如何借助Vue3建构你的前端知识体系?

    你好,我是盛鑫晶,网名大圣. 先简单介绍一下我自己,我曾经就职于百度和 360,最近几年从前端架构师转型做 IT 教育,现在是一名自由职业者.我也喜欢混迹开源社区,同时也是 Vue 3 的 Contr ...

  6. Vue3 全家桶实践

    Vue 3 全家桶实践 使用 vite 创建一个 Vue 3 项目,并实践 vue-router4.vuex4 结合 Composition API 的使用. 1. 使用 vite 创建 Vue3 项 ...

  7. ssr Android简书,react ssr 初体验

    用到的技术栈 react 16 + webpack3 + koa2 看看它是如何实现服务端渲染的,here we go! 为什么要用服务端渲染 优点 无非就是两点 SEO 友好 加快首屏渲染,减少白屏 ...

  8. 股市精忠社杨忠国在线体验:华为“全家桶”补全

    股市精忠社杨忠国在线体验昨日消息,股市精忠社杨忠国在线体验注意到华为在北京召开华为全场景智慧生活新品发布会,正式发布了华为MateView.华为MateView GT.华为MateBook 16.华为 ...

  9. 【前端工程化】深入浅出vite(二)--vue3全家桶+ts构建后管系统

    安装基础包 npm create vite@latest # 这里选择的是Vue+Typescript的组合 cd vue-admin npm install# 先安装基础包 npm install ...

最新文章

  1. 获取后台数据-Http
  2. 对硬盘进行分区和格式化
  3. selectByExample和selectByExampleWithBLOBs的区别
  4. IO静态映射和动态映射
  5. ZooKeeper官方文档学习笔记03-程序员指南03
  6. CCNA2.0笔记_TCP/IP概述
  7. Xcode 12 引用缺失包:libstdc++.tbd libstdc++.6.tbd libstdc++.6.0.9.tbd 等
  8. Windows Phone 数据库并行访问【转】
  9. 307.区域和检索-数组可修改
  10. eclipse J2ME调试时模拟器一闪就消失原因
  11. 全栈必备 存储基础
  12. leetcode-SQL-1867. 最大数量高于平均水平的订单
  13. 使用Fluxion搭建钓鱼热点破解WiFi密码
  14. 40家全球知名MCU生产厂商及其详细介绍
  15. 电子科技大学计算机考研题,电子科技大学820计算机考研真题题库
  16. 连续和离散的傅里叶变换
  17. Unity更改模型贴图像素点颜色报错
  18. 巧用 Docker Buildx 构建多种系统架构镜像
  19. N行M列每个位置放Aij个1厘米的正方体,求表面积
  20. codeforces765F Souvenirs

热门文章

  1. java 回车_Java中的回车换行符/n /r /t
  2. android 语音识别 之 讯飞语音移植
  3. 半导体始祖Fairchild,第一个万亿美元初创企业的传奇
  4. linux vi 不保存退出
  5. Android Compose——一个简单的新闻APP
  6. 信创环境下三个开源数据库:SQLite、MariaDB(MySQL)和postgreSQL(瀚高数据库免费版)基本操作
  7. 使用ntpd配置集群时间同步
  8. 服务器微信推送,开启服务器消息推送后收不到微信推送的数据
  9. 推荐几款好用的拍照翻译软件给你
  10. 推荐几个高质量的技术公众号