Commit 77c26684 authored by chenqikuai's avatar chenqikuai

init

parents
/node_modules/
/dist/
\ No newline at end of file
{
"name": "vue3ssr",
"version": "1.0.0",
"description": "",
"main": "index.js",
"scripts": {
"start": "nodemon server.js",
"build:client": "vue-cli-service build --dest dist/client",
"build:server": "cross-env SSR=1 vue-cli-service build --dest dist/server",
"build": "npm run build:client && npm run build:server"
},
"author": "",
"license": "ISC",
"dependencies": {
"@vue/cli-service": "^4.5.15",
"@vue/server-renderer": "^3.2.23",
"cross-env": "^7.0.3",
"express": "^4.17.1",
"nodemon": "^2.0.15",
"vue": "^3.2.23",
"vue-router": "4",
"webpack-manifest-plugin": "^4.0.2",
"webpack-node-externals": "^3.0.0"
}
}
\ No newline at end of file
```bash
npm install # 安装
npm run build # 打包
npm start # 运行
```
const path = require("path");
const express = require("express");
const fs = require("fs");
const { renderToString } = require("@vue/server-renderer");
const manifest = require("./dist/server/ssr-manifest.json");
const server = express();
const appPath = path.join(__dirname, "./dist", "server", manifest["app.js"]);
const createApp = require(appPath).default;
server.use(
"/img",
express.static(path.join(__dirname, "./dist/client", "img"))
);
server.use("/js", express.static(path.join(__dirname, "./dist/client", "js")));
server.use(
"/css",
express.static(path.join(__dirname, "./dist/client", "css"))
);
server.use(
"/favicon.ico",
express.static(path.join(__dirname, "./dist/client", "favicon.ico"))
);
server.get("*", async (req, res) => {
const { app, router } = createApp();
await router.push(req.url);
await router.isReady();
const appContent = await renderToString(app);
fs.readFile(path.join(__dirname, "/dist/client/index.html"), (err, html) => {
if (err) {
throw err;
}
html = html
.toString()
.replace('<div id="app">', `<div id="app">${appContent}`);
res.setHeader("Content-Type", "text/html");
res.send(html);
});
});
console.log("You can navigate to http://localhost:8080");
server.listen(8080);
<template>
<div>
<div>Current user is: {{ user }}</div>
<div @click="add">{{ count }}</div>
<RouterView />
</div>
</template>
<script>
import { RouterView } from "vue-router";
export default {
name: "App",
components: {
RouterView,
},
data() {
return {
user: "John Doe",
count: 0,
};
},
methods: {
add() {
this.count++;
},
},
};
</script>
import { createSSRApp } from "vue";
import { createWebHistory } from "vue-router";
import createRouter from "./router.js";
import App from "./App.vue";
// 针对客户端的启动逻辑......
const app = createSSRApp(App);
const router = createRouter(createWebHistory());
app.use(router);
router.isReady().then(() => {
app.mount("#app");
});
import { createSSRApp } from "vue";
import { createMemoryHistory } from "vue-router";
import createRouter from "./router.js";
import App from "./App.vue";
export default function () {
const app = createSSRApp(App);
const router = createRouter(createMemoryHistory());
app.use(router);
return {
app,
router,
};
}
/* 你可能已经注意到我们的服务端代码使用了一个 * 处理函数来接收任意 URL。
这允许我们将被访问的 URL 传递给 Vue 应用,并在客户端和服务端之间复用相同的路由配置!
这里推荐使用官方的 vue-router。让我们先创建一个路由文件。
注意和应用实例类似,每个请求都需要有一个干净的路由实例,因此该文件应该导出一个 createRouter 函数: */
// router.js
import { createRouter } from "vue-router";
const routes = [
{ path: "/home", component: () => import("./views/Home.vue") },
{ path: "/about", component: () => import("./views/About.vue") },
];
export default function (history) {
return createRouter({
history,
routes,
});
}
<template>about</template>
\ No newline at end of file
<template>home</template>
\ No newline at end of file
const { WebpackManifestPlugin } = require("webpack-manifest-plugin");
const nodeExternals = require("webpack-node-externals");
const webpack = require("webpack");
module.exports = {
chainWebpack: (webpackConfig) => {
// 我们需要禁用 cache loader,否则客户端构建版本会从服务端构建版本使用缓存过的组件
webpackConfig.module.rule("vue").uses.delete("cache-loader");
webpackConfig.module.rule("js").uses.delete("cache-loader");
webpackConfig.module.rule("ts").uses.delete("cache-loader");
webpackConfig.module.rule("tsx").uses.delete("cache-loader");
if (!process.env.SSR) {
// 将入口指向应用的客户端入口文件
webpackConfig.entry("app").clear().add("./src/entry-client.js");
return;
}
// 将入口指向应用的服务端入口文件
webpackConfig.entry("app").clear().add("./src/entry-server.js");
// 这允许 webpack 以适合于 Node 的方式处理动态导入,
// 同时也告诉 `vue-loader` 在编译 Vue 组件的时候抛出面向服务端的代码。
webpackConfig.target("node");
// 这会告诉服务端的包使用 Node 风格的导出
webpackConfig.output.libraryTarget("commonjs2");
webpackConfig
.plugin("manifest")
.use(new WebpackManifestPlugin({ fileName: "ssr-manifest.json" }));
// https://webpack.js.org/configuration/externals/#function
// https://github.com/liady/webpack-node-externals
// 将应用依赖变为外部扩展。
// 这使得服务端构建更加快速并生成更小的包文件。
// 不要将需要被 webpack 处理的依赖变为外部扩展
// 也应该把修改 `global` 的依赖 (例如各种 polyfill) 整理成一个白名单
webpackConfig.externals(nodeExternals({ allowlist: /\.(css|vue)$/ }));
webpackConfig.optimization.splitChunks(false).minimize(false);
webpackConfig.plugins.delete("preload");
webpackConfig.plugins.delete("prefetch");
webpackConfig.plugins.delete("progress");
webpackConfig.plugins.delete("friendly-errors");
webpackConfig.plugin("limit").use(
new webpack.optimize.LimitChunkCountPlugin({
maxChunks: 1,
})
);
},
};
This source diff could not be displayed because it is too large. You can view the blob instead.
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment