zoukankan      html  css  js  c++  java
  • 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改造指南,非常感谢!
    本文也是在此基础上有部分更新以及补充。

  • 相关阅读:
    关于在MAC上进行 LARAVEL 环境 Homestead 安装过程记录
    js 贷款计算器
    js 实现阶乘
    js 两点间距离函数
    composer Your requirements could not be resolved to an installable set of packages
    vue 项目优化记录 持续更新...
    vue 项目打包
    vue 真机调试页面出现空白
    vue 真机调试
    谈谈-Android状态栏的编辑
  • 原文地址:https://www.cnblogs.com/thyong/p/12182579.html
Copyright © 2011-2022 走看看