研发幸福论:从import顺序看代码的呼吸感
点击关注公众号,“技术干货”及时达!
前言"啪!"刚接手新项目的你兴奋地双击打开项目文件,却被满屏的 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 setupimport { 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顺序不仅是代码规范,更是研发团队的技术审美宣言。
点击关注公众号,“技术干货”及时达!
阅读原文
网站开发网络凭借多年的网站建设经验,坚持以“帮助中小企业实现网络营销化”为宗旨,累计为4000多家客户提供品质建站服务,得到了客户的一致好评。如果您有网站建设、网站改版、域名注册、主机空间、手机网站建设、网站备案等方面的需求...
请立即点击咨询我们或拨打咨询热线:13245491521 13245491521 ,我们会详细为你一一解答你心中的疑难。 项目经理在线