阅读 3777

如何使用 Typescript 搭建一个 Node.js 服务

前言

很多时候,我们在学习了某个新东西以后,却不知道如何在项目中运用自己学到的新东西。本篇旨在使用 Node.js + TypeScript + Express 实现一个简单的后台服务器,以此更加深入的理解 ts 在实际开发中的运用。

基础知识

阅读本篇专栏前,你需要掌握以下知识:

  1. 掌握 JavaScript 基础;
  2. 了解 Node.js 及 Express 框架;
  3. 了解 TypeScript 基本知识,可参考 TypeScript 要点知识整理

项目初始化

  1. 创建项目文件夹 simple-login-node,进入文件夹;
  2. 进入项目文件夹,通过 npm inittsc --init 以及 tslint --init 初始化项目,初始化前,请确保已全局安装过 npm、typescript 以及 tslint,可通过以下命令进行安装;
npm install -g npm
npm install -g typescript
npm install -g tslint
复制代码
  1. 在根目录下创建 /src 目录,在目录下建立 app.ts 文件。

进行以上操作后,我们的目录结构如下:

具体文件说明

/src/app.ts // 程序入口文件
.npmrc // 项目 npm 配置文件,可针对项目单独设置 npm 的配置
package.json // 项目模块描述文件
tsconfig.json // typescript 配置文件
tslint.json // tslint 配置文件
复制代码

以上就是一个使用 typescript 编写的 node.js 项目的基本结构,我们来进一步细分,每个配置有哪些重要的配置呢?

package.json

  • scripts
    配置指令集合,当我们使用 npm run * 的时候,* 表示我们在 script 节点下配置的指令。
"scripts": {
    "test": "echo \"Error: no test specified\" && exit 1"
}
复制代码

以上配置,当我们执行 npm run test的时候,实践会将 test 后配置的语句进行执行,即 echo "Error: no test specified" && exit 1

  • dependencies
    在项目下使用 npm install --save * 安装的包,及其版本
  • devDependencies
    在项目下使用 npm install --save-dev * 安装的包,仅在开发模式下安装。因为 typescript 仅在编译时检查代码,即仅在开发时有效,因此很多 typecript 相关模块以及带声明文件的第三方模块仅在开发模式下安装。
    下面我们安装 express 模块,在项目根目录下运行:
npm install --save express // 安装普通 express 模块,并在 dependencies 下生成包记录
npm install --save-dev @types/express // 安装带有声明文件的 express 模块,并在 devDependencies 下生成包记录,仅开发模式下安装。
复制代码

此时 package.json 如下(仅贴关键代码):

"dependencies": {
  "express": "^4.17.1"
},
"devDependencies": {
  "@types/express": "^4.17.0"
}
复制代码

tsconfig.json

如果一个目录下存在存在 tsconfig.json 文件,那么就意味着这个目录是 typescipt 项目的根目录。tsconfig.json 定义了用来编译这个项目的根目录及编译选项。让我们一起来看看其中的一些关键配置项。

{
  /* 编译选项 */
  "compilerOptions": {
    "target": "es6", // 指定 EECMAScript 的目标版本, 这里我们采用 es6
    "module": "commonjs", // 指定编译生成哪个模块的系统代码,考虑到兼容性,这里我们设置成 commonjs
    "outDir": "dist", // 编译输出目录,即 .ts 文件编译成 .js 文件后的输出目录。这里设置为根目录下的 /dist 目录
    "strict": true, // 严格模式
    "noImplicitAny": false, // 在表达式和声明上有隐含的 any类型时报错。设为 false 避免当类型推论为 any 时报错
    "moduleResolution": "node", // 决定如何处理模块。设置为 node
    "baseUrl": "./", // 定义 ts 项目的根目录,设置 paths 前必须设置
    //定义路径别名,即当我们通过路径引入一个模块时,可以使用别名来进行引入,这里第一个 * 设置是为了引入第三方模块; 第二个 '@/*' 则是为了直接快捷的导入 /src 下的模块。
    "paths": {
      "*": ["node_modules/*", "src/types/*"],
      "@/*": ["src/*"]
    }
    "esModuleInterop": true // 模块导入方式
  },
  "include": ["src"], // 需要编译的 ts 文件,这里设置为 src 目录下的所有文件
  "exclude": ["node_modules"] // 编译需要排除的文件目录
}
复制代码

tslint.json

tslint 配置文件,用于检查代码是否符合设置的代码规范,不符合将会提示对应错误,具体设置请看文章最后的源码。

