前言
"啪!"刚接手新项目的你兴奋地双击打开项目文件,却被满屏的 import 语句当头一棒——React、antd、工具函数、业务组件像被猫抓乱的毛线团纠缠在一起。
// 优化前的灾难现场
import React, { useState } from "react";
import ComponentC from "../components/ComponentC";
import styles from "./index.module.scss";
import testIcon from "@/images/test/testIcon.svg";
import ComponentA from "../components/ComponentA";
import { getMockDataList } from "@/service/testCenter";
import { Spin } from "antd";
import ComponentB from "../components/ComponentB";
import { useEffect } from "react";
此刻的你,是否想起了被祖传代码支配的恐惧?
痛点直击:混乱import的三大原罪
- 定位失焦:在antd组件和业务工具函数之间反复横跳,找依赖像玩"大家来找茬"
- 心智负担:每次新增依赖都要纠结放在哪里,代码洁癖者的噩梦
- 协作灾难:团队成员各写各的,PR评审变成import顺序辩论赛
破局之道:像整理衣橱般重构import
第一性原理:优秀的代码组织应该像宜家家具说明书——即使新手也能秒懂装配顺序
技术方案三部曲:
- 规范定义(立规矩)
// 理想中的import层次
// 第一梯队:框架核心
import React, { useState, useEffect } from 'react'
// 第二梯队:三方库
import { Spin } from 'antd'
import { useRouter } from 'react-router-dom'
// 第三梯队:项目模块
import { getMockDataList } from '@/service/testCenter'
import testIcon from "@/images/test/testIcon.svg";
// 第四梯队:同级组件
import Topic from '../components/Topic'
- 自动化校验(上手段)
请先安装 eslint-plugin-import 插件,
npm install eslint-plugin-import --save-dev
否则我们配置完 .eslintrc 文件后会发现,终端会报错:
1:1 error Definition for rule 'import/order' was not found
感谢评论区细心的掘友补充指正。
// .eslintrc 配置精髓
{
// 在plugins数组的最后添加上“import”,告诉eslint要使用我们
// 刚刚安装好的eslint-plugin-import插件
plugins: ["react", "@typescript-eslint", "react-hooks", "import"],
rules: {
"import/order": [
"error",
{
"groups": [
"builtin", // Node内置
"external", // npm包
"internal", // 项目内部
"parent", // 父目录
"sibling", // 同级
"index" // 目录索引
],
"pathGroups": [
{
"pattern": "react",
"group": "external",
"position": "before"
},
{
"pattern": "@/**", // 给别名路径特殊待遇
"group": "internal",
"position": "before"
}
],
"newlines-between": "always", // 分组间空行
// 一定要手动修改下方的值,因为pathGroupsExcludedImportTypes
// 的默认值是["builtin", "external", "object"],
// 因此,假如我们不重新赋值,那么我们在pathGroups中
// 定义的有关react的配置,就会被排除(因为它属于external),设置的position: before
// 并不会生效,我们会发现eslint还是提示我们应该将antd在react之前import
// 所以再强调一遍,一定要修改pathGroupsExcludedImportTypes的值
"pathGroupsExcludedImportTypes": ["builtin"],
"alphabetize": { // 字母表排序
"order": "asc",
"caseInsensitive": true
}
}
]
}
}
- 开发流闭环(提体验)
当我们完成前两步的时候,我们会发现,终端里的报错信息一下子就变多了,对于之前缺少管理的文件,我们得一个个手动修复,这实在是太累了。
因此我们可以修改.vscode/settings.json文件
// 终极武器:保存即自动格式化
{
"editor.codeActionsOnSave": {
"source.fixAll.eslint": true
},
"eslint.validate": ["javascript", "javascriptreact", "typescript", "typescriptreact"]
}
这将在保存时自动应用 ESLint 的修复,包括 import 排序。
完成修改配置后,每次 Cmd + S 自动获得:
// 如德芙般丝滑的import结构
import React, { useEffect, useState } from 'react'
import { Spin } from 'antd'
import { useRouter } from 'react-router-dom'
import { getMockDataList } from '@/service/testCenter'
import testIcon from "@/images/test/testIcon.svg";
import styles from './index.module.scss'
import ComponentA from "../components/ComponentA";
import ComponentB from "../components/ComponentB";
import ComponentC from "../components/ComponentC";
Vue生态适配指南
对于<script setup>的魔法世界,只需微调分组策略:
"pathGroups": [
{
"pattern": "vue*", // 捕获vue全家桶
"group": "external",
"position": "before"
}
]
优化效果:
<script setup>
import { ref } from 'vue'
import { useStore } from 'vuex'
import { formatCurrency } from '@/libs/utils'
import ProductCard from './ProductCard.vue'
</script>
❗️❗️❗️注意事项❗️❗️❗️
请注意,当你的vue项目是从vue官方文档通过执行
npm create vue@latest
命令创建的,那么你按照本篇博客的方式进行eslintrc.js的文件修改会出现问题,并不能达成期望的效果🙅。
出于篇幅考虑,在本篇博客中,我仅罗列我遇到的一些问题和解决方法,欢迎掘友在评论区分享其他的踩坑经验。
- vite要求esm:如果我们的eslintrc.js文件中存在
module.exports的写法,那么在运行这个项目时会报错。由于脚手架项目在package.json中设置了"type": "module",这意味着所有.js文件都会被当作ES模块处理。而ESLint的配置文件如果使用.eslintrc.js,并且里面用了CommonJS的语法(比如module.exports),就会导致错误,因为ES模块中不能使用module.exports。module.exports => export default .eslintignore文件已废弃:在最新版的eslint中,不再建议用户通过创建.eslintignore文件来忽略某些文件,而是期望在 ESLint 配置中添加ignores字段来实现对某些文件的忽略。ignores: ["**/node_modules/**", "dist/**", "public/**"], // 替代 .eslintignore- ESLint在v8.23.0之后引入了Flat Config格式:新的配置文件方式,使用
eslint.config.js,而传统的.eslintrc.*文件不再被支持。// eslint.config.js import eslintJs from "@eslint/js"; import importPlugin from "eslint-plugin-import"; import vuePlugin from "eslint-plugin-vue"; import globals from "globals"; export default [ { // 全局配置 languageOptions: { globals: { ...globals.browser, ...globals.node, AudioWorkletGlobalScope: true, }, parserOptions: { ecmaVersion: "latest", sourceType: "module", }, }, ignores: ["**/node_modules/**", "dist/**", "public/**"], // 替代 .eslintignore }, eslintJs.configs.recommended, // ESLint 推荐规则 ...vuePlugin.configs["flat/recommended"], // Vue3 推荐规则 { plugins: { import: importPlugin, }, // 自定义规则 rules: { "no-console": "warn", "import/order": [ "error", { groups: [ "builtin", "external", "internal", ["sibling", "parent"], "index", "object", "type", ], pathGroups: [ { pattern: "vue*", group: "external", position: "before", }, { pattern: "@/hooks/**", group: "internal", position: "before", }, { pattern: "@/contexts/**", group: "internal", position: "before", }, { pattern: "@/components/**", group: "internal", position: "before", }, { pattern: "@/icons/**", group: "internal", position: "before", }, { pattern: "@/service/**", group: "internal", position: "before", }, { pattern: "@/images/**", group: "internal", position: "before", }, { pattern: "@/**", group: "internal", position: "before", }, { pattern: "./**", group: "sibling", position: "after", }, ], pathGroupsExcludedImportTypes: ["builtin"], alphabetize: { order: "asc", caseInsensitive: true, }, "newlines-between": "always", }, ], }, }, ]; - 请注意修改vite.config.js:检查
vite.config.js中的插件配置import { fileURLToPath, URL } from "node:url"; import vue from "@vitejs/plugin-vue"; import { defineConfig } from "vite"; import eslintPlugin from "vite-plugin-eslint"; // https://vitejs.dev/config/ export default defineConfig({ plugins: [ vue(), eslintPlugin({ // 关闭 LintOnStart 警告 lintOnStart: false, }), ], resolve: { alias: { "@": fileURLToPath(new URL("./src", import.meta.url)), }, }, }); - 确认是否安装好必要的依赖以及正确的依赖版本:
这里的这个"devDependencies": { "@babel/eslint-parser": "^7.26.8", "@eslint/js": "^9.20.0", "@vitejs/plugin-vue": "^5.0.4", "eslint": "^9.20.1", "eslint-plugin-import": "^2.31.0", "eslint-plugin-vue": "^9.32.0", "globals": "^15.15.0", "vite": "^5.2.8", "vite-plugin-eslint": "^1.8.1" },globals如果不安装最新版本很坑😡😡😡,因为老版本存在一个错误的变量: 老版本导出的AudioWorkletGlobalScope在内部是这么写的:"AudioWorkletGlobalScope ",没错,它居然在最后有个空格,这就导致我们运行项目,终端会报错❌,我真是服了.....10:14:23 [vite] Pre-transform error: Key "languageOptions": Key "globals": Global "AudioWorkletGlobalScope " has leading or trailing whitespace
研发幸福感公式
代码整洁度 × 自动化程度 = 开发愉悦指数
实践收益:
- 新人上手时间减少40%(此数据并非实际量化,系博主主观判断🏳️)
- 代码冲突率下降65%(此数据并非实际量化,系博主主观判断🏳️)
- 心智资源释放,专注业务逻辑
灵魂拷问:当你的import顺序能像书架上的书籍分类般清晰,还会害怕需求变更吗?
结语
技术启示录:优秀的工程素养体现在对细节的极致追求。import顺序不仅是代码规范,更是研发团队的技术审美宣言。