// 本地预览 dist/:按线上路径挂在 /app 下(/app 和 /app/ 都返回首页) import http from 'node:http'; import fs from 'node:fs'; import path from 'node:path'; import { fileURLToPath } from 'node:url'; const DIST = path.resolve(path.dirname(fileURLToPath(import.meta.url)), '..', 'dist'); const PORT = Number(process.env.PORT) || 8080; const TYPES = { '.html': 'text/html; charset=utf-8', '.css': 'text/css; charset=utf-8', '.js': 'text/javascript; charset=utf-8', '.svg': 'image/svg+xml', '.webp': 'image/webp', '.png': 'image/png', '.jpg': 'image/jpeg', '.apk': 'application/vnd.android.package-archive', }; http.createServer((req, res) => { const url = decodeURIComponent(req.url.split('?')[0]); if (url === '/') { res.writeHead(302, { Location: '/app' }); return res.end(); } if (url !== '/app' && !url.startsWith('/app/')) { res.writeHead(404, { 'Content-Type': 'text/plain; charset=utf-8' }); return res.end(`本地预览只包含 /app,${url} 属于线上商城`); } let file = path.join(DIST, url.slice(4)); if (!file.startsWith(DIST)) { res.writeHead(403); return res.end(); } if (fs.existsSync(file) && fs.statSync(file).isDirectory()) file = path.join(file, 'index.html'); if (!fs.existsSync(file)) { res.writeHead(404); return res.end('Not Found'); } res.writeHead(200, { 'Content-Type': TYPES[path.extname(file)] || 'application/octet-stream' }); fs.createReadStream(file).pipe(res); }).listen(PORT, () => console.log(`本地预览:http://localhost:${PORT}/app`));