三、项目初始化:使用 Express/Koa 创建基础服务、项目目录结构规范、ESLint 与 Prettier 代码规范配置

好,咱们正式开始动手了。

这一章,说白了就是搭架子。架子搭得稳,后面写代码才不慌。我见过太多项目,一开始图省事,随便建个文件夹就开始写,结果到后面目录乱成一锅粥,代码风格五花八门,连格式化都靠手敲空格——嗯,那场面,想想都头疼。

所以这一章,我会带你一步步把基础服务搭起来,把规矩立好。咱们用 Express 和 Koa 各建一个基础服务,然后统一目录结构,最后把 ESLint 和 Prettier 配好。这样后面 27 章,你只管写业务逻辑,不用再操心这些破事。

3.1 使用 Express 创建基础服务

Express 是我用得最多的框架,没有之一。它轻量、灵活,社区资源丰富。我个人习惯用它来快速搭建 RESTful API。

先初始化项目:

mkdir my-microservice
cd my-microservice
npm init -y
npm install express

然后创建一个 app.js

const express = require('express');
const app = express();
const PORT = process.env.PORT || 3000;

app.use(express.json());

app.get('/health', (req, res) => {
  res.json({ status: 'ok', timestamp: new Date().toISOString() });
});

app.listen(PORT, () => {
  console.log(`Server running on port ${PORT}`);
});

你看,就这么几行,一个基础服务就跑起来了。我在项目中遇到过有人把几百行代码全塞进一个 app.js,那简直是灾难。所以咱们后面会拆,但现在先这样。

小提示: 记得把端口号用环境变量控制。这样部署到不同环境时,改个变量就行,不用改代码。

3.2 使用 Koa 创建基础服务

Koa 是 Express 的升级版,由同一团队开发。它用了 async/await,解决了回调地狱的问题。我个人觉得,Koa 更适合写中间件密集型的服务。

安装 Koa:

npm install koa

创建 app-koa.js

const Koa = require('koa');
const app = new Koa();
const PORT = process.env.PORT || 3001;

app.use(async (ctx, next) => {
  if (ctx.path === '/health') {
    ctx.body = { status: 'ok', timestamp: new Date().toISOString() };
  } else {
    await next();
  }
});

app.listen(PORT, () => {
  console.log(`Koa server running on port ${PORT}`);
});

你可能会问:为什么用两个框架?其实在微服务架构里,不同服务可以用不同技术栈。比如用户服务用 Express,订单服务用 Koa,完全没问题。我之前的项目就这么干过,各取所长嘛。

注意: Koa 的中间件执行顺序和 Express 不一样。Koa 是洋葱模型,Express 是线性模型。写中间件时一定要搞清楚,不然容易出 bug。

3.3 项目目录结构规范

目录结构这东西,没有绝对的标准,但有一些约定俗成的做法。我总结了一套比较通用的结构,适合中小型微服务项目:

my-microservice/
├── src/
│   ├── controllers/    # 控制器,处理请求和响应
│   ├── services/       # 业务逻辑层
│   ├── models/         # 数据模型
│   ├── middlewares/     # 中间件
│   ├── routes/         # 路由定义
│   ├── utils/          # 工具函数
│   ├── config/         # 配置文件
│   └── app.js          # 入口文件
├── tests/              # 测试文件
├── .env                # 环境变量
├── .eslintrc.js        # ESLint 配置
├── .prettierrc         # Prettier 配置
├── package.json
└── README.md

为什么要这么分?说白了就是「各司其职」。控制器只管接请求、返回响应;服务层只管业务逻辑;模型只管数据操作。这样每个人改自己的部分,互不干扰。

我曾经在一个项目里看到有人把数据库查询、业务判断、返回结果全写在一个函数里,那代码简直没法看。后来重构时,光是拆文件就花了两天。所以一开始就把规矩立好,后面省心。

3.4 ESLint 与 Prettier 代码规范配置

代码规范这事,说大不大,说小不小。但团队协作时,统一的代码风格能减少很多不必要的争吵。我见过因为缩进用空格还是 Tab 吵起来的,真的没必要。

3.4.1 安装 ESLint

npm install eslint --save-dev
npx eslint --init

执行 --init 后,会问你几个问题:

  • 选择「Use a popular style guide」
  • 选择「Airbnb」或「Standard」——我个人推荐 Standard,规则少一些,没那么严格
  • 选择「JavaScript modules (import/export)」
  • 选择「None of these」

生成 .eslintrc.js 后,可以加一些自定义规则:

module.exports = {
  env: {
    node: true,
    es2021: true,
  },
  extends: 'standard',
  rules: {
    'no-console': 'off',  // 允许 console.log
    'semi': ['error', 'always'],  // 强制分号
    'indent': ['error', 2],  // 缩进2个空格
  },
};

3.4.2 安装 Prettier

npm install prettier --save-dev

创建 .prettierrc

{
  "semi": true,
  "singleQuote": true,
  "tabWidth": 2,
  "trailingComma": "all",
  "printWidth": 100
}

然后让 ESLint 和 Prettier 配合工作:

npm install eslint-config-prettier eslint-plugin-prettier --save-dev

.eslintrc.js 中加上:

extends: [
  'standard',
  'prettier',  // 放在最后,覆盖 ESLint 的格式规则
],
plugins: ['prettier'],
rules: {
  'prettier/prettier': 'error',
},
核心思路: ESLint 负责代码质量(比如未使用的变量、潜在 bug),Prettier 负责代码格式(比如缩进、分号、引号)。两者分工明确,互不干扰。

3.4.3 配置 VSCode 自动格式化

在项目根目录创建 .vscode/settings.json

{
  "editor.formatOnSave": true,
  "editor.defaultFormatter": "esbenp.prettier-vscode",
  "editor.codeActionsOnSave": {
    "source.fixAll.eslint": true
  }
}

这样每次保存文件,Prettier 会自动格式化,ESLint 会自动修复。你只管写代码,剩下的交给工具。

避坑指南: 我曾经遇到过 ESLint 和 Prettier 冲突的情况,比如 ESLint 要求缩进 4 个空格,Prettier 要求 2 个。解决办法就是让 Prettier 的规则覆盖 ESLint 的格式规则,用 eslint-config-prettier 关掉 ESLint 中与格式相关的规则。

3.5 本章小结

这一章我们做了三件事:

  • 用 Express 和 Koa 各搭了一个基础服务
  • 定了一套项目目录结构规范
  • 配好了 ESLint 和 Prettier

架子搭好了,规矩立好了。下一章咱们就开始写真正的业务逻辑了。你想想看,如果一开始这些没做好,后面写代码时是不是会各种别扭?嗯,反正我是受不了代码乱七八糟的。

好,这一章就到这。有问题随时问我。