• nodejs项目升级为ts



    title: nodejs项目升级为ts
    date: 2020-01-12
    categories:

    • frontend
      tags:
    • nodejs
    • typescript

    前言

    最近把之前自用的一个小型nodejs框架(koa2+mongo)升级为了ts,在此记录一下大致步骤。

    安装typescript

    直接使用npm 安装

    npm i -S -D typescript
    

    建议不要只安装到全局,避免不同机器上的typescript版本不一致。
    安装完之后,我们新建一个tsconfig.json(或者tsc init),这是我的内容:

    {
      "compilerOptions": {
        // "incremental": true,                   /* 增量编译 提高编译速度*/
        "target": "ES2017",                       /* 编译目标ES版本*/
        "module": "commonjs",                     /* 编译目标模块系统*/
        // "lib": [],                             /* 编译过程中需要引入的库文件列表*/
        "declaration": true,                      /* 编译时创建声明文件 */
        "outDir": "dist",                         /* ts编译输出目录 */
        "baseUrl": "src",
        "paths": {
            "@/*": ["*"],
        },
        // "importHelpers": true,                 /* 从tslib导入辅助工具函数(如__importDefault)*/
        "strict": false,                           /* 严格模式开关 等价于noImplicitAny、strictNullChecks、strictFunctionTypes、strictBindCallApply等设置true */
        "noImplicitAny": false,
        "noUnusedLocals": false,                   /* 未使用局部变量报错*/
        "noUnusedParameters": false,               /* 未使用参数报错*/
        "noImplicitReturns": true,                /* 有代码路径没有返回值时报错*/
        "noFallthroughCasesInSwitch": true,       /* 不允许switch的case语句贯穿*/
        "moduleResolution": "node",               /* 模块解析策略 */
        "typeRoots": [                            /* 要包含的类型声明文件路径列表*/
          "./typings",
          "./node_modules/@types"
          ],                      
        "allowSyntheticDefaultImports": true,    /* 允许从没有设置默认导出的模块中默认导入,仅用于提示,不影响编译结果*/
        "esModuleInterop": true                  /* 允许编译生成文件时,在代码中注入工具类(__importDefault、__importStar)对ESM与commonjs混用情况做兼容处理*/
    
      },
      "include": [                                /* 需要编译的文件 */
        "src/**/*.ts",
        "typings/**/*.ts"
      ],
      "exclude": [                                /* 编译需要排除的文件 */
        "node_modules/**"
      ],
    }
      
    

    集成eslint代码规范

    其实ts最早都是使用tslint,但2019开始ts官方决定投奔eslint,嗯,所以我们肯定也用eslint了。

    1.安装依赖

    首先安装eslint以及ts的parser、plugin:

    npm i eslint @typescript-eslint/parser @typescript-eslint/eslint-plugin -D
    

    说白了,就是eslint需要配置ts的parser、以及ts的规则,这是我的.eslintrc.js文件内容:

    module.exports = {
        'parser':'@typescript-eslint/parser',
        'extends': ['standard', 'plugin:@typescript-eslint/recommended'],
        'env': {'node': true},
        'rules':{
            '@typescript-eslint/ban-ts-ignore': 0
        }
    }
    

    extends里面很明显多了一个standard,这是因为@typescript-eslint/recommended的规则并不完善,所以我们还需要使用eslint的standard规则,安装以下依赖:

    npm i eslint-config-standard eslint-plugin-import eslint-plugin-node eslint-plugin-promise eslint-plugin-standard -D
    

    除了eslint-config-standard,后面几个都是standard的依赖
    rules是用来覆盖规则的,我这里加了一个ban-ts-ignore,因为我有的地方要写ts-ignore,不加他就报错了

    都配置好后,我们再来package.json的scripts中追加两行命令:

    "lint": "eslint --ext .js --ext .ts src/",
    "lint-fix": "eslint --fix --ext .js --ext .ts src/",
    

    lint用来检测代码格式问题,lint-fix用来自动修复代码格式

    2.配置vscode

    ts的标配,毫无疑问是vscode。
    vscode中需要安装eslint插件(插件搜索安装即可),安装完成之后 首选项-> 设置 -> 扩展 -> eslint,我们直接点击 在settings.json 中编辑 ,打开编辑文件进行编辑,这是我的配置文件(备注中有追加说明):

    {
        "workbench.colorTheme": "One Dark Pro",
        "editor.codeActionsOnSave": {
            "source.fixAll.eslint": true    // 保存文件时,自动eslint修复格式
        },
        "eslint.enable": true,  // 开启eslint
        "eslint.options": {
            "extensions": [ // eslint检测的文件格式
                ".js",
                ".ts",
                ".tsx",
            ],
        },
        "eslint.validate": [    // eslint检测的语法
            "javascript",
            "javascriptreact",
            "typescript",
            "typescriptreact",
        ]
    }
    

    这里需要说明的是,eslint插件2.0.4开始,保存时自动修复文件是通过 editor.codeActionsOnSave 进行配置了,以前是 "eslint.autoFixOnSave": true 。
    描述里还说只要在eslint中正确配置了ts,就不需要在eslint.validate中配置了,不过我都不管了,还是统统加上了。

    源码调整

    这部分就略繁琐了,需要把原代码全部修改成ts,让我们一步步来

    1.修改文件后缀

    把原来的js文件全部改成ts,我这里是直接写了段node代码来干这个事情:

    const fs = require('fs')
    const path = require('path')
    
    function renameFile (dir) {
      fs.access(dir, function (err) {
        if (err) {
          console.log('目录不存在')
        }
        _rename(dir)
      })
    
      function _rename (dir) {
        fs.readdir(dir, function (err, paths) {
          if (err) {
            console.log(err)
          } else {
            paths.forEach(function (curPath) {
              console.log(`查找到${curPath}`)
              const _src = dir + '/' + curPath
              const _dist = dir + '/' + curPath.replace('.js', '.ts')
              fs.stat(_src, function (err, stat) {
                if (err) {
                  console.log(err)
                } else {
                  // 判断是文件还是目录
                  if (stat.isFile()) {
                    if (curPath.endsWith('.js')) {
                      fs.rename(_src, _dist, function (err) {
                        if (err) {
                          console.log(err)
                        } else {
                          console.log(`${_src} ==> ${_dist}`)
                        }
                      })
                    }
                  } else if (stat.isDirectory()) {
                    // 当是目录是,递归复制
                    _rename(_src)
                  }
                }
              })
            })
          }
        })
      }
    }
    
    renameFile(path.join(__dirname, 'src'))
    

    跑完之后把src目录下面的js文件全部改成ts文件

    2.修改代码

    主要是原来的require全部改成es的import,这里我是写了一个正则,搜索替换的:

    const ([^ ]+) = require(([^)]+))  // from
    import $1 from $2   // to
    

    你如果对自己代码有自信,也可以在上一步的node脚本中,读取文件内容,然后替换掉。

    关于import这一块就比较有意思了,我们来看一段对比:

    // 编译前:
    import path from 'path'
    import fsTool from '@/util/fs-tool'
    
    // 编译后:
    "use strict";
    var __importDefault = (this && this.__importDefault) || function (mod) {
        return (mod && mod.__esModule) ? mod : { "default": mod };
    };
    Object.defineProperty(exports, "__esModule", { value: true });
    const path_1 = __importDefault(require("path"));
    const fs_tool_1 = __importDefault(require("@/util/fs-tool"));
    

    显然,ts追加了一个 __importDefault函数,这就是ts的esModuleInterop编译选项的作用,对es和commonjs的模块做兼容处理:

    • ts编译的时候,给当前模块插入一个属性: __esModule: true
    • 导入的时候先判断该模块是否有__esModule
    • 如果为true,判定为es模块,直接导出
    • 如果为false,判定为commonjs模块,作为default导出:{ default: mod }

      参考文章里认为这里会有一个陷阱,所以需要打开allowSyntheticDefaultImports编译选项。的确,如果一个模块是用ts写的,或者自己就是追加了__esModule属性,但同时exports又没有default,最终 __importDefault导出来的会是一个空值。
      但我认为自己追加__esModule属性不太可能,用ts写的没有export default但又import default时,ts是会报错的,所以我认为这种情况可以忽略。

    调试

    1.chrome中调试

    在package.json的scripts中追加以下命令:

    "dev": "nodemon --watch src -e ts,tsx,js,json --exec node --inspect -r ts-node/register ./src/app.ts",
    

    node调试的命令是 --inspect选项,nodemon用来监测到文件改动时自动重启,ts-node用来调试ts代码

    以上,就能直接在chrome的开发者工具中调试了,如果有不清楚的可以自己去搜索node --inspect,这里就不做贴图说明了

    vscode中调试

    .vscode/launch.json填入以下内容:

    {
        "version": "0.2.0",
        "configurations": [
            {
                "type": "node",
                "request": "attach",
                "name": "Node: Nodemon",
                "processId": "${command:PickProcess}",
                "restart": true,
                "protocol": "inspector",
            }
        ]
    }
    

    打开debug面板,选择刚才设置的Node: Nodemon,点击调试并运行,即可开始在vscode中调试(需要先npm run dev把项目运行起来):

    说明

    在查阅过的资料中,京东社交电商部的这篇文章帮助最大:Node.js项目TypeScript改造指南,非常感谢!
    本文也是在此基础上有部分更新以及补充。

  • 相关阅读:
    IT公司笔试题(一)
    select与poll函数介绍
    ps命令介绍
    strace命令介绍(转)
    STREAMS流机制
    记录锁
    spingboot启动报驱动Loading class `com.mysql.jdbc.Driver'. This is deprecated. The new driver class is `com.mysql.cj.jdbc.Driver'. The driver is automatically registered via the SPI and manual loading of th
    list通过lambda 表达式去重,筛选
    Idea查看一个类和子类(实现类)的结构图
    Redis主从配置
  • 原文地址:https://www.cnblogs.com/thyong/p/12182579.html
Copyright © 2020-2023  润新知