Express 项目 ts 支持
安装依赖
安装 express 脚手架工具
npm i express-generator -g
shell
express ts-express # 使用脚手架工具
shell
安装 typescript
pnpm i typescript -D
shell
生成 tsconfig 文件
tsc --init
shell
修改文件
首先我们将 .js
后缀文件修改为 .ts
文件,然后将 commonjs
模块代码转换为 es6
模块代码。
修改 bin 目录下的 bin 文件为 server.ts 文件
安装缺少的声明文件。
pnpm i @types/node @types/express -D
pnpm i @types/http-errors @types/cookie-parser @types/morgan @types/debug -D
shell
代码如下:
bin/server.ts
#!/usr/bin/env node
/**
* Module dependencies.
*/
import app from '../app';
import debug from 'debug';
debug('ts-express:server');
import http from 'http';
/**
* Get port from environment and store in Express.
*/
var port = normalizePort(process.env.PORT || '4001');
app.set('port', port);
/**
* Create HTTP server.
*/
var server = http.createServer(app);
/**
* Listen on provided port, on all network interfaces.
*/
server.listen(port);
server.on('error', onError);
server.on('listening', onListening);
/**
* Normalize a port into a number, string, or false.
*/
function normalizePort(val: string) {
var port = parseInt(val, 10);
if (isNaN(port)) {
// named pipe
return val;
}
if (port >= 0) {
// port number
return port;
}
return false;
}
/**
* Event listener for HTTP server "error" event.
*/
function onError(error: any) {
if (error.syscall !== 'listen') {
throw error;
}
var bind = typeof port === 'string'
? 'Pipe ' + port
: 'Port ' + port;
// handle specific listen errors with friendly messages
switch (error.code) {
case 'EACCES':
console.error(bind + ' requires elevated privileges');
process.exit(1);
break;
case 'EADDRINUSE':
console.error(bind + ' is already in use');
process.exit(1);
break;
default:
throw error;
}
}
/**
* Event listener for HTTP server "listening" event.
*/
function onListening() {
var addr = server.address();
var bind = typeof addr === 'string'
? 'pipe ' + addr
: 'port ' + addr!.port;
debug('Listening on ' + bind);
}
typescript
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;
typescript
routes/users.ts
import express from 'express';
const router = express.Router();
/* GET users listing. */
router.get('/', function(req, res, next) {
res.send('respond with a resource');
});
export default router;
typescript
app.ts
import createError from 'http-errors';
import path from 'path';
import cookieParser from 'cookie-parser';
import logger from 'morgan';
import express from 'express';
import indexRouter from './routes/index';
import usersRouter from './routes/users';
const app = express();
// view engine setup
app.set('views', path.join(__dirname, 'views'));
app.set('view engine', 'jade');
app.use(logger('dev'));
app.use(express.json());
app.use(express.urlencoded({ extended: false }));
app.use(cookieParser());
app.use(express.static(path.join(__dirname, 'public')));
app.use('/', indexRouter);
app.use('/users', usersRouter);
// catch 404 and forward to error handler
app.use(function(req, res, next) {
next(createError(404));
});
// 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);
export default app;
typescript
package.json
{
"name": "ts-express",
"version": "0.0.0",
"private": true,
"scripts": {
// ...
},
"dependencies": {
"cookie-parser": "~1.4.4",
"debug": "~2.6.9",
"express": "~4.16.1",
"http-errors": "~1.6.3",
"jade": "~1.11.0",
"morgan": "~1.9.1"
},
"devDependencies": {
"@types/cookie-parser": "^1.4.3",
"@types/debug": "^4.1.7",
"@types/express": "^4.17.13",
"@types/http-errors": "^1.8.2",
"@types/morgan": "^1.9.3",
"@types/node": "^17.0.41",
"typescript": "^4.7.3"
}
}
json
下一步我们需要把所有的 ts 文件转成 js 文件,因为 node 无法执行 ts 文件。
首先我们修改 tsconfig.json
指定输出目录。
{
compilerOptions: {
outDir: "./dist"
}
}
json
然后修改 package.json 文件
"scripts": {
"start": "node ./bin/www",
"build-ts": "tsc"
}
json
现在我们的项目就可以正常打包。
构建脚本处理
运行上述命令,会发现模板文件,静态文件没有拷贝到 dist 目录下。
下面我们需要编写一个脚本处理文件拷贝的问题。
首先我们需要安装一个工具。并在根目录下,新建一个 copy.ts
文件。
pnpm i shelljs @types/shelljs -D
shell
// src/copy.ts
import * as shelljs from 'shelljs';
shelljs.cp('-R', 'public', 'dist');
shelljs.cp('-R', 'views', 'dist');
typescript
我们需要增加一个 scripts 脚本,我们我们还需要安装 ts-node
。
"scripts": {
"start": "node ./bin/www",
"build-ts": "tsc",
+ "copy-static": "ts-node copy.ts"
+ "build": "npm run build-ts && npm run copy-static"
},
diff
pnpm i ts-node -D
shell
执行 npm run build
命令就完成编译以及文件拷贝的工作。
不过还存在一个问题,执行脚本后,也会把根目录下的 copy.ts
的编译结果输出。我们需要配置一下 tsconfig.json
。
{
"exclude": [
"copy.ts"
]
}
json
这样就符合我们的期望,最后我们修改一下启动脚本,
{
"scripts": {
"start": "node ./dist/bin/server.js",
"build-ts": "tsc",
"copy-static": "ts-node copy.ts",
"build": "npm run build-ts && npm run copy-static"
}
}
json
运行 npm run start
脚本,访问 http://localhost:4001/
可以看到页面可以正常访问了。
我们我们再增加一种编译模式,原因是当我们修改文件后,需要重新构建 ts 文件并且重启服务。
{
"scripts": {
"start": "node ./dist/bin/server.js",
"watch": "nodemon ./dist/bin/server.js",
"build-ts": "tsc",
"copy-static": "ts-node copy.ts",
"build": "npm run build-ts && npm run copy-static"
}
}
json
nodemon
个人建议全局安装即可。
至此,我们的 express 项目已经改造完毕。