[Frontend筆記] React專案中的規範設置

專案配置:React+Typescript+Webpack5+Eslint+Prettier

Posted by 李定宇 on Wednesday, September 14, 2022

React+Typescript+Webpack5+Eslint+Prettier專案設置

初始化專案

透過 npx create-react-app eslint_demo --template typescript創建專案

安裝相關的eslintprettier依賴:npm i -D eslint eslint-plugin-prettier eslint-config-prettier prettier

接著,可以直接寫.vscode的設置,讓IDE自動format:

// in the .vscode/settings.json
{
   "editor.codeActionsOnSave": {
      "source.fixAll.eslint": true
   },
   "eslint.alwaysShowStatus": true,
   "editor.defaultFormatter": "esbenp.prettier-vscode",
   "[javascript]": { "editor.defaultFormatter": "esbenp.prettier-vscode" },
   "editor.formatOnSave": true
}

webpack設置

下載webpack dependency

npm i -D webpack webpack-cli webpack-dev-server

下載相關babel

這裏我們需要babel和相關的dependency來確保webpack能夠讀.tsx檔案

npm i -D babel-loader @babel/core @babel/preset-env @babel/plugin-transform-runtime @babel/preset-react 

各個依賴的作用如下:

依賴 作用
babel-loader 轉譯器,將.jsx轉換為.js,但主要作用只是識別出.jsx
@babel/core babel-loader識別出.jsx,然後透過此dependancy來轉譯
@babel/preset-env babel轉譯中的一些預設設定,將一些如es6 const/let語法轉譯成低級並可兼容的語法
@babel/plugin-transform-runtime 轉化高版本內置模塊,可以看作對@babel/preset-env的補充
@babel/preset-react jsx標籤轉換為React.createElement
@babel/preset-typescrpt 讓babel支持Typescript語法

配置babel,根目錄新增.babelrc

babel官方推薦,在根目錄下新增.babelrc來配置babel,配合上述的dependency,在.babelrc會像這樣:

{
    "presets": ["@babel/env", "@babel/preset-react", "@babel/preset-typescript"],
    "plugins": [
        [
            "@babel/plugin-transform-runtime",
            {
                "regenerator": true
            }
        ]
    ]
}

在webpack中添加loader

// webpack.base.js
const path = require('path');

module.exports = {
  // 入口文件
  entry: {
    main: path.resolve(__dirname, '../src/packages/home/index.tsx'),
  },
  module: {
    rules: [
      {
        // 同時識別 jsx js tsx ts 文件
        test: /\.(t|j)sx?$/,
        use: 'babel-loader',
      },
    ],
  },
};

這裏的設置是將.js.ts.jsx.tsx都丟到 babel-loader做處理。

設置eslint

下載相關dependency

下載react/typescript/eslint相關依賴。首先,安裝 eslint airbnb的規則,其會自動下載相關的eslint dependency

npx install-peerdeps --dev eslint-config-airbnb

下載 eslint+prettier的dependency:

npm i -D prettier eslint-config-prettier eslint-plugin-prettie

下載react test 相關的dependency:

npm i -D eslint-plugin-testing-library eslint-plugin-jest-dom 

其中除了基本eslint+prettier的dependency,還有react、react-test、jest-dom等等的eslint dependency,可以讓react工程師很方便的寫代碼和寫測試。

設置.eslintrc.js

在根目錄上新增.eslintrc.js,現在extends中先宣告要使用的eslint規則:

extends: [
    'airbnb',
    'plugin:jsx-a11y/recommended',
    'plugin:testing-library/react',
    'plugin:jest-dom/recommended',
    'eslint:recommended',
    'plugin:react/recommended',
    'plugin:@typescript-eslint/recommended',
    'prettier',
],

然後修改 parserparserOptions,讓eslint可以讀取.ts.tsx

parser: '@typescript-eslint/parser',
parserOptions: {
    ecmaVersion: 'latest',
    sourceType: 'module',
    project: './tsconfig.json',
},

不過此時會看到,.eslintrc.js本身會報一個錯: Parsing error: "parserOptions.project" has been set for @typescript-eslint/parser. ,而且如果專案裡還有設置webpack(like: webpack.config.is),也會有相似的報錯。原因就是我們使用@typescript-eslint/parser對所有檔案做解析,產生的報錯;最簡單的解決辦法,就是讓eslint直接略過相關的檔案:

  • 在根目錄上新增.eslintrcIgnore
  • 在檔案內寫入想要忽略類型檢查的檔案

    .eslintrc.js 
    webpack.common.js
    webpack.local.js
    

這樣一來基本的設置就完成了,接下來的一些Rules可以基於團隊的共識來撰寫。

完整.eslintrc.js:

module.exports = {
    env: {
        browser: true,
        es2021: true,
        jest: true,
        node: true,
    },
    extends: [
        'airbnb',
        'plugin:jsx-a11y/recommended',
        'plugin:testing-library/react',
        'plugin:jest-dom/recommended',
        'eslint:recommended',
        'plugin:react/recommended',
        'plugin:@typescript-eslint/recommended',
        'prettier',
    ],
    parser: '@typescript-eslint/parser',
    parserOptions: {
        ecmaVersion: 'latest',
        sourceType: 'module',
        project: './tsconfig.json',
    },
    plugins: [
        'react',
        '@typescript-eslint',
        'react-hooks',
        'jsx-a11y',
        'simple-import-sort',
        'jest-dom',
        'testing-library',
        'prettier',
    ],
    rules: {
        'import/no-extraneous-dependencies': [2, { devDependencies: true }],
        'testing-library/await-async-query': 'error',
        'testing-library/no-await-sync-query': 'error',
        'testing-library/no-debugging-utils': 'warn',
        'jest-dom/prefer-checked': 'error',
        'jest-dom/prefer-enabled-disabled': 'error',
        'jest-dom/prefer-required': 'error',
        'jest-dom/prefer-to-have-attribute': 'error',
        'react/prop-types': ['off'],
        'react/jsx-filename-extension': [1, { extensions: ['.tsx', '.jsx'] }],
        'import/extensions': [
            'error',
            'never',
            {
                scss: 'always',
            },
        ],
        'import/prefer-default-export': 0,
        'import/no-anonymous-default-export': 0,
        'import/no-unresolved': 0,
        'simple-import-sort/imports': 'error',
        'simple-import-sort/exports': 'error',
        'sort-imports': 'off',
        'import/order': 'off',
        'no-use-before-define': 'off',
        'no-shadow': 'off',
        '@typescript-eslint/no-shadow': 'error',
        '@typescript-eslint/ban-ts-comment': 0,
        '@typescript-eslint/no-explicit-any': 'error',
        '@typescript-eslint/prefer-optional-chain': 'warn',
        '@typescript-eslint/no-inferrable-types': 'warn',
        '@typescript-eslint/prefer-nullish-coalescing': 'warn',
        'react-hooks/rules-of-hooks': 'error',
        'react-hooks/exhaustive-deps': 'warn',
        'react/no-array-index-key': 'off',
        'react/react-in-jsx-scope': 'off',
        'prettier/prettier': [
            'error',
            {
                endOfLine: 'auto',
            },
        ],
        'react/jsx-one-expression-per-line': 'off',
        'react/jsx-curly-newline': 'off',
        'no-param-reassign': [
            'error',
            {
                props: true,
                ignorePropertyModificationsFor: ['state'],
            },
        ],
    },
    settings: {
        'import/resolver': {
            node: {
                extensions: ['.js', '.jsx', '.ts', '.tsx'],
                paths: ['src'],
            },
        },
    },
}

ChangeLog

  • 20220911-完成初稿