我的前端插件库辅助工具总结

268 阅读3分钟

本文用于记录我在开发中,项目用到的一些辅助工具的总结,比较全的配置参考了element-plus项目

代码检查

  • typescript
  • eslint
@typescript-eslint/eslint-plugin // 兼容typescript
@typescript-eslint/parser
eslint-define-config // 为eslint配置添加提示
eslint-plugin-jest // 处理jest
eslint-config-prettier // 兼容prettier
eslint-plugin-react // 处理react jsx
eslint-plugin-react-hooks // 处理hooks
eslint-plugin-vue // 处理.vue文件

以下是一些简单的配置

const { defineConfig } = require('eslint-define-config')

module.exports = defineConfig({
  'extends': [
    'eslint:recommended',
    'plugin:@typescript-eslint/eslint-recommended',
    'plugin:@typescript-eslint/recommended',
    'plugin:jest/recommended',
    'prettier'
  ],
  'parserOptions': {
    'parser': '@typescript-eslint/parser',
    'ecmaVersion': 2021,
    'sourceType': 'module'
  },
  'plugins': ['@typescript-eslint', 'jest'],
  'rules': {},
  'overrides': [
    {
      'files': ['packages/web/src/**'],
      'extends': ['plugin:react/recommended'],
      'rules': {
        'react/react-in-jsx-scope': 'off',
        'no-useless-escape': 'off'
      }
    },
    {
      'files': ['*.vue'],
      'extends': ['plugin:vue/vue3-recommended'],
      'parser': 'vue-eslint-parser',
      'env': {
        'vue/setup-compiler-macros': true
      },
      'rules': {
        'vue/script-setup-uses-vars': 'error'
      }
    },
    {
      'files': ['*.js'],
      'rules': {
        'no-undef': 'off',
        '@typescript-eslint/no-var-requires': 'off'
      }
    }
  ]
})
  • jest
    • ts-jest // 允许jest中使用ts
    • @vue/vue3-jest // vue3
"@testing-library/react": "^12.1.2",
"@testing-library/jest-dom": "^5.16.1",
"@testing-library/react-hooks": "^7.0.2",

jest的简单配置

import type { Config } from '@jest/types'
const config: Config.InitialOptions = {
  preset: 'ts-jest/presets/js-with-ts',
  clearMocks: true,
  testEnvironment: 'jsdom',
  modulePathIgnorePatterns: ['<rootDir>/package.json'],
  moduleNameMapper: {
    '@/(.*)$': '<rootDir>/src/$1'
  },
  rootDir: '.',
  resetMocks: false,
  globals: {
    'ts-jest': {
      tsconfig: 'tsconfig.json'
    }
  }
}
export default config
  • prettier用于代码样式优化
    • pretty-quick辅助工具
  • stylelint样式格式管理工具,官网

pnpm

包管理工具

提交时代码检查

  • lint-staged只处理本次修改的部分,与git挂钩
  • @commitlint/config-conventional、@commitlint/cligit commit的message提交规范
  • husky 在script中添加"prepare": "husky install",用于npm install时自动创建husky.sh文件

使用类似npx husky add .husky/pre-commit "npx lint-staged"创建提交的钩子

package.json中

"lint-staged": {
  "*.{js,jsx,less,md,json}": "prettier --write",
  "*.ts?(x)": [
    "prettier --parser=typescript --write"
  ],
  "packages/*/src/*": "eslint --ext .js,.ts,.tsx"
},

使用npx husky add .husky/commit-msg "npx --no-install commitlint --edit $1"创建提交说明规范

日志生成工具

  • conventional-changelog-cli目前2.0版本只能针对git中tag版本输出日志,且同一个tag同一天中并非按时间顺序输出。更多参考 在scripts中写入
"changelog": "conventional-changelog -p angular -i CHANGELOG.md -s -r 0  -n ./changelog-option.js"
/*配置项说明:
-p angular 指定风格,也可以用custom-config
-i CHANGELOG.md 指定输出的文件名称
-s -r 0 指定增量更新,不会覆盖以前的更新日志
-n ./changelog-option.js 指定自定义配置
*/
// changelog-option.js中
const compareFunc = require('compare-func')

const message = {
    'feat': '✨ Features | 新功能',
    'fix': '🐛 Bug Fixes | Bug 修复',
    'perf': '⚡ Performance Improvements | 性能优化',
    'revert': '⏪ Reverts | 回退',
    'docs': '📝 Documentation | 文档',
    'style': '💄 Styles | 风格',
    'refactor': '♻ Code Refactoring | 代码重构',
    'test': '✅ Tests | 测试',
    'build': '👷‍ Build System | 构建',
    'ci': '🔧 Continuous Integration | CI 配置',
    'chore': '🎫 Chores | 其他更新'
}

