Nuxt3开发及部署服务器全流程
引子
上次用Nuxt还是V1.x版本,几年过去已经是Nuxt3了,在给小程序技术选型的时候,也想过用Next,但最近几年没开发过几次React相关项目,就放弃了,下次一定。
本次开发管理后台基本上都是靠chatgpt辅助开发的,我设置好modelSchema,让chatgpt按照Schema生成增删改查的相关前后端页面,只能说它帮我做到了7 8成。
因为该项目没什么复杂点,这里就简单记录一下遇到几个问题。
建立Mongodb连接
安装 Mongoose
使用 npm 或 yarn 安装 Mongoose:
npm install mongoose
创建数据库连接模块
在项目的 server/db
目录中创建一个文件,比如 mongoose.js
,用于管理 Mongoose 的连接。
示例代码:server/db/mongoose.js
import mongoose from 'mongoose';
let isConnected = false;
export async function connectToDatabase() {
if (isConnected) {
console.log('✅ MongoDB is already connected');
return;
}
const config = useRuntimeConfig(); // 获取 Nuxt 的运行时配置
const uri = config.mongodbUri;
try {
await mongoose.connect(uri, {
useNewUrlParser: true,
useUnifiedTopology: true,
});
isConnected = true;
console.log('✅ Mongoose connected successfully');
} catch (error) {
console.error('❌ Mongoose connection error:', error);
throw error;
}
}
在 Nitro hooks
中初始化连接
在 server
目录下创建一个文件,比如 dbConnection.ts
,使用 Nitro hooks
确保在服务器启动时建立 Mongoose 连接。
示例代码:server/dbConnection.ts
import { connectToDatabase } from './db/mongoose';
export default defineNitroPlugin(async () => {
await connectToDatabase(); // 初始化 Mongoose 连接
});
定义 Mongoose 模型
在 server/models
目录中创建 Mongoose 模型。例如,一个用户模型可以这样定义:
示例代码:server/models/User.js
import mongoose from 'mongoose';
const UserSchema = new mongoose.Schema({
name: { type: String, required: true },
email: { type: String, required: true, unique: true },
createdAt: { type: Date, default: Date.now },
});
export default mongoose.models.User || mongoose.model('User', UserSchema);
在 API 路由中操作数据库
通过 API 文件调用 Mongoose 模型来操作数据库。例如,创建一个用于获取用户数据的 API:
示例代码:server/api/users.ts
import User from '../models/User';
export default defineEventHandler(async (event) => {
try {
const users = await User.find(); // 查询所有用户
return users;
} catch (error) {
console.error('❌ Failed to fetch users:', error);
return { error: 'Failed to fetch users' };
}
});
配置环境变量
将 MongoDB 的 URI 配置到 .env
文件中:
MONGODB_URI=mongodb://localhost:27017/your_database_name
在 nuxt.config.ts
中设置运行时变量:
export default defineNuxtConfig({
runtimeConfig: {
mongodbUri: process.env.MONGODB_URI,
public: {},
},
});
用户登录校验
安装依赖
需要使用 express-session
、connect-mongodb-session
npm install express-session connect-mongodb-session
配置 Session
在 server/middleware
下创建一个中间件文件,例如 session.ts
:
import session from 'express-session';
import MongoDBStore from 'connect-mongodb-session';
const MongoDBStoreSession = MongoDBStore(session);
import { useRuntimeConfig } from '#imports';
export default defineEventHandler(async (event) => {
const config = useRuntimeConfig();
const mongoUri = config.mongoUri || 'mongodb://127.0.0.1:19999';
const store = new MongoDBStoreSession({
uri: mongoUri, // MongoDB URI
collection: 'sessions',
});
const sessionMiddleware = session({
secret: process.env.SESSION_SECRET || 'defaultSecret', // 用于加密 session 的 secret
resave: false,
saveUninitialized: false,
store,
cookie: {
maxAge: 1000 * 60 * 60, // 设置 cookie 过期时间为 1 小时
},
});
await new Promise((resolve, reject) => {
sessionMiddleware(event.node.req, event.node.res, (err) => {
if (err) reject(err);
resolve();
});
});
});
创建登录 API
import bcrypt from 'bcryptjs';
import Admin from '../models/Admin';
export default defineEventHandler(async (event) => {
const { username, password } = await readBody(event);
const user = await Admin.findOne({ username });
if (!user) {
throw createError({ statusCode: 401, statusMessage: 'Invalid username or password' });
}
const isPasswordValid = await bcrypt.compare(password, user.password);
if (!isPasswordValid) {
throw createError({ statusCode: 401, statusMessage: 'Invalid username or password' });
}
// 用户登录成功,设置会话
event.node.req.session.user = { id: user._id, username: user.username, role: user.role };
return { message: 'Login successful' };
});
创建用户校验中间件
在 server/middleware
下创建用户校验中间件,例如 auth.ts
:
export default defineEventHandler((event) => {
const session = event.node.res.locals.session;
if (!session?.user) {
throw createError({
statusCode: 401,
statusMessage: '未登录,请先登录',
});
}
});
可以将这个中间件应用到需要保护的 API 路由中。
可用可不用。
创建登出 API
// server/api/logout.post.js
export default defineEventHandler(async (event) => {
await new Promise((resolve, reject) => {
event.node.req.session.destroy((err) => {
if (err) reject(err);
resolve();
});
});
return { message: 'Logout successful' };
});
session 验证
// server/api/session.get.js
export default defineEventHandler((event) => {
const user = event.node.req.session.user;
return { user };
});
路由守卫
在 Nuxt 中实现页面访问控制,可以使用 middleware
:
创建中间件
在 middleware/auth.ts
中:
export default defineNuxtRouteMiddleware(async (to, from) => {
const { data: sessionData } = await useFetch('/api/session');
if (!sessionData.value?.user && to.name !== 'login') {
return navigateTo('/login');
}
});
应用中间件
export default defineNuxtConfig({
middleware: 'auth',
});
部署服务器
npm run build
nuxt 执行打包命令
npm run build
会生成 .output 文件夹,将其改名 output 文件夹,上传到服务器相应目录( /home/user/project)
ecosystem.config.cjs
首先服务器要先安装 pm2
npm install pm2 -g
然后该目录下编写 ecosystem.config.cjs 文件:
module.exports = {
apps: [
{
name: '', // 应用程序名称
port: '3000', // 监听端口
args: '', // 传递给脚本的参数
instances: 'max', // 应用实例数
autorestart: true, // 自动重启
watch: false, // 监控文件变动
exec_mode: 'cluster',
script: 'output/server/index.mjs',
},
],
};
启动脚本 start-nuxt.sh
在该目录下创建启动脚本 start-nuxt.sh
#!/bin/bash
BUILD_ID=DONTKILLME
echo "pm2 starting"
pm2 start ecosystem.config.js --env production
echo "pm2 started
保存后执行:
sh start-nuxt.sh
docker部署也差不多,但懒得打镜像。
其他踩坑事项
目前nuxt3
服务器打包会造成内存溢出导致卡死
现阶段只能本地打包好上传服务器
本地IP访问
export default defineNuxtConfig({
nitro: {
devServer: {
host: '0.0.0.0', // 或指定为 127.0.0.1,如果只想本地访问
port: 3000,
}
}
})
xlsx 转译
在服务器端使用xlsx解析excel的时候,本地运行正常,但build到服务器后就报错找不到文件,咨询了chatgpt大概是:
依赖库包含未转译的现代 JavaScript 代码:某些库可能包含 ES6+ 语法,默认情况下可能无法在某些运行环境中直接使用,因此需要通过 Babel 等工具进行转译。
特定库需要结合 Nuxt 进行 SSR 处理:某些库需要转译以兼容 Nuxt 的 SSR 渲染环境。
通过build.transpile
选项用于指定需要进行转译(transpile)的依赖项就可以。
export default defineNuxtConfig({
build: {
transpile: ['xlsx']
}
});