nodeJS server-side-developkoa开发实践1:基于 gulp 搭建 ts 自动构建环境
1. 目标概述
我们这一节的目标是同过gulp于相关工具构建一个基于 TypeScript 的 koa 项目开发环境,并且希望这个环境能够自动的监听 TypeScritp 源代码的变化,在发生源代码改变时执行自动编译为用于 NodeJS 的 JavaScript 代码,并启动服务。
2. 目录结构的确定
2.1 src 目录
为了方便,我们确定所有基于 TypeScript 编写的 源文件 同意放置在一个名为 src 的目录下,src 是 source(源)的简称,用这个名字放构建前的源代码也是惯例。
2.2 dist 目录
- [src] | - logger.ts | - app.ts - gulpfile.ts - packa.json - tsconfig.json
dist 目录是 我们构建后生成的 JavaScript 项目的目录,这些文件应当是直接便于 NodeJS 运行的,并且方便将其整体交付到 生产环境进行部署。
3. gulpfile.ts 文件
3.1 什么是 gulpfile
gulpfile 是用于 gulp 运行的文件,一般而言,它的名字为 gulpfile.js
,且不可自行改动。gulp 模块为我们提供了一个脚本,它接受一个路径,当这个路径中包含名字为 gulpfile.js 的文件时,将读取该文件中的 JavaScript 源代码以脚本方式运行。
3.2 使用 @esbuild-kit/cjs-loader
工具运行 gulpfile
以往我们使用 gulpfile.js
文件作为 gulp 的 cli 入口,而 gulpfile.ts
是不能直接被 gulp 所识别的,这时我们需要做一个动态转换进行运行,该工具就是 @esbuild-kit/cjs-loader,其原理为:
通过使用 esbuild 按需即时将 ESM 和 TypeScript 转换为 CommonJS
可以通过以下命令进行安装到你的项目中:
npm i @esbuild-kit/cjs-loader # or yarn yarn add -D @esbuild-kit/cjs-loader # ro pnpm pnpm i @esbuild-kit/cjs-loader -D
我们当然不会对于整个项目使用这种按需即时转换的方式运行,但是这种方式却尤其适合运行单个的文件。以往我们运行 gulpfile 的方式是:
gulp
你只需要在 package.json 中配置 script 字段中配置:
"gulp:build": "gulp"
3.3 编写 gulpfile.ts
// gulpfile.ts import path from "path"; import ts from "gulp-typescript"; import { src, dest, series } from "gulp"; import { deleteAsync } from "del"; const BASE_DIR = __dirname; const DIST_DIR = path.join(BASE_DIR, 'dist'); const APP_JS = path.join(DIST_DIR, 'app.js') // 清除旧文件 async function clearOldFiles(): Promise<void> { await deleteAsync(["dist/**/*"]); return; } // 编译 ts 文件为 js 文件 function buildTypescript(): NodeJS.ReadWriteStream { return src("./src/**/*.ts") .pipe( ts({ noImplicitAny: false, removeComments: false, esModuleInterop:true, declaration: true, target: "es2017", module: "commonjs", outDir: './dist', lib: ["DOM", "ES2015", "ES2016", "ES2017", "ES2018", "ES2022"], declarationDir: './dist/types' }) ) .pipe(dest("./dist")); } // 注册默认任务 const build = series([ clearOldFiles, // 启动时先删除旧文件 series([ buildTypescript, // 编译 ts 为 js ]), ]); export default build;
gulp-typescript
模块不会让你的 src 目录下的文件有合适的类型约束,你可以简单地单独建立一个 tsconfig.json 文件,写下相同配置:
{ "compilerOptions": { "target": "es2017", "module": "commonjs", "rootDir": "./", "lib": ["DOM", "ES2015", "ES2016", "ES2017", "ES2018", "ES2022"], "esModuleInterop": true, "forceConsistentCasingInFileNames": true, "strict": true, "noImplicitAny": false, "skipLibCheck": true, }, "include":[ "./src/**/*" ] }
3.4 配置 script 字段
// package.json script:{ // ... "ts:build": "gulp --require @esbuild-kit/cjs-loader" }
4. 编写 koa 的 hello work
4.1 使用日志记录器 logger
什么是日志器——顾名思义就是用于记录日志的该工具。一个日志记录器可以在终端分级别高亮输出日志,也可以将其导出文件进行后续调试,这对于一个项目后期的调试和维护尤其重要。
你可以简单地自己实现一个 logger:
import { green, yellow, red } from 'jc-color'; function logger(message: string, level: string) { const time = new Date().toLocaleString(); switch (level) { case 'info': green(`[INFO] [${time}] ${message}`).print(); break; case 'warn': yellow(`[WARN] [${time}] ${message}`).print(); break; case 'error': red(`[ERROR] [${time}] ${message}`).print(); break; default: console.log(message); } } logger('This is an info message', 'info'); logger('This is a warning message', 'warn'); logger('This is an error message', 'error');
当然如果实现一个基于类的日志器在使用的时候会更让人舒服一些:
import { blue, cyan, yellow, red } from 'jc-color'; class Logger { static debug(message: string) { blue(`[DEBUG] [${new Date().toISOString()}] ${message}`).ptint(); } static info(message: string) { cyan(`[INFO] [${new Date().toISOString()}] ${message}`).ptint(); } static warn(message: string) { yellow(`[WARN] [${new Date().toISOString()}] ${message}`).ptint(); } static error(message: string) { red(`[ERROR] [${new Date().toISOString()}] ${message}`).ptint(); } } Logger.debug('This is a debug message'); Logger.info('This is an info message'); Logger.warn('This is a warning message'); Logger.error('This is an error message');
你也可以使用一些线程的日志模块,比如 log4js:
npm i log4js # or yarn add log4js # or pnpm i log4js
// src/logger.ts import log4js from 'log4js' log4js.configure({ appenders: { fileout: { type: "file", filename: "fileout.log" }, datafileout: { type: "dateFile", filename: "mylog.log", pattern: ".yyyy-MM-dd hh-mm-ss-SSS" }, consoleout: { type: "console" }, }, categories: { default: { appenders: ["fileout", "consoleout"], level: "debug" }, anything: { appenders: ["consoleout"], level: "debug" } } }); export const logger = log4js.getLogger();
4.2 编写应用入口文件 app.ts
在 src 目录下创建 TypeScript 文件 app.ts
,如果你喜欢也可以叫做其他的名字,但需要是 ts 后缀。
// src/app.ts import Koa from 'koa'; import { logger } from './logger.ts const port = 3000; function bootstrap() { const app = new Koa(); app.use(async (ctx, next) => { await next(); ctx.response.type = 'text/html'; ctx.response.body = '<h1>Hello, World!</h1>'; }); app.listen(port, ()=>{ logger.debug(`app started at: http://localhost:${port}`); }) } bootstrap()
5. 再次配置 script 字段
现在我们需要从编译地文件中运行服务器地入口文件,他就是 dist/app.js
。你可以在 package.json 的 script 字段中这样配置:
{ script:{ // ... "start": "node ./dist/app.js", } }
这样你就可以在项目的根目录下使用如下命令启动服务器了:
pnpm run start
在之前我们配置过一个将 src 目录下 TypeScript 源代码编译为 JavaScript 的脚本:
{ script:{ // ... "ts:build": "gulp --require @esbuild-kit/cjs-loader", } }
因此现在我们需要运行两次命令:
- 第一次:运行编译命令编译TypeScript项目为JavaScript项目:
pnpm run ts:build
- 第二次:运行启动命令,启动服务器:
pnpm run start
这似乎太麻烦了,因此我们再加一条命令,这条新命令不干别的,只是按照顺序把上面两个命令都运行一遍:
{ script:{ "ts:build": "gulp --require @esbuild-kit/cjs-loader", "start": "node ./dist/app.js", "dev": "pnpm run ts:build && pnpm run start" } }
6. nodemon 全自动化响应运行
在前一节中我们已经可以使用一个命令就完成整个服务器代码的编译和运行两个过程了。但是在开发中,我们对源代码地改变是频繁的。往往在更改源代码后都想更快地看到最新的效果,这时我们就不得不随之频繁地点开终端,运行命令——哪怕命令已经很简单了,但仍然很麻烦。
为了解决这样一个问题,实现 “一劳永逸” 地运行,我们选哟使用另外一个工具,那就是 nodemon。nodemon 是一个可以用于监视文件变化的模块,可以使用如下方式添加到你的开发依赖中:
npm i nodemon -D # or yarn add -D nodemon # or pnpm i nodemon -D
现在我们在 package.json 文件中再次对 script 字段进行更改:
{ //... script:{ // ... "ts:build": "gulp --require @esbuild-kit/cjs-loader", "start": "node ./dist/app.js", "dev": "nodemon -e ts --watch src --exec \"pnpm run ts:build && pnpm run start\"" } }
现在,你只需要运行一次dev命令,就可以愉快地进行 TypeScript 开发 Koa 服务器了:
pnpm run dev
7. 小结
本文带大家搭建了一个全自动化编译、运行 由 TypeScript 编写的 koa 服务器,它能够自动的清理 旧有的编译项目,监视 TypeScript 源代码变化,自动编译为 JavaScript 并在编译完成后 自动地启动服务器。