module.exports = {
    writerOpts: {
        transform: (commit, context) => {
            let discard = true
            const issues = []
            commit.notes.forEach(note => {
                note.title = 'BREAKING CHANGES'
                discard = false
            })
            if (
                !['feat', 'fix', 'perf', 'revert'].includes(commit.type) &&
                discard
            ) {
                return // 此处过滤不是按照commitlint规则输出的日志
            } else {
                // custom-config模式只会输出header
                // commit.header = commit.header.replace(
                //   commit.type,
                //   message[commit.type]
                // )
                commit.type = message[commit.type]
            }

            if (commit.scope === '*') {
                commit.scope = ''
            }
            if (typeof commit.hash === 'string') {
                commit.hash = commit.hash.substring(0, 7)
            }
            if (typeof commit.subject === 'string') {
                let url = context.repository
                    ? `${context.host}/${context.owner}/${context.repository}`
                    : context.repoUrl
                if (url) {
                    url = `${url}/issues/`
                    // Issue URLs.
                    commit.subject = commit.subject.replace(
                        /#([0-9]+)/g,
                        (_, issue) => {
                            issues.push(issue)
                            return `[#${issue}](${url}${issue})`
                        }
                    )
                }
                if (context.host) {
                    // User URLs.
                    commit.subject = commit.subject.replace(
                        /\B@([a-z0-9](?:-?[a-z0-9/]){0,38})/g,
                        (_, username) => {
                            if (username.includes('/')) {
                                return `@${username}`
                            }

                            return `[@${username}](${context.host}/${username})`
                        }
                    )
                }
            }

            // remove references that already appear in the subject
            commit.references = commit.references.filter(
                reference => issues.indexOf(reference.issue) === -1
            )
            return commit
        },
        groupBy: 'type',
        commitGroupsSort: 'title',
        commitsSort: ['scope', 'subject'],
        noteGroupsSort: 'title',
        notesSort: compareFunc
    }
}

构建工具

  • babel
    • babel-loader
    • @babel/core
    • @babel/preset-env
    • @babel/preset-typescript 一般用上这几个即可,另外其它框架使用其对于的,比如react@babel/preset-react
// .babelrc
{
  "presets": [
    "@babel/preset-env",
    [
      "@babel/preset-react",
      {
        "runtime": "automatic" // react17jsx不需要引入React
      }
    ],
    "@babel/preset-typescript"
  ]
}
  • vite

    • @vitejs/plugin-react
    • @vitejs/plugin-vue
    • vite-plugin-style-import antd/element/vant的按需加载工具
        import { AntdResolve, createStyleImportPlugin } from 'vite-plugin-style-import'
        
        plugins: [
            createStyleImportPlugin({
              resolves: [AntdResolve()]
            })
        ]
    
  • webpack

    • webpack-cli提供配置文件
    • webpack-dev-server本地服务
    • webpack-bundle-analyzer 构建后文件大小视图
    • html-webpack-plugin html解析
    • 各种loader
{
    test: /.(ts|tsx)$/,
    exclude: /node_modules/,
    use: ['babel-loader'] // babel处理,如果有.babelrc文件则会自动启用
},
{
    test: /.less$/,
    exclude: /node_modules/,
    use: [
        'style-loader', // 在js中引用css
        {
            loader: 'css-loader', // 处理css中的import/url图片等
            options: {
                modules: true // css模块化
            }
        },
        // 可以添加postcss-loader做兼容处理
        'less-loader' // 可以处理less
    ]
},
{
    test: /.(png|jpg)/,
    exclude: /node_modules/,
    use: [
        {
            loader: 'url-loader',// 配合file-loader处理js中的图片文件资源
            options: {
                limit: 16000, // 大于此则使用file-loader处理成静态资源,小于则base64处理
                name: 'img/[name].[hash:8].[ext]'
            }
        }
    ]
}

其它

  • ts-node node环境允许ts执行
  • rimraf scripts删除文件
  • nodemon node环境热更新
  • concurrently 按顺序执行scripts命令
  • cross-env scripts修改环境变量
  • 根目录下.npmrc文件用来设置当前项目的npm配置。例如文件内配置registry=http://registry.npm.taobao.org/则npm i时会启用该仓库镜像地址。如果使用pnpm配置shamefully-hoist=true时会将依赖包引用平铺至node_modules目录下,参考
  • 根目录下.nvmrc文件(需要全局安装了nvm),在进入该目录下执行nvm use则会使用当前配置的node版本