这是个代码风格统一的问题,不是语法对错。你们项目用的是 @antfu/eslint-config(Anthony Fu 的配置),它默认要求:模块顶层导出的函数用 function 声明,箭头函数留给回调和局部场景。
为什么 ESLint 要区分两种写法?
核心目的就三个:
- 风格统一 --- 全项目一种写法,读代码不用猜「这里为什么用箭头、那里为什么用 function」
- 可读性 --- 顶层
export function encrypt()一眼能看出是模块对外 API;const encrypt = () => {}更像变量赋值 - 减少无意义的差异 --- 两种写法在顶层功能上几乎一样,团队约定一种,避免 PR 里来回改风格
Antfu 的配置哲学是:顶层用 function,箭头函数留给它真正擅长的场景。
什么时候用 function?
模块顶层(文件最外层、要 export 的函数):
// ✅ 你们项目的写法
export function encrypt(txt: string) { ... }
export function useProjectList() { ... }
function generateRandomString() { ... } // 文件内私有辅助函数
你们项目里 authApi.ts、projectApi.ts、crypto.ts、useProjectList.ts 都是这个模式。
函数/composable 内部的具名函数(Antfu 也倾向用 function):
javascript
export function useProjectList() {
async function fetchList() { ... } // ✅
function handleSearch() { ... } // ✅
}
什么时候用 const xxx = () => {} 或 () => {}?
箭头函数更适合这些场景:
1. 回调(最常见)
javascript
list.map((item) => item.id)
list.filter(item => item.active)
button.addEventListener('click', () => { ... })
你们项目里就有:
typescript
return raw.map((item) => {
const row = item as Record<string, unknown>
// ...
}).filter(item => Number.isFinite(item.id))
2. 需要保留外层 this 时
ini
// 类或 Options API 里
someObj.onClick = () => {
this.value = 1 // 箭头函数继承外层的 this
}
3. 单行、临时、不需要名字的函数
scss
const double = (n: number) => n * 2
setTimeout(() => doSomething(), 1000)
4. 需要把函数当值传递、且不想提升(hoist)时
javascript
const handlers = {
click: () => { ... },
submit: () => { ... },
}
快速对照
| 场景 | 推荐写法 |
|---|---|
模块顶层 export 的函数 |
export function foo() {} |
| 文件内顶层辅助函数 | function helper() {} |
| composable 内部的具名方法 | function fetchList() {} |
.map / .filter / 事件回调 |
() => {} |
需要固定 this |
() => {} |
| 对象字面量里的方法(看团队约定) | 两种都有人用,你们项目顶层偏 function |
总结
ESLint 不是在说「箭头函数不好」,而是在说:顶层导出的函数请用 function,别用 const xxx = () => {}。
const xxx = () => {} 依然很好用,只是更适合回调、闭包、需要 lexical this、匿名小函数这类场景,而不是模块的主要 API 定义。
如果不想遵守这条规则,可以在 eslint.config.js 里关掉 antfu/top-level-function(或对应规则),但和你们现有代码风格会不一致。