nextjs全栈项目tiptap加antd项目开发总结

60 阅读2分钟

前言

nextjs是一款定义为全栈开发的框架,具备nodejs后端的特性, 也就是支持SSR,官网也说能支持SSG,懂点前端知识再加点nodejs知识就能写一个全栈应用.

背景

公司在逐渐发展,需要做一个公司的论坛,这个任务页面部分交给我了,需要每一个文章都需要在搜索引擎被搜索到SSR(其实我感觉jsp的SSR效果是不是更好捏0_0).

项目搭建

结合上面,我也懒的废话了,开始搭建项目(时间2025年7月5号),我考虑的是 nextjs@14+antd+tiptap+zustand+sass+react-query+react-infinite-scroll-component

目录结构

image.png

总结

看到这个目录结构,基本就会了百分之80了,可能你看到这个Deploy.js不太熟悉代码如下

const path = require('path');
const Client = require('ssh2').Client;
const outDir = require("./build/outDir")

require('dotenv').config({ path: '.env.development' });
// SSH连接配置
const sshConfig = {
  host: process.env.SSHHOST ,
  port: process.env.SSHPORT ,
  username: process.env.SSHUSER ,
  password: process.env.SSHPASSWORD ,
};

// 本地目录路径和远程目录路径
const localDir = __dirname;
const remoteDir = process.env.REMOTE_DIR;
// 创建SSH连接
const conn = new Client();

// 新增:递归创建远程目录的函数
const ensureRemoteDir = (sftp, remotePath) => {
  return new Promise((resolve, reject) => {
    // 先检查目录是否存在
    sftp.stat(remotePath, (statErr, stats) => {
      if (!statErr && stats.isDirectory()) {
        // 目录已存在
        resolve();
        return;
      }

      // 目录不存在,创建它
      sftp.mkdir(remotePath, (err) => {
        if (err) {
          if (err.code === 4 || err.code === 2) { // 目录不存在或父目录不存在
            const parentDir = path.dirname(remotePath);
            if (parentDir !== remotePath && parentDir !== '/') {
              ensureRemoteDir(sftp, parentDir)
                .then(() => ensureRemoteDir(sftp, remotePath))
                .then(resolve)
                .catch(reject);
            } else {
              reject(err);
            }
          } else if (err.code === 11) {
            // 目录已存在(竞态条件)
            resolve();
          } else {
            reject(err);
          }
        } else {
          console.log(`创建远程目录:${remotePath}`);
          resolve();
        }
      });
    });
  });
};

// 监听ready事件
conn.on('ready', () => {
  console.log('SSH连接成功');
  const execDir = remoteDir + outDir
  // 将本地目录下的所有文件上传至服务器上指定目录
  const uploadPromise = [];
  conn.sftp((err, sftp) => {
    if (err) throw err;
    const files = [outDir];

    const uploadFile = (file) => {
      return new Promise((resolve, reject) => {
        try {
          const localFilePath = localDir + '/' + file;
          const remoteFilePath = remoteDir + '/' + file;

          // 新增:确保远程目录存在
          const remoteFileDir = path.dirname(remoteFilePath);
          ensureRemoteDir(sftp, remoteFileDir).then(() => {
            const readStream = fs.createReadStream(localFilePath);
            const writeStream = sftp.createWriteStream(remoteFilePath);
            writeStream.on('close', () => {
              console.log(`文件 ${file} 上传成功`);
              resolve();
            });
            writeStream.on('error', (err) => {
              console.log(`文件 ${file} 上传失败:${err}`);
              reject(err);
            });
            readStream.pipe(writeStream);
          }).catch(reject);
        } catch (error) {
          reject(error);
        }
      });
    }

    const uploadDir = (files) => {
      files.forEach((file) => {
        // 修复:使用正确的文件路径
        const fullPath = localDir + '/' + file;
        // 检查是否存在文件
        const isExist = fs.existsSync(fullPath);
        if (!isExist) {
          console.log(`文件 ${file} 不存在`);
        } else {
          try {
            const stat = fs.lstatSync(fullPath);
            if (stat.isDirectory()) {
              const dirFiles = fs.readdirSync(fullPath);
              uploadDir(dirFiles.map((dirFile) => file + '/' + dirFile));
            } else if (stat.isFile()) {
              uploadPromise.push(uploadFile(file));
            }
          } catch (error) {
            console.log(`处理文件 ${file} 时出错:${error.message}`);
          }
        }
      });
    }
    uploadDir(files);

    Promise.all(uploadPromise).then(() => {
      console.log('所有文件上传成功');
      // 执行SSH命令

      conn.exec(`cd ${execDir} && npm i --silent && pm2 del all`, (err, stream) => {
        if (err) throw err;

        stream.on('close', (code, signal) => {
          console.log(`npm install 命令执行完毕,退出码: ${code}`);

          // npm install 完成后执行其他命令
          conn.exec(`ls -l ${execDir} && cd ${execDir} && pm2 start ecosystem.config.js`, (err, stream) => {
            if (err) throw err;

            stream.on('close', () => {
              console.log('所有远程命令执行完毕');
              conn.end();
            }).on('data', (data) => {
              console.log('远程命令输出:\n' + data);
            }).stderr.on('data', (data) => {
              console.log('远程命令错误:\n' + data);
            });
          });
        }).on('data', (data) => {
          console.log('npm install 输出:\n' + data);
        }).stderr.on('data', (data) => {
          console.log('npm install 错误:\n' + data);
        });
      });
    }).catch((err) => {
      console.log('上传失败:' + err);
    });
  });
}).connect(sshConfig);

// 监听error事件
conn.on('error', (err) => {
  console.error('SSH连接失败', err);
});

// 结束SSH连接
conn.on('end', () => {
  console.log('SSH连接已断开');
});