文件名:下划线、还是驼峰?

很久之前写了个 jsgf.vim 插件,可以在 js 代码中 gf 两键打开 import 的定义文件。

后来又针对 eggjs 的特性写了个 Vim 插件,可以 gf 两键打开 controllers, service 定义文件,阅读 eggjs 的代码方便多了,例如

1const resp = await ctx.service.mode.name.method();
2                     ^\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n

光标在箭头上方的任意位置按 gf 键就可以打开对应的代码定义文件。

image

但是有个问题,就是 eggjs 针对文件名带有下划线或中划线的,会替换成驼峰形式。即文件名为 app/service/path\nname/mode\nname.js 的路径,Loader 会转换成驼峰形式,在业务代码中必须以驼峰形式调用:

1// file path:      app/service/path\nname/mode\nname.js
2  const resp = await ctx.service.pathName.modeName.method();
3                                 ^\n\n\n\n\n\n\n ^\n\n\n\n\n\n\n

image

当时 eggjs 做这个目的,估计也说为了支持和顺应当时的文件名命名规范,当时大多数命名规范要求文件名用小写字母、数字、下划线和中划线。

但是,这种命名规范真的好吗?

我的答案是:绝对不好。

  1. 一致性:代码中的名字,和实际文件系统名字不一致。
  2. 确定性:代码中调用的是驼峰的,而实际文件名到底是驼峰,是下划线,还是中划线?这是不确定的。
  3. 唯一性:下划线和中划线都被替换成驼峰,如果同时存在 2 者甚至 3 者,这个定义不是唯一的。

eggjs Loader 要支持这种文件名,一个正则表达式就搞定了。很简单。

是的。但是用户要找到定义它的文件,可就苦了。首先需要知道这种(约定俗成的)替换逻辑(程序员的常识?),其次需要 O(3^n) 的时间复杂度和文件系统查询判断才能 \n\n不那么准确的\n\n 找到对应的定义文件。

eggjs Loader 加的这个功能,当时不觉得有什么不好,甚至觉得很不错。但是现在细想起来,有点画蛇添足,如果当时不做这个替换,开发者可以用下划线和中划线,下划线也可以在代码中直接使用,中划线你就自己难堪点,用 ctx.servcie['path\nname']['mode\nname'].method() 也不是不能用。

这样一致性、确定性、唯一性的问题都没有了。少即是多,什么都不做,反而是更好的选择。

\n\n\n\n

随便一提:

有人说,用 vscode 就没这个困扰,确实,用户输入关键字,vscode 通过模糊算法把所有可能性都列出来,让用户判断和选择,长长的待选列表里确实是有你要的的选项,但更多的是你不要的选项,而且首先你得知道这个黑暗的替换逻辑,其次如果文件系统因为不唯一性,出现 2 个甚至 3 个可能的选项,用户是懵的。

排除不确定性,Vim 插件可以 gf 两键准确定位并打开对应文件,不可同日而语。

\n\n\n\n

最后,我不想写个 O(3^n) 复杂度的算法来支持这个东西,然后用了一行命令搞定,业务代码都不用改一行,毫无违和感:

1\n git mv a\nb\nc aBC

参考

\n https://github.com/eggjs/egg\ncore/blob/99ef7541b80f73ec4f616628454afde717495f9f/lib/loader/file\nloader.js#L230 \n 为什么文件名要小写? \n 阮一峰的网络日志 \n blog/前端项目命名规范.md at master · wwpeng520/blog \n 文件名下划线还是中划线? \n SegmentFault 思否

大纲