美文网首页
AntDesignPro中台项目迁移Typescript,并接入

AntDesignPro中台项目迁移Typescript,并接入

作者: 前端小钱 | 来源:发表于2020-05-08 15:05 被阅读0次

    前言

    在一家小公司实习了一段时间,接手的项目代码格式及规范化不可描述,为了保证项目的可维护性,决定接入Typescript,同时采用EslintPrettier进行代码规范化,为下一步的CodeReview

    Typescript是强类型语言,接入Typescript一开始会有很大的痛点,但是过了一阵子就可以享受到Typescript带来的好处,bug减少了,代码易读了,也可维护了,好处网上一大把就不累赘了。

    常用的代码格式化工具主要有ESlintTSLintStandardJS。TS官方已经决定弃用TSLint,全面拥抱ESLint。因此在技术选型方面将采用ESlint

    Eslint的主要功能包含代码格式的校验,代码质量的校验,JS规范,如用===而不是==判断相等、用驼峰命名变量而不是用下划线。而 Prettier 是美丽的意思,只是代码格式的校验(并格式化代码),不会对代码质量进行校验,如单行代码长度、tab长度、空格、逗号表达式等问题。

    一、安装相关依赖

    1.1 安装 Typescript

    推荐使用全局安装,可以在其他项目中也使用TS。

    npm install -g typescript
    

    1.2 安装声明文件

    所需的 react, react-dom 的声明文件, 以及 加载TS的ts-loader

    npm install --save-dev @types/react @types/react-dom ts-loader
    

    1.3 配置 tsconfig.json

    在使用Typescript时需要根据实际项目的需要进行相关规则的配置,具体配置根据项目而异、可参考官网,具体看这里TS官网。我的配置项如下所示:

    {
      "compilerOptions": {
        "allowSyntheticDefaultImports": true,
        "noUnusedParameters": true,
        "outDir": "build/dist",
        "baseUrl": ".",
        "strict": true,
        "noImplicitAny": true,
        "removeComments": true,
        "preserveConstEnums": true,
        "sourceMap": true,
        "forceConsistentCasingInFileNames": true,
        "strictPropertyInitialization": true,
        "experimentalDecorators": true,
        "noImplicitReturns": true,
        "moduleResolution": "node",
        "strictNullChecks": true,
        "esModuleInterop": true,
        "noUnusedLocals": true,
        "importHelpers": true,
        "noImplicitThis": false,
        "suppressImplicitAnyIndexErrors": false,
        "skipLibCheck": true,
        "noResolve": false,
        "module": "es2015",
        "allowJs": true,
        "target": "es5",
        "jsx": "react",
        "lib": [
          "es5",
          "es2015",
          "dom",
          "es7",
          "es2018"
        ],
        "paths": {
          "@/*": [
            "./src/*"
          ]
        }
      },
      "exclude": [
        "node_modules",
        "build",
        "scripts",
        "acceptance-tests",
        "webpack",
        "jest",
        "src/setupTests.ts",
        "tslint:latest",
        "tslint-config-prettier"
      ]
    }
    

    二、ESLint+Prettier代码规范

    针对JS项目迁移到TS的项目,主要有两种选择ESLintTSLintTSLint仅针对TS代码,因此如果采用TSLint规范TS代码,JS代码需要采用其他工具。而ESLint不仅能规范js代码,通过配置解析器,也能规范TS代码。此外由于性能问题,TypeScript 官方决定全面采用ESLint。因此本项目采用ESLint配合Prettier规范化TSJS代码。

    2.1 ESLint规范TS代码

    2.1.1 安装ESLint依赖

    npm i eslint @typescript-eslint/parser @typescript-eslint/eslint-plugin --save-dev
    
    • eslint: ESLint的核心代码

    • @typescript-eslint/parser:ESLint的解析器,用于解析Typescript文件,从而检查和规范Typescript代码

    • @typescript-eslint/eslint-plugin:这是一个ESLint插件,包含了各类定义好的检测Typescript代码的规范

    安装好依赖后,需要在项目根目录中的.eslintrc.js中配置,包括解析器、继承的代码规范、插件和环境:

    module.exports = {
       parser:  '@typescript-eslint/parser', //定义ESLint的解析器
       extends: ['plugin:@typescript-eslint/recommended'],//定义文件继承的子规范
       plugins: ['@typescript-eslint'],//定义了该eslint文件所依赖的插件
       env:{                          //指定代码的运行环境
           browser: true,
           node: true,
       }                                
    }
    

    2.2 规范React代码

    项目是以依赖于React的AntDesignPro为基础,因此需要安装规范React代码的Eslint插件。

    2.2.1安装插件

    npm i eslint-plugin-react --save-dev
    

    2.2.2 配置React规范

    然后修改.eslintrc.js的配置,如下所示:

    module.exports = {
       parser:  '@typescript-eslint/parser',
       extends: [
        'plugin:react/recommended',
        'plugin:@typescript-eslint/recommended'
       ],//使用推荐的React代码检测规范
       plugins: ['@typescript-eslint'],
       env:{                          
           browser: true,
           node: true,
       },
       settings: {//自动发现React的版本,从而进行规范react代码
           "react": {
               "pragma": "React",
               "version": "detect"
           }
       },  
       parserOptions: {//指定ESLint可以解析JSX语法
           "ecmaVersion": 2019,
           "sourceType": 'module',
           "ecmaFeatures":{
               jsx:true
           }
       }
       rules: {
       //在Rules中可以自定义你的React代码编码规范。
       }
    }
    

    2.3接入Prettier

    2.3.1 安装依赖

    npm install prettier eslint-config-prettier eslint-plugin-prettier -g
    
    • prettier:prettier插件的核心代码

    • eslint-config-prettier:解决ESLint中的样式规范和prettier中样式规范的冲突,以prettier的样式规范为准,使ESLint中的样式规范自动失效

    • eslint-plugin-prettier:将prettier作为ESLint规范来使用

    2.3.2 配置规则

    在项目的根目录下创建.prettierrc.js文件并配置prettier代码检查规则

    module.exports = {
        //最大长度80个字符
        printWidth: 80,
        //行末分号
        semi: false,
        //单引号
        singleQuote: true,
        //尽可能使用尾随逗号(包括函数参数)
        trailingComma: 'all',
        //在对象文字中打印括号之间的空格。
        bracketSpacing: true,
        // > 标签放在最后一行的末尾,而不是单独放在下一行
        jsxBracketSameLine: false,
        //箭头圆括号
        arrowParens: 'avoid',
        //在文件顶部插入一个特殊的 @format 标记,指定文件格式需要被格式化。
        insertPragma: false,
        //缩进
        tabWidth: 4,
        //使用tab还是空格
        useTabs: false,
    }
    

    2.3.3 结合Prettier配置Eslint

    修改.eslintrc.js文件,引入prettier
    最终配置为:

    module.exports = {
        parser: '@typescript-eslint/parser',
        extends: [
            'plugin:react/recommended',
            'plugin:@typescript-eslint/recommended',
            'prettier/@typescript-eslint',
            'plugin:prettier/recommended',
        ],
        plugins: ['@typescript-eslint', 'react'],
        env: {
            browser: true,
            node: true,
            es6: true,
        },
        rules: {
            quotes: ['error', 'single'], //强制使用单引号
            semi: ['error', 'never'], // 要求或禁止使用分号而不是 ASI
            camelcase: 0, // 双峰驼命名格式
            eqeqeq: 2, //必须使用全等
            yoda: [2, 'never'], //禁止尤达条件
            strict: [2, 'never'], // 禁用严格模式,禁止在任何地方出现 'use strict'
            'no-extra-boolean-cast': 2, //禁止不必要的bool转换
            'no-lone-blocks': 2, //禁止不必要的嵌套块
            'no-plusplus': 0, //禁止使用++,--
            'no-proto': 2, //禁止使用__proto__属性
            'no-self-compare': 2, //不能比较自身
            'no-undef': 2, //不能有未定义的变量
            'no-unreachable': 2, //不能有无法执行的代码
            'no-unused-expressions': 2, //禁止无用的表达式
            'no-debugger': process.env.NODE_ENV === 'production' ? 2 : 0,
            'no-alert': 2, //禁止使用alert
            'no-caller': 1, //禁止使用arguments.caller或arguments.callee
            'no-inline-comments': 2, //禁止行内备注
            'no-func-assign': 2, //禁止重复的函数声明
            'no-eval': 2, //禁止使用eval,
            'no-empty': 2, //块语句中的内容不能为空
            'no-const-assign': 2, //禁止修改const声明的变量
            'no-var': 2, //禁止使用var
            'no-multiple-empty-lines': [1, { max: 2 }], //空行最多不能超过2行
            'no-extra-semi': 'error', // 禁止不必要的分号
            'array-bracket-spacing': [2, 'never'], //是否允许非空数组里面有多余的空格
            'linebreak-style': ['error', 'unix'], // 强制使用一致的换行风格
            'brace-style': [2, '1tbs', { allowSingleLine: true }], // if while function 后面的{必须与if在同一行,java风格。
            'comma-dangle': 0, // 数组和对象键值对最后一个逗号, never参数:不能带末尾的逗号, always参数:必须带末尾的逗号,
            'comma-spacing': [2, { before: false, after: true }], // 控制逗号前后的空格
            'computed-property-spacing': [2, 'never'], // 以方括号取对象属性时,[ 后面和 ] 前面是否需要空格, 可选参数 never, always
            'use-isnan': 2, //禁止比较时使用NaN,只能用isNaN()
            'default-case': 2, //switch语句最后必须有default
            'newline-after-var': 2, //变量声明后是否需要空一行
            'max-depth': [2, 4], //嵌套块深度最多四层
            'max-params': [2, 4], //函数最多只能有4个参数
            'no-else-return': 2, //如果if语句里面有return,后面不能跟else语句,禁止出现 if (cond) { return a } else { return b },应该写为 if (cond) { return a } return b
            'no-eq-null': 2, //禁止对null使用==或!=运算符
            'no-iterator': 2, //禁止使用__iterator__ 属性
            'no-mixed-spaces-and-tabs': [2, false], //禁止混用tab和空格
            'no-new-func': 1, //禁止使用new Function
            'no-new-object': 2, //禁止使用new Object()
            'no-self-compare': 2, //不能比较自身
            'no-unused-vars': [2, { vars: 'all', args: 'after-used' }], //不能有声明后未被使用的变量或参数
            'no-use-before-define': 0, //未定义前不能使用
            'valid-typeof': 2, //无效的类型判断
            'wrap-iife': [2, 'inside'], //立即执行函数表达式的小括号风格
            // 注释的斜线和星号后要加空格
            'spaced-comment': [
                2,
                'always',
                {
                    block: {
                        exceptions: ['*'],
                        balanced: true,
                    },
                },
            ],
            // new, delete, typeof, void, yield 等表达式前后必须有空格,-, +, --, ++, !, !! 等表达式前后不许有空格
            'space-unary-ops': [
                2,
                {
                    words: true,
                    nonwords: false,
                },
            ],
            'prefer-rest-params': 2, // 必须使用解构 ...args 来代替 arguments
            'consistent-this': [2, 'self', 'that'], // this 的别名规则,只允许 self 或 that
            curly: [2, 'multi-line', 'consistent'], // if 后必须包含 { ,单行 if 除外
            'for-direction': 2, // for 循环不得因方向错误造成死循环
            'getter-return': [2, { allowImplicit: true }], // getter 必须有返回值,允许返回 undefined
            'keyword-spacing': 2, // 关键字前后必须有空格
            // new关键字后类名应首字母大写
            'new-cap': [
                2,
                {
                    capIsNew: false, // 允许大写开头的函数直接执行
                },
            ],
            'no-await-in-loop': 2, // 禁止将 await 写在循环里
            'no-class-assign': 2, // class定义的类名不得与其它变量重名
            'no-dupe-args': 2, // 函数参数禁止重名
            'no-duplicate-case': 2, // 禁止 switch 中出现相同的 case
            'no-duplicate-imports': 2, // 禁止重复 import
            'no-empty-function': 0, // 禁止空的 function,包含注释的情况下允许
            'no-empty-pattern': 2, // 禁止解构中出现空 {} 或 []
            'no-ex-assign': 2, // catch 定义的参数禁止赋值
            'no-extend-native': [2, { exceptions: ['Array', 'Object'] }], // 禁止扩展原生对象
            'no-extra-parens': [2, 'functions'], // 禁止额外的括号,仅针对函数体
            'no-floating-decimal': 2, // 不允许使用 2. 或 .5 来表示数字,需要用 2、2.0、0.5 的格式
            'no-func-assign': 2, // 禁止对函数声明重新赋值
            'no-implied-eval': 2, // 禁止在 setTimeout 和 setInterval 中传入字符串,因会触发隐式 eval
            'no-multi-assign': 2, // 禁止连等赋值
            '@typescript-eslint/explicit-function-return-type': [
                'off',
                {
                    allowExpressions: true,
                    allowTypedFunctionExpressions: true,
                },
            ],
            '@typescript-eslint/no-explicit-any': 0, // 特殊情况可将类型显示设置为any
            '@typescript-eslint/interface-name-prefix': 0, // 允许接口命名以I开头
            '@typescript-eslint/no-var-requires': 0, // antd中引用style需要用require
            '@typescript-eslint/no-use-before-define': 0, // mapStateToProps在之前就用到(typeof推断类型)
            '@typescript-eslint/camelcase': 0, // 驼峰命名格式
            '@typescript-eslint/no-empty-function': 0, // 给函数默认值可以为空
            'react/display-name': 0, // 一个莫名其妙的Bug
            'react/no-find-dom-node': 0,
            '@typescript-eslint/no-non-null-assertion': 0, // 允许用!断言不为空
        },
        settings: {
            //自动发现React的版本,从而进行规范react代码
            react: {
                pragma: 'React',
                version: 'detect',
            },
        },
        parserOptions: {
            //指定ESLint可以解析JSX语法
            ecmaVersion: 2019,
            sourceType: 'module',
            ecmaFeatures: {
                jsx: true,
            },
        },
    }
    
    • prettier/@typescript-eslint:使得@typescript-eslint中的样式规范失效,遵循prettier中的样式规范。

    • plugin:prettier/recommended:使用prettier中的样式规范,且如果使得ESLint会检测prettier的格式问题,同样将格式问题以error的形式抛出。

    2.4 在VSCode中集成ESLint配置

    当在项目中有了如上配置,其他开发人员需要在自己的VSCode中进行ESLint和Prettier插件的安装配置。VScode的ESLint和Prettier会读取项目的配置文件,从而达到对代码的检查。踩坑如下:

    • 需要注意的是如果是通过工作机进行远程工作的,一定要记得远程的VScode安装插件才生效,本地安装并没用。
    • 同时在团队协作过程中,插件的版本有可能不同,如稳定版本和非稳定版本对于eslint规则的解析不同,因此团队直接尽可能安装相同版本的插件.

    2.4.1 配置setting,保存自动校验

    prettier和eslint可以在保存时自动检查并自动格式化一部分问题,在settings.json文件中修改其配置文件如下:

    {
        "eslint.enable": true, //是否开启vscode的eslint
        "eslint.options": { //指定vscode的eslint所处理的文件的后缀
            "extensions": [
                ".js",
                ".vue",
                ".ts",
                ".tsx"
            ]
        },
        "editor.formatOnSave": true,
        "editor.codeActionsOnSave": {
            "source.fixAll.eslint": true
        },
        "eslint.validate": [ //确定校验准则
            "javascript",
            "javascriptreact",
            {
                "language": "html",
                "autoFix": true
            },
            {
                "language": "vue",
                "autoFix": true
            },
            {
                "language": "typescript",
                "autoFix": true
            },
            {
                "language": "typescriptreact",
                "autoFix": true
            }
        ]
    }
    

    2.5 husky规范工作流

    在项目迁移和规范化的过程中,我们不可能一次性将所有已经存在的代码迁移到TS,因此在实际过程中我们是采用JS和TS混合开发,在实际做业务需求过程中将改动的文件迁移成TS,对尚未碰见的代码不做改动,保证项目的正常运行。同样,对Eslint的格式化也是主要集中在新开发的页面。在开发的过程中,为了保证团队所有成员都能严格执行Eslint规范,采用husky构建工作流,eslint将检查做了修改,存在stage阶段尚未commit阶段的代码,在commit前进行校验,校验无误即通过,否则不通过。

    2.5.1 安装依赖

        npm install husky --save-dev
    

    2.5.2 配置

    在package.json的script中配置:

     "scripts": {
       "lint": "npm run lint:js && npm run lint:style && npm run lint:prettier",
        "lint-staged": "lint-staged",
        "lint-staged:js": "eslint --ext .js,.jsx,.ts,.tsx ",
        "lint:fix": "eslint --fix --cache --ext .js,.jsx,.ts,.tsx --format=pretty ./src && npm run lint:style",
        "lint:js": "eslint --cache --ext .js,.jsx,.ts,.tsx --format=pretty ./src",
        "lint:prettier": "check-prettier lint",
        "lint:style": "stylelint --fix \"src/**/*.less\" --syntax less",
    }
    

    接着需要在package.json的husky中配置如下:

     "husky": {
       "hooks": {
          "pre-commit": "npm run lint"
        }
    },
    

    在pre-commit这个hook也就是在提交之前进行lint的检测。

    上述我们通过在husky的pre-comit这个hook中执行一个npm命令来做lint校验。其实一般情况,我们会定义一个lint-staged,来在提交前检测代码的规范。使用lint-staged来规范代码的方式如下,我们修改package.json文件为:

    {
      "husky": {
        "hooks": {
          "pre-commit": "lint-staged"
        }
      },
      "lint-staged": {
        "**/*.{js,jsx,ts,tsx}": [
          "eslint --fix",
          "prettier --write",
          "git add ."
        ]
      }
    }
    

    注意:这里有个需要注意,如果发现文件eslint还报错,居然还能提交成功,也就是husky没有生效,那么可以cd进入到.git/hooks文件夹,查看一下有没有pre-commit文件(不是pre-commit.sample文件),如果没有,那么就是git版本的原因,需要升级到2.13.0以上。

    在本项目中采用cicd进行持续集成,因此也可以将eslint加入到ci中,在这里不在详细介绍。

    总结

    项目的Typescript迁移和Eslint+Prettier的代码格式化,目前已经上线几个月运行良好,至今项目已经迁移一半,基本无痛点,很爽。


    qrcode.png

    相关文章

      网友评论

          本文标题:AntDesignPro中台项目迁移Typescript,并接入

          本文链接:https://www.haomeiwen.com/subject/dogkectx.html