ESLint 导入语句的分组排序

解决的痛点

在我们的前端项目中必不可少的需要在当前模块中导入其他模块的功能,但是随着模块实现的功能越来越复杂导入的其他模块的功能越来越多会导致模块中导入语句的书写混乱,比如:

javascript 复制代码
// 模拟混乱的导入 - 各种类型混合在一起
import { useState, useEffect } from 'react';
import index from './index';
import './App.css';
import axios from 'axios';
import _ from 'lodash';
import { UserService } from '../services/UserService';
import React from 'react';
import { Component } from 'react';
import { debounce } from '@/utils/helpers';
import { Header, Footer } from './components';
import moment from 'moment';
import { createStore } from 'redux';
import config from '../../config';
import { someUtil } from './utils/someUtil';
import { Button, Input, Select } from 'antd';
import { useSelector, useDispatch } from 'react-redux';
import fs from 'fs';
import path from 'path';
import { fetchUserData } from '../api';
import '@/styles/global.css';
...

上面的模块有很多并且混乱的导入语句:第三方模块、当前目录模块、上级目录模块、项目内部模块(需配置别名)import { debounce } from '@/utils/helpers';、Node.js 内置模块;各种类型的导入语句混杂在一起开发体验非常差。我们可以使用 ESLint 来保证代码质量,严格模式的配置可以包括使用一些严格的规则集,以及根据项目需求自定义规则,这篇文章配置的就是导入语句分组、排序的严格模式配置。

配置方案

导入语句的分组、排序是由 eslint-plugin-import 插件提供的 import/order 规则控制的。安装 ESLint 和 eslint-plugin-import 插件依赖。

bash 复制代码
yarn add eslint eslint-plugin-import -D

配置 ESLint 文件(已忽略其他无关配置)

javascript 复制代码
module.exports = {
  // ...
  extends: [
    'eslint:recommended',
    // Import排序
    'plugin:import/errors',
    'plugin:import/warnings',
    'plugin:import/typescript',
  ],
  // ...
  rules: {
    // ...
    // 📦 Import/Export规则
    'import/order': [
        'error',
        {
          groups: [
            'builtin',   // Node.js内置模块
            'external',  // npm包
            'internal',  // 内部别名模块
            'parent',    // 父级目录导入
            'sibling',   // 同级目录导入  
            // ['parent', 'sibling'], 合并父级和同级目录的导入,可以自己尝试一下
            'index',     // 当前目录索引文件
            'object',    // 对象导入(较少使用)
          ],
          'newlines-between': 'always', // 组间空行分隔
          alphabetize: { 
            order: 'asc',              // 字母升序排序
            caseInsensitive: true,     // 不区分大小写
          },
        },
    ],
    // ...
  },
};

这个配置的含义是:

  • groups: 指定导入分组的顺序,按照这个数组的顺序分组,同一组内的导入会在一起,不同组之间会用空行分隔(因为设置了 'newlines-between': 'always')。
  • alphabetize: 对导入进行字母排序,order: 'asc' 表示升序,caseInsensitive: true 表示不区分大小写。

分组说明:

  • builtin: Node.js 内置模块(如 fs、path)
  • external: 来自 node_modules 的模块(通过 npm 安装的)
  • internal: 内部模块(别名路径或者内部模块,通常在 webpack 等中配置)
  • parent: 父级目录的模块(例如 ../xxx)
  • sibling: 同级目录的模块(例如 ./xxx)
  • index: 当前目录的索引文件(例如 ./index.js)
  • object: 对象导入(例如 import { something } from './xxx' 中的导入方式)

这样配置后,ESLint 会自动检查导入语句的顺序,并按照规定的分组和字母顺序进行排序,同时在不同分组之间插入空行。 例如,以下是一个排序后的例子:

javascript 复制代码
// 1. 内置模块  
import fs from 'fs';  
import path from 'path';

// 2. 外部模块  
import React from 'react';  
import lodash from 'lodash';

// 3. 内部模块(假设配置了别名@表示内部模块)  
import { myUtil } from '@/utils';

// 4. 父级目录模块  
import parentModule from '../parent';

// 5. 同级目录模块  
import siblingModule from './sibling';

// 6. 当前目录索引文件  
import index from './';

如果导入语句不符合这个顺序,ESLint 会报错,并且可以通过 eslint --fix 自动修复。 这个严格模式配置规则对于保持代码中导入语句的整洁和一致性非常有用。

可以看到不同类型的导入语句已经被分组并且每个分组间使用空行分隔。

对于同一分组中的导入语句需要按导入的模块名**整个导入语句**进行排序、合并。比如

javascript 复制代码
import React from 'react';
import { Component } from 'react';
import { useState, useEffect } from 'react';

console.log(React, Component, useState, useEffect);

React、Component 和 useState, useEffect,保存后 ESLint 分组排序合并后会变成

javascript 复制代码
import React, { Component, useEffect, useState } from 'react';

console.log(React, Component, useState, useEffect);

规则比较的是整个导入语句的字符串排序(包括导入的变量)。React{ Component } 按ASCII码比较,R{之前,所以 import React from 'react'; 排在 import { Component } from 'react';之前,即默认导入会永远排在命名导入之前

进行以上配置后混乱的导入语句会被自动整理为:

javascript 复制代码
// Node.js 内置模块 (builtin)
import fs from 'fs';
import path from 'path';

// 外部依赖包 (external) - 按字母排序
import { Button, Input, Select } from 'antd';
import axios from 'axios';
import _ from 'lodash';
import moment from 'moment';
import React, { Component, useEffect, useState } from 'react';
import { useDispatch, useSelector } from 'react-redux';
import { createStore } from 'redux';

// 内部别名模块 (internal) - 按字母排序
import { debounce } from '@/utils/helpers';
import '@/styles/global.css';

// 父级目录导入 (parent) - 按字母排序
import { fetchUserData } from '../api';
import config from '../../config';
import { UserService } from '../services/UserService';

// 同级目录导入 (sibling) - 按字母排序
import './App.css';
import { Header, Footer } from './components';
import { someUtil } from './utils/someUtil';

// 当前目录索引文件 (index)
import index from './index';

配置效果

这种排序方式带来的好处:

  • 快速定位依赖:一眼找到第三方库和内部模块
  • 减少合并冲突:统一的导入顺序减少团队协作冲突
  • 依赖关系清晰:从导入结构就能看出模块依赖层次
  • 代码审查友好:整齐的导入语句提升代码可读性
相关推荐
踩着两条虫3 小时前
VTJ.PRO低代码快速入门指南
前端·低代码
Lazy_zheng3 小时前
一场“数据海啸”,让我重新认识了 requestAnimationFrame
前端·javascript·vue.js
crary,记忆3 小时前
MFE: React + Angular 混合demo
前端·javascript·学习·react.js·angular·angular.js
Asort4 小时前
JavaScript设计模式(十七)——中介者模式 (Mediator):解耦复杂交互的艺术与实践
前端·javascript·设计模式
linda26184 小时前
String() 和 .toString()的区别
前端·javascript·面试
拜晨4 小时前
初探supabase: RLS、trigger、edge function
前端
拖拉斯旋风4 小时前
零基础学JavaScript,简单学个设计模式吧
javascript
wyzqhhhh4 小时前
webpack
前端·javascript·webpack
Lorin洛林4 小时前
多 Web 端子系统共享会话:原理与实践
前端