TS + Nodejs:快速搭建一个服务端开发环境

4,276 阅读1分钟

ts + react 系列 中,数据都是本地的 mock,那么如何搭建一个真实的 API server 呢?

按照惯例,先搭建一个基于 ts + express 的服务端开发环境:

初始化

express-generator

express-generator:快速创建 Express 应用程序框架。

可以使用 npx 命令(在 Node.js 8.2.0 中可用)运行应用程序生成器。

$ npx express-generator ts-express

对于早期的 Node 版本,可将应用程序生成器作为全局 npm 软件包安装,然后启动它。

$ npm install -g express-generator
$ express ts-express

typescript

安装 typescript

$ npm i -D typescript

生成配置文件 tsconfig.json

$ tsc --init

工程改造(js2ts)

将工程里的 .js 文件 都重命名为 .ts 文件。

ts-express.png

bin/www 是整个服务端的启动脚本。

// bin/www
// 修改服务端端口 3000 -> 40001
var port = normalizePort(process.env.PORT || "4001");

改完之后,肯定出现了大量报错,莫慌,我们一起来修改:

Cannot find name 'require'.

require 未定义,因为缺少声明文件

$ npm i --save-dev @types/node @types/express

参数类型未定义

express 声明文件最终的导出方式是 export =,它对应的导入方式是 import = / import from

/*
 ** node_modules/@types/express/index.d.ts
 */
...
export = e;

而我们的导入方式是 require

/*
 ** app.ts
 */
var express = require("express");

统一将模块的导入改为 import from,并安装声明文件

$ npm i -D @types/http-errors @types/cookie-parser @types/morgan @types/debug

app.use

export interface IRouterHandler<T> {
  (...handlers: RequestHandler[]): T;
  (...handlers: RequestHandlerParams[]): T;
}

app.use 参数的类型是一个函数重载,但是 error handler 传入四个参数时,并没成功定位到 ErrorRequestHandler 类型。所以我们加一个类型断言

// 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);

对象可能为 null

添加类型保护 addr?

function onListening() {
  var addr = server.address();
  var bind = typeof addr === "string" ? "pipe " + addr : "port " + addr?.port;
  debug("Listening on " + bind);
}

参数 “val” 隐式具有 “any” 类型

指定 string 类型

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;
}

参数 “error” 隐式具有 “any” 类型

指定 any 类型

function onError(error: any) {
  ...
}

编译 ts2js

因为 node 是不可以编译 ts 文件的,所以 build 时需要编译 .ts.js

  1. 指定输出目录

    //  tsconfig.json
    {
      "compilerOptions": {
        "outDir": "./dist"
      }
    }
    
  2. 添加脚本

    {
      "scripts": {
        "build-ts": "tsc"
      }
    }
    

拷贝静态资源/模版文件

文件的拷贝工作通过 shell 脚本实现。

  1. 安装 shelljs

    npm i -D shelljs @types/shelljs
    
  2. 在根目录上新建 copyStatic.ts

    // copyStatic.ts
    import * as shelljs from "shelljs";
    
    // -R:递归
    shelljs.cp("-R", "public", "dist");
    shelljs.cp("-R", "views", "dist");
    
  3. 添加运行脚本

    // package.json
    {
      "script": {
        "copy-static": "ts-node copyStatic.ts"
      }
    }
    

    ts-node 需要提前安装

    $ npm i -D ts-node
    

npm run build

添加 build 脚本

// package.json
{
  "script": {
    "build-ts": "tsc",
    "copy-static": "ts-node copyStatic.ts",
    "build": "npm run build-ts && npm run copy-static"
  }
}

编译时,排除 copyStatic.ts

// tsconfig.json
{
  "exclude": ["copyStatic.ts"]
}

启动服务器

// package.json
{
  "script": {
    "start": "node /dist/bin/server.js"
  }
}
$ npm start

打开 http://localhost:4001/,就可以看到 express 页面了。

watch 监控模式

nodemon 是一个自动重启 node 应用的工具,当监听的文件或监听目录下的文件发生修改时,自动重启应用。

我们修改文件后,第一步需要重新 build,第二步需要重启服务。nodemon 让我们开发时只需关注代码即可,不再需要手动重启服务。

  1. 安装 nodemon

    $ npm i nodemon -D
    
  2. 配置脚本

    // package.json
    {
      "script": {
        "start": "node ./dist/bin/server.js",
        "watch": "nodemon ./dist/bin/server.js"
      }
    }
    

TS + Nodejs 系列