搭建 express 服务

  1. 安装 body-parser 模块,用于处理 post 请求的请求体
npm install --save body-parser
复制代码
  1. 在 /src 目录下建立 config 目录下,目录下建立 index.ts,用于存放项目的一些开修改配置,我们将 Express 服务占用的端口写在其中,代码如下:
const systemConfig = {
  port: 8000,
};

export default {
  systemConfig,
};
复制代码
  1. 我们这可是 typescript 项目,我们试着定义一下 配置中 systemConfig 变量的类型,在 /src 目录下建立 types 目录,用于存放项目的所有类型定义,在 /types 下建立 config.ts 文件,对类型进行定义,并在配置文件中引入
// /src/types/config.ts
export interface ISystemConfig {
  port: number;
}

// /src/config/index.ts
import { ISystemConfig } from "@/types/config";

const systemConfig: ISystemConfig = {
  port: 8000,
};
复制代码

这样就限定了 systemConfig 对象下只有一个属性 port,且类型为 number,并且这样我们在使用 systemConfig 时,将会有代码提示其内部属性哦。
4. 在 /src/app.ts 下创建 Express 服务

// 第三方模块
import bodyParser from 'body-parser';
import express from 'express';
import { NextFunction, Request, Response } from 'express'; // express 申明文件定义的类型

// 自定义模块
import { systemConfig } from './config';

const app = express();

// 处理 post 请求的请求体,限制大小最多为 20 兆
app.use(bodyParser.urlencoded({ limit: '20mb', extended: true }));
app.use(bodyParser.json({ limit: '20mb' }));

// error handler
app.use(function(err: Error, req: Request, res: Response, next: NextFunction) {
  return res.sendStatus(500);
});

app.listen(systemConfig.port, function() {
  console.log(`the server is start at port ${systemConfig.port}`);
});

export default app;
复制代码

项目运行

大功告成,我们来运行试试

node ./src/app.ts
复制代码

但是。。语法错误

这时候我们想起来, ts 文件是无法直接运行,我们来先编译一下自己的 ts 代码,在根目录运行

tsc
复制代码

还记得我们之前在 tsconfig.json 中设置了 编译后的 js 代码的输出目录为 /dist 目录,我们执行命令后,可以看到 /dist 目录在根目录下生成了!

来看看 dist 目录下结构

可以看到,所有代码都生成了对应的 js 代码,且目录结构与 ts 代码目录结构一致。 我们运行 app.js 看看
耶!成功了!我们浏览器访问看看

因为没有定义具体的接口,所以返回了 “Cannot Get /”,但是我们服务器已经启动了!

一个问题

难道我们更新了代码都要重新编译,然后在启动服务吗?

解决办法

安装 nodemon 以及 ts-node 用于监控 指定目录 ts 代码的改动,改动后自动编译运行项目。

// 仅开发环境安装
npm install --save-dev nodemon // nodemon 用来监视node.js应用程序中的任何更改并自动重启服务
npm install --save-dev ts-node // ts-node 命令可直接执行 TypeScript 源文件而不需要预先编译
复制代码

安装完成后,我们使用 nodemon 监控 /src 下所有 ts 文件的改动,当改动发生后,使用 ts-node 直接重新app.ts。命令如下:

nodemon --watch 'src/' -e ts --exec 'ts-node' ./src/app.ts
复制代码

可以看到,当我们改动 config 中端口号为 8001 后,项目真的有自动重启,棒棒哒!

根据 ten_noc 的提醒,在windows下,脚本命令会提示找不到ts-node,需要安装cross-env,然后将启动命令修改为:

cross-env nodemon --watch 'src/' -e ts --exec 'ts-node' ./src/app.ts
复制代码

优化一下

可以看到,上面那条启动命令有点儿长,每次都输入这么一长串是不是不太友好!那么有没有简化的方式呢?有!还记得前面我们说过,package.json 中可以配置启动脚本!

//  在 script 标签中加入 dev 配置
"scripts": {
    "test": "echo \"Error: no test specified\" && exit 1",
    "dev": "nodemon --watch 'src/' -e ts --exec 'ts-node' ./src/app.ts"
  }
复制代码

然后我们只要运行 npm run dev 就可以启动项目了,看看效果

耶!给自己一个赞0.0

总结及源码

本篇简单地搭建了一个基于 typecript 的 express 服务,关于 typescript,笔者也是在学习过程中,如果有不足的地方欢迎各位大佬在评论区指出。如果你觉得对你有帮助的话,不要忘了点赞和关注哦^o^
本节源码地址:github.com/liyuezu/sim…