部署优化 #116
4
frontend/.gitignore
vendored
4
frontend/.gitignore
vendored
@@ -12,9 +12,11 @@ dist
|
|||||||
dist-ssr
|
dist-ssr
|
||||||
*.local
|
*.local
|
||||||
|
|
||||||
# VAD 静态资源(从 node_modules 自动复制)
|
# VAD / ONNX Runtime 静态资源(从 node_modules 自动复制)
|
||||||
public/silero_vad_*.onnx
|
public/silero_vad_*.onnx
|
||||||
public/vad.worklet.bundle.min.js
|
public/vad.worklet.bundle.min.js
|
||||||
|
public/ort-wasm*.mjs
|
||||||
|
public/ort-wasm*.wasm
|
||||||
|
|
||||||
# Editor directories and files
|
# Editor directories and files
|
||||||
.vscode/*
|
.vscode/*
|
||||||
|
|||||||
8
frontend/.vite/deps/_metadata.json
Normal file
8
frontend/.vite/deps/_metadata.json
Normal file
@@ -0,0 +1,8 @@
|
|||||||
|
{
|
||||||
|
"hash": "949ba93d",
|
||||||
|
"configHash": "caf561c6",
|
||||||
|
"lockfileHash": "76f9e0b5",
|
||||||
|
"browserHash": "26e6c47e",
|
||||||
|
"optimized": {},
|
||||||
|
"chunks": {}
|
||||||
|
}
|
||||||
3
frontend/.vite/deps/package.json
Normal file
3
frontend/.vite/deps/package.json
Normal file
@@ -0,0 +1,3 @@
|
|||||||
|
{
|
||||||
|
"type": "module"
|
||||||
|
}
|
||||||
@@ -5,6 +5,10 @@ server {
|
|||||||
root /usr/share/nginx/html;
|
root /usr/share/nginx/html;
|
||||||
index index.html;
|
index index.html;
|
||||||
|
|
||||||
|
# 跨域隔离头(SharedArrayBuffer / WASM 线程模式所需)
|
||||||
|
add_header Cross-Origin-Opener-Policy "same-origin";
|
||||||
|
add_header Cross-Origin-Embedder-Policy "require-corp";
|
||||||
|
|
||||||
# 前端静态资源
|
# 前端静态资源
|
||||||
location / {
|
location / {
|
||||||
try_files $uri $uri/ /index.html;
|
try_files $uri $uri/ /index.html;
|
||||||
@@ -33,7 +37,35 @@ server {
|
|||||||
proxy_send_timeout 86400s;
|
proxy_send_timeout 86400s;
|
||||||
}
|
}
|
||||||
|
|
||||||
# 静态资源缓存
|
# ONNX Runtime WASM 模块(.mjs 必须是 JS MIME 类型)
|
||||||
|
location ~* \.mjs$ {
|
||||||
|
types { }
|
||||||
|
default_type application/javascript;
|
||||||
|
expires 7d;
|
||||||
|
add_header Cache-Control "public, immutable";
|
||||||
|
add_header Cross-Origin-Opener-Policy "same-origin";
|
||||||
|
add_header Cross-Origin-Embedder-Policy "require-corp";
|
||||||
|
}
|
||||||
|
|
||||||
|
# WebAssembly 二进制文件
|
||||||
|
location ~* \.wasm$ {
|
||||||
|
types { }
|
||||||
|
default_type application/wasm;
|
||||||
|
expires 7d;
|
||||||
|
add_header Cache-Control "public, immutable";
|
||||||
|
add_header Cross-Origin-Opener-Policy "same-origin";
|
||||||
|
add_header Cross-Origin-Embedder-Policy "require-corp";
|
||||||
|
}
|
||||||
|
|
||||||
|
# ONNX 模型文件
|
||||||
|
location ~* \.onnx$ {
|
||||||
|
types { }
|
||||||
|
default_type application/octet-stream;
|
||||||
|
expires 7d;
|
||||||
|
add_header Cache-Control "public, immutable";
|
||||||
|
}
|
||||||
|
|
||||||
|
# 其他静态资源缓存
|
||||||
location ~* \.(js|css|png|jpg|jpeg|gif|ico|svg|woff|woff2|ttf|eot)$ {
|
location ~* \.(js|css|png|jpg|jpeg|gif|ico|svg|woff|woff2|ttf|eot)$ {
|
||||||
expires 7d;
|
expires 7d;
|
||||||
add_header Cache-Control "public, immutable";
|
add_header Cache-Control "public, immutable";
|
||||||
@@ -41,6 +73,6 @@ server {
|
|||||||
|
|
||||||
# Gzip 压缩
|
# Gzip 压缩
|
||||||
gzip on;
|
gzip on;
|
||||||
gzip_types text/plain text/css application/json application/javascript text/xml application/xml application/xml+rss text/javascript;
|
gzip_types text/plain text/css application/json application/javascript text/xml application/xml application/xml+rss text/javascript application/wasm;
|
||||||
gzip_min_length 256;
|
gzip_min_length 256;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -50,8 +50,9 @@ export function useVAD(options?: VADOptions) {
|
|||||||
getStream: () => Promise.resolve(stream),
|
getStream: () => Promise.resolve(stream),
|
||||||
startOnLoad: true,
|
startOnLoad: true,
|
||||||
model: "legacy",
|
model: "legacy",
|
||||||
// 指向 node_modules 中的 WASM 文件(由 Vite 中间件提供)
|
// WASM 和模型文件均由 Vite 插件复制到 public/ (即 dist 根目录)
|
||||||
onnxWASMBasePath: "/node_modules/onnxruntime-web/dist/",
|
onnxWASMBasePath: "/",
|
||||||
|
modelURL: "/silero_vad_legacy.onnx",
|
||||||
|
|
||||||
onSpeechStart: () => {
|
onSpeechStart: () => {
|
||||||
setIsSpeaking(true);
|
setIsSpeaking(true);
|
||||||
|
|||||||
@@ -7,13 +7,34 @@ const ortDistDir = path.resolve(__dirname, 'node_modules/onnxruntime-web/dist')
|
|||||||
const vadDistDir = path.resolve(__dirname, 'node_modules/@ricky0123/vad-web/dist')
|
const vadDistDir = path.resolve(__dirname, 'node_modules/@ricky0123/vad-web/dist')
|
||||||
const publicDir = path.resolve(__dirname, 'public')
|
const publicDir = path.resolve(__dirname, 'public')
|
||||||
|
|
||||||
// 需要复制到 public 的静态资源
|
// 需要复制到 public 的 VAD 模型/工作线程
|
||||||
const staticFiles = [
|
const vadFiles = [
|
||||||
{ src: vadDistDir, name: 'silero_vad_legacy.onnx' },
|
{ src: vadDistDir, name: 'silero_vad_legacy.onnx' },
|
||||||
{ src: vadDistDir, name: 'silero_vad_v5.onnx' },
|
{ src: vadDistDir, name: 'silero_vad_v5.onnx' },
|
||||||
{ src: vadDistDir, name: 'vad.worklet.bundle.min.js' },
|
{ src: vadDistDir, name: 'vad.worklet.bundle.min.js' },
|
||||||
]
|
]
|
||||||
|
|
||||||
|
// 需要复制到 public 的 ONNX Runtime WASM 文件(生产环境需要)
|
||||||
|
const ortFiles = [
|
||||||
|
{ src: ortDistDir, name: 'ort-wasm-simd-threaded.mjs' },
|
||||||
|
{ src: ortDistDir, name: 'ort-wasm-simd-threaded.wasm' },
|
||||||
|
{ src: ortDistDir, name: 'ort-wasm-simd.mjs' },
|
||||||
|
{ src: ortDistDir, name: 'ort-wasm-simd.wasm' },
|
||||||
|
{ src: ortDistDir, name: 'ort-wasm.mjs' },
|
||||||
|
{ src: ortDistDir, name: 'ort-wasm.wasm' },
|
||||||
|
]
|
||||||
|
|
||||||
|
/** 把所需文件复制到 public 目录(不存在则复制) */
|
||||||
|
function copyAssetsToPublic() {
|
||||||
|
for (const { src, name } of [...vadFiles, ...ortFiles]) {
|
||||||
|
const srcFile = path.join(src, name)
|
||||||
|
const destFile = path.join(publicDir, name)
|
||||||
|
if (fs.existsSync(srcFile) && !fs.existsSync(destFile)) {
|
||||||
|
fs.copyFileSync(srcFile, destFile)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
export default defineConfig({
|
export default defineConfig({
|
||||||
server: {
|
server: {
|
||||||
proxy: {
|
proxy: {
|
||||||
@@ -30,21 +51,15 @@ export default defineConfig({
|
|||||||
react(),
|
react(),
|
||||||
{
|
{
|
||||||
name: 'serve-vad-assets',
|
name: 'serve-vad-assets',
|
||||||
// 1. 启动时将 VAD 模型/工作线程复制到 public
|
// 1. 启动时将 VAD 模型 + ORT WASM 复制到 public
|
||||||
configResolved() {
|
configResolved() {
|
||||||
for (const { src, name } of staticFiles) {
|
copyAssetsToPublic()
|
||||||
const dest = path.join(publicDir, name)
|
|
||||||
if (!fs.existsSync(dest)) {
|
|
||||||
fs.copyFileSync(path.join(src, name), dest)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
},
|
},
|
||||||
// 2. 中间件拦截所有 WASM 相关请求,从原始路径提供文件
|
// 2. 开发模式中间件:拦截 WASM 相关请求,确保 MIME 类型正确
|
||||||
configureServer(server) {
|
configureServer(server) {
|
||||||
server.middlewares.use((req, res, next) => {
|
server.middlewares.use((req, res, next) => {
|
||||||
const url = req.url?.split('?')[0] ?? ''
|
const url = req.url?.split('?')[0] ?? ''
|
||||||
// 匹配任意路径下的 ort-wasm 文件(包括 .vite/deps/ 和根路径)
|
const wasmMatch = url.match(/\/(ort-wasm[\w-]*\.(mjs|wasm))$/)
|
||||||
const wasmMatch = url.match(/\/(ort-wasm-simd-threaded\.(mjs|wasm))$/)
|
|
||||||
if (wasmMatch) {
|
if (wasmMatch) {
|
||||||
const fileName = wasmMatch[1]
|
const fileName = wasmMatch[1]
|
||||||
const filePath = path.join(ortDistDir, fileName)
|
const filePath = path.join(ortDistDir, fileName)
|
||||||
@@ -58,6 +73,17 @@ export default defineConfig({
|
|||||||
next()
|
next()
|
||||||
})
|
})
|
||||||
},
|
},
|
||||||
|
// 3. 构建阶段确保所有资源都在 dist 中
|
||||||
|
writeBundle() {
|
||||||
|
const distDir = path.resolve(__dirname, 'dist')
|
||||||
|
for (const { src, name } of [...vadFiles, ...ortFiles]) {
|
||||||
|
const srcFile = path.join(src, name)
|
||||||
|
const destFile = path.join(distDir, name)
|
||||||
|
if (fs.existsSync(srcFile) && !fs.existsSync(destFile)) {
|
||||||
|
fs.copyFileSync(srcFile, destFile)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
},
|
},
|
||||||
],
|
],
|
||||||
})
|
})
|
||||||
Reference in New Issue
Block a user