我想用 Vue 和 Nestjs 构建 monorepo。主要页面将使用 Nestjs + Twig 和路径
/<page>
进行处理。管理面板位于 /admin/<page>
(Vue 应用程序)。以及使用 Nestjs /api/<route>
创建的 api 路由。 Vue admin 在其 dist
目录中构建,然后我们通过 https://github.com/fastify/fastify-static 中的 useStaticAssets 方法提供它,这是 Nestjs 应用程序的 main.ts 文件:
import { NestFactory } from '@nestjs/core';
import {
FastifyAdapter,
NestFastifyApplication,
} from '@nestjs/platform-fastify';
import { AppModule } from './app.module';
import helmet from '@fastify/helmet';
import { join } from 'path';
async function bootstrap() {
const PORT = process.env.PORT || 3000;
const app = await NestFactory.create<NestFastifyApplication>(
AppModule,
new FastifyAdapter({ logger: true }),
);
await app.register(helmet);
/** client assets and backend (uploaded files etc.) */
app.useStaticAssets({
root: join(__dirname, '..', 'public'),
prefix: '/public/',
});
/** vue admin */
app.useStaticAssets({
root: join(__dirname, '..', 'vue-admin', 'dist'),
prefix: '/admin',
prefixAvoidTrailingSlash: true,
decorateReply: false,
});
app.setViewEngine({
engine: {
twig: require('twig'),
},
templates: join(__dirname, '..', 'views'),
});
await app.listen(PORT);
}
bootstrap();
预期行为:无需重新加载即可浏览 Vue 应用程序页面,并且在浏览器中刷新页面时,获得相同的页面。它可以与 Symfony 配合使用,解决方案是here。
实际行为:相同,但是当在现有 Vue 路由(例如 /admin/about)刷新页面时,它会响应 404。看起来它试图查找静态文件,但我们希望它由 Vue 路由器处理。
我尝试执行类似的操作来解决问题,但它没有按预期工作:
import { All, Controller, Res } from '@nestjs/common';
import { FastifyReply } from 'fastify';
import { join } from 'path';
import { readFileSync } from 'fs';
@Controller()
export class AppController {
@All('admin/*')
vueAdmin(@Res() res: FastifyReply) {
res
.type('text/html')
.send(
readFileSync(
join(__dirname, '..', 'vue-admin/dist/index.html'),
'utf8',
),
);
}
}
我在这个问题中想要实现的行为已经实现,并且按预期工作。 main.ts 的最终代码:
import { NestFactory } from '@nestjs/core';
import {
FastifyAdapter,
NestFastifyApplication,
} from '@nestjs/platform-fastify';
import { AppModule } from './app.module';
import fastifyHelmet from '@fastify/helmet';
import fastyfyMultipart from '@fastify/multipart';
import { join } from 'path';
import { queryStringParse } from './utils';
import { readFileSync } from 'node:fs';
import * as fastifyHttpsRedirect from 'fastify-https-redirect';
async function bootstrap() {
const HOST = process.env.HOST || '127.0.0.1';
const PORT = process.env.USE_HTTPS ? 443 : process.env.PORT || 3000;
const fastifyAdapterOptions: any = {
logger: true,
http2: true,
querystringParser: (str) => queryStringParse(str),
};
if (process.env.USE_HTTPS) {
const httpsOptions = {
key: readFileSync(join(__dirname, '..', 'certs', 'ssl.key')),
cert: readFileSync(join(__dirname, '..', 'certs', 'ssl.crt')),
allowHTTP1: true,
requestCert: false, // process.env.NODE_ENV === 'production',
rejectUnauthorized: false, // process.env.NODE_ENV === 'production',
};
fastifyAdapterOptions.https = httpsOptions;
}
const app = await NestFactory.create<NestFastifyApplication>(
AppModule,
new FastifyAdapter(fastifyAdapterOptions),
);
app.register(fastifyHttpsRedirect);
app.register(fastyfyMultipart);
if (process.env.NODE_ENV === 'development') {
app.enableCors({
credentials: true,
origin: true,
});
}
await app.register(fastifyHelmet, {
crossOriginResourcePolicy: false,
contentSecurityPolicy: {
directives: {
'img-src': ["'self'", 'https: data: blob:'],
},
},
});
/** client assets and backend (uploaded files etc.) */
app.useStaticAssets({
root: join(__dirname, '..', 'public'),
prefix: '/public/',
});
/** vue admin */
app.useStaticAssets({
root: join(__dirname, '..', 'vue-admin', 'dist'),
prefix: '/admin',
prefixAvoidTrailingSlash: true,
decorateReply: false,
});
app.setViewEngine({
engine: {
nunjucks: require('nunjucks'),
},
options: {
onConfigure: (env) => {
// eslint-disable-next-line @typescript-eslint/no-var-requires
env.addFilter('date', require('nunjucks-date-filter'));
},
autoescape: true,
web: {
useCache: process.env.NODE_ENV === 'production',
async: process.env.NODE_ENV === 'production',
},
debug: process.env.NODE_ENV === 'development',
watch: process.env.NODE_ENV === 'development',
},
templates: join(__dirname, '..', 'views'),
});
await app.listen(PORT, HOST);
}
bootstrap();
但是这种技术解决方案也有一些缺点。 Fastify 团队在此解释:https://fastify.dev/docs/latest/Guides/Recommendations/#use-a-reverse-proxy