1. 背景说明
最近由于自己的技术栈进步需要,我在将我的前端项目从原本的Vue2.0+Vuex+ElementUI+JavaScript
重构成了Vue3.0+Pinia+ElementPlus+TypeScript
之后,我打算趁刚刚熟练完TypeScript
的机会,也将我原本使用JavaScript
编写的Express
后端代码也一并的进行TypeScript
的重构。
但是,众所周知,Express
作为一个极其轻量级的Node.js
后端框架,你只要安好express
的依赖,就可以直接编写app.js
然后往里面加接口、加监听,最后用node
或者nodemon
启动一下就好了;虽然简单易起服务,但是想要使其能够担起规范化、易维护的职责,还是需要有一套规范的框架、文件结构目录来创建。在这里,我选择了express
官方提供的一套脚手架express-generator
来完成初始项目的创建。但是官方貌似没有随着TypeScript
的流行进一步的升级它们的脚手架,因此生成的目录结构还是JavaScript
的。为了更好的配合前端接口的数据规范化,将其改造成TypeScript
是大势所趋。
鉴于目前网络上相关的教程较少,我决定自己总结一下我的项目创建历程,供大家参考。
2. 项目创建+改造流程说明
2.1 使用express-generator创建基本目录结构
首先,全局安装express-generator
依赖(之前装过的就不用装了):
npm install express-generator -g
安装完成之后,切到你想新建项目的文件夹里面,打开powershell
,在里面输入如下命令:
# 我这边用ts-express作为我的项目名称,实际创建时根据自身需要进行更改
express ts-express
之后用vscode
或者其他的IDE
打开它,然后在终端输入npm install
进行依赖的安装。
最终初步通过express-generator
创建的目录结构如下图所示:
其中:
- bin/www:是整个项目的启动脚本,内部配置了很多和服务启动相关的选项。
- public:公共资源,比如前端传给后端图片,后端拿到之后就放在public/images目录里面。
- routes:路由模块,配置的就是你和前端相交互的接口。
- views:视图模块,包括了一些基础的请求渲染视图。.jade文件是一种模板引擎文件,通常用于创建HTML文档。它以简洁、缩进层次分明的方式来表示HTML标记和结构,是一种被称为Jade的模板语言的源文件格式。当请求的接口不存在或者发生错误的时候,就会调用这些视图。
- app.js:express的入口程序,也是整个express的核心模块。app的主要功能有:
- 注册第三方的Node.js插件。在创建express项目的时候,肯定要用到很多第三方的库里面提供的方法,这时候就需要先通过npm进行下载,之后在app.js里面进行引入,再通过
app.use()
进行注册。 - 注册路由模块。引入routes里面写好的路由接口文件,通过
app.use()
进行注册并指定请求的url
。 - 配置全局错误中间件。一般发生请求错误的时候,可以直接通过在app.js里面配置全局错误中间件捕获错误并抛出,这样就不会因为请求错误而使得后端服务崩溃而自动关闭。
2.2 进行TypeScript改造
在进行TS改造之前,需要安装与TS相关的依赖(之前安装过的就不用装了):
npm install typescript -D
npm install ts-node -g
npm install ts-node-dev -g
进行TS项目的初始化,生成tsconfig.json
:
tsc --init
然后把原项目中所有的JS文件统统改成TS文件,把bin目录下的www改成server.ts:
之后会发现一大堆的错,慢慢来修复就可以了。
app.ts改造
在app.ts当中,把所有的CommonJS语法的同步引入require全部换成ES6的import异步引入即可。如:
var express = require('express');
换成:
import express from "express";
然后把末尾的模块导出也换成export default默认导出。
module.exports = app;
换成:
export default app;
然后把里面所有的var全部换成const。
上述步骤完成之后,会发现有很多的包引入错误:
因为我们还没有安装对应包的类型声明。
运行以下命令进行安装:
npm install @types/node @types/express @types/http-errors @types/cookie-parser @types/morgan @types/debug -D
下面两个路由的报错,我们按照改造app.ts的前三步对路由文件进行改造就可以了,也就是把引入模块、导出模块的方式改一下,然后把var改成const就可以。
把上面的类型报错解决了之后,app.ts中只剩下错误处理器的4个报错:
比较明显,是因为参数类型推断不出来。我们在最后加一个对这个函数的Express中错误处理函数的类型推断就可以了:
// error handler
app.use(function (err, req, res, next) {
// set locals, only providing error in development
res.locals.message = err.message;
res.locals.error = req.app.get("env") === "development" ? err : {};
// render the error page
res.status(err.status || 500);
res.render("error");
} as express.ErrorRequestHandler);
至此,app.ts的报错就已经全部解决了。
server.ts改造
也和改造app.ts同理,先把require换成import,然后把var全换成const。因为没有导出,所以不用改导出方式。
完成后,会发现三个报错:
其中的val和error报错,可以直接加个any解决报错;最下面的addr的报错,根据提示是因为:
因为addr
可能为null
,所以不能用.
选择属性。因为我们这边确定addr
不为null
,所以我们只用加上非空断言操作符!
就可以了。
function onListening() {
const addr = server.address();
const bind = typeof addr === "string" ? "pipe " + addr : "port " + addr!.port;
debug("Listening on " + bind);
}
至此,server.ts的报错就已经全部解决了。
配置tsconfig.json输出目录+package.json启动运行脚本
上面的两步都处理好之后,就可以进行编译输出目录的配置。因为最后我们启动服务还是需要采用JavaScript
的形式,TypeScript
只是我们编写代码的工具。
打开tsconfig.json,找到"outDir"配置项,把它打开,并配置编译输出的目录"./dist":
但是要注意,ts只是将编译的文件放入到了dist中,但是public和views这些静态的资源没有被打包进去。
所以要再继续配置静态资源的打包路径。我们在这里使用shelljs
进行配置。
ShellJS是一个基于Node.js的包,它提供了一组简单的命令行工具,让你可以在Node.js环境下使用类似于Unix shell(如Bash)的命令。它在Node.js中提供了一个简单的、易于使用的接口,用于执行常见的Shell命令、操作文件系统和进行其他常见的命令行任务。
安装shelljs
及其声明文件:
npm i shelljs @types/shelljs -D
在根目录新建一个文件:copyStatic.ts
,代码如下:
import * as shelljs from "shelljs";
shelljs.cp("-R" , "public" , "dist");
shelljs.cp("-R" , "views" , "dist");
这段代码的用途是将当前目录中的public
目录和views
目录,以及它们的所有子目录和文件,递归地复制到名为dist
的目录中。
安装nodemon
模块,可以自动监听项目文件的变化,会自动的重启项目:
npm install nodemon -D
完成后,在package.json内部修改脚本:
{
// ...
"scripts": {
"start": "nodemon ./dist/bin/server.js",
"copy-static": "ts-node copyStatic.ts",
"ts-build": "tsc",
"build": "npm run ts-build && npm run copy-static"
},
// ...
}
- start:对应启动服务。
- copy-static:使用ts-node运行copyStatic.ts文件内容,拷贝静态文件资源到dist目录下。
- ts-build:运行tsc,打包ts项目,根据outDir输出到对应的目录下。在我们这边就是dist目录下。
- build:就是第二条+第三条命令一起运行。
完成后,在tsconfig.json增加:
{
"compilerOptions": {
//...
},
“exclude”: [
“copyStatic.ts”
]
}
以排除对copyStatic.ts文件的编译,不打到dist里面。
和MySQL数据库建立连接
上述步骤完成之后,就可以开始建立和MySQL数据库的连接了。
首先安装mysql相关的依赖:
npm install mysql @types/mysql -D
然后在根目录下新建database
目录,再在里面新建index.ts
文件,之后再里面就可以配置自己想要链接的数据库选项了。下面给出模板:
// 导入mysql模块
import mysql from "mysql";
// 创建数据库连接对象
const db = mysql.createPool({
host: "你想连接的数据库的IP地址,本地的就默认127.0.0.1",
user: "你的用户名",
password: "你的密码",
database: "你想连接到的数据库",
});
// 向外共享db数据库连接对象
export default db;
上述文件编写好了之后,就可以导入它,并在对应的接口中进行数据库的操作了,这个放在后面说。
routes和controller分离
原本的express-generator给我们生成的routes目录下的文件:index.ts和users.ts都是把接口请求的url和接口的具体逻辑全部放在一起了,这样使得代码冗杂而不易于维护。因此,最好是把具体的逻辑处理函数和接口路径的注册两者分开来存放,在controller目录下定义好对应的处理函数后在末尾导出,最后在routes中对应的文件中进行引入即可。
以原本的index.ts文件为例:
import express from "express";
const router = express.Router();
/* GET home page. */
router.get("/", function (req, res, next) {
res.render("index", { title: "Express" });
});
export default router;
按照上述原则,我们应该把前面的url和后面的函数分开来进行存放。我们在根目录下新建controller目录,并在里面新建index.ts文件,内部具体的代码如下:
import db from "../database/index"; // 引入mysql数据库连接模块
import type { Request, Response } from "express"; // 引入express内部的类型定义
// 定义controller函数类型
const indexController: {
[key in string]: (req: Request, res: Response) => void;
} = {};
// 添加对应的接口↓
indexController.firstapi = (req, res) => {
res.send("Hello World!");
};
export default indexController;
然后,在routes里面的index.ts文件里面引入:
import express from "express";
import indexController from "../controller";
const router = express.Router();
router.get("/", indexController.firstapi);
export default router;
这样,我们就实现了业务和接口本身分离的模式,易于接口的注册和业务的维护。
最后,有关于db的使用方式,在这里不详细赘述(因为这单独讲会讲很多),没基础有兴趣的同学可以直接开始学习express框架哦!
类型定义分离daos
当然,在定义接口的时候自然免不了对于某个数据类型的定义,因此可以在根目录新建daos目录,内部存放相关的提前定义好的interface或者是type,便于在controller的具体业务逻辑内部进行引用,规范化代码的编写。在这边我就先不展示了。
2.3 改造完成!
经过上述步骤的改造之后,express+typescript+MySQL的项目已经基本搭建完成了,目录结构如下:
dist是打包后的代码哦!之后就可以快乐的利用TypeScript进行后端的编写了!!
3. 结语
希望这篇小小的教程能够带给有需要的同学帮助!我本人也是不断的试错才总结出来如上的方法,如果我哪里有写错或者是不恰当的地方,希望在评论区能够多多指正!
最后,附上我自己个人从零设计+实现的前后端分离的全栈项目:知识分享平台littleSharing~☆
。
-
前端技术栈:
Vue3.0+TypeScript+ElementPlus+Pinia+Vite
前端源码:github.com/nonhana/lit…
-
后端技术栈:
Node.js+Express+TypeScript+MySQL
(就是按照我上述的总结方法进行重构的框架)后端源码:github.com/nonhana/lit…
初学Vue或者对Express框架有兴趣的小伙伴可以看一看呀!