原创 海石 2025-02-26 08:31 重庆
点击关注公众号,“技术干货” 及时达!
前言
"啪!"刚接手新项目的你兴奋地双击打开项目文件,却被满屏的 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顺序不仅是代码规范,更是研发团队的技术审美宣言。
点击关注公众号,“技术干货” 及时达!