Nuxt3开发及部署服务器全流程

Aditya2024-12-25小程序NuxtMongoDB

引子

上次用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-sessionconnect-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大概是:

  1. 依赖库包含未转译的现代 JavaScript 代码:某些库可能包含 ES6+ 语法,默认情况下可能无法在某些运行环境中直接使用,因此需要通过 Babel 等工具进行转译。

  2. 特定库需要结合 Nuxt 进行 SSR 处理:某些库需要转译以兼容 Nuxt 的 SSR 渲染环境。

通过build.transpile 选项用于指定需要进行转译(transpile)的依赖项就可以。

export default defineNuxtConfig({
  build: {
    transpile: ['xlsx']
  }
});

Last Updated 2024/12/27 11:36:49