美文网首页基础前端ECMAScript
ES Modules 中的 __dirname 和 __file

ES Modules 中的 __dirname 和 __file

作者: CondorHero | 来源:发表于2022-08-05 10:54 被阅读0次
    ES Modules 中的 __dirname 和 __filename.png

    模块化发展

    早期,前端这块没有模块化系统,而 Node.js 需要模块化所以只能一直使用 CommonJS 标准凑合着,后来 ECMAScript 委员会通过了 ES Modules 标准。CommonJS 的处境就比较尴尬了,时至今日无论用 JS 来写前后端 ES Modules 都已经成为了标配。

    ES Modules 遇到了问题

    CommonJS 中提供的全局变量如require, exports, module.exports, __filename, __dirname 等,在 ES Modules 环境中均是不可用的,require, exports, module.exports 在 ES Modules 中基本对应着 import, export, export default

    但是在编程中 __filename__dirname 也是高频 API,当它们出现在 ESM 中,运行代码会抛出错误 ReferenceError: __dirname is not defined in ES module scope

    难道 ES Modules 里面没有对应的 API 吗,当然有不过完全没有 CommonJS 中的 __filename__dirname 用着方便,我们需要自己模拟。

    import.meta.url

    import.meta 包含当前模块的一些信息,其中 import.meta.url 表示当前模块的 file: 绝对路径,拿到这个绝对路径我们就可以配合其他 API 来实现 __filename__dirname

    我们有代码:

    console.log(import.meta.url);
    

    运行会得到一个基于 file 协议的 URL:file:///Users/ape/Desktop/temp/index.js

    fileURLToPath

    接下来需要把 file 协议转换成路径,我们需要借助 Node.js 内部 url 模块的 fileURLToPath API。

    import { fileURLToPath } from "node:url";
    console.log(fileURLToPath(import.meta.url));
    

    运行得到路径:/Users/ape/Desktop/temp/index.js

    __filename

    通过 import.meta.urlfileURLToPath 我们能很容易模仿 __filename API。

    const __filename = fileURLToPath(import.meta.url);
    

    __dirname

    我们已经拿到了 __filename 的值,实现 __dirname 就简单了,借助 Node.js 的内部模块 path 的 dirname 方法来实现:

    import { dirname } from "node:path"
    import { fileURLToPath } from "node:url"
    
    const __filename = fileURLToPath(import.meta.url);
    const __dirname = dirname(__filename);
    
    console.log(__dirname);
    

    运行得到模块绝对文件夹路径:/Users/ape/Desktop/temp

    require.resolve

    最后给大家来点干货。

    描述场景

    模块化开发有一个很头疼的问题,就是要经常 import,比如在包含 React、Vue 等库的项目中,就要经常 import { useState } from "React"import { ref } from "vue",有没有一种办法可以省略这个步骤,自动 import 用到 API 直接使用,当然有这就是 unplugin-auto-import 包出现的原因,但是 unplugin-auto-import 不够智能,项目哪些需要自动引入的 API,需要我们手动传入,所以 unplugin-auto-import 维护了一份 presets

    这问题就出现了,npm 的库千千万 presets 一定包含不全,由使用者手动引入就不符合开箱即用的思想了。

    解决办法也很简单:

    1. 动态:我们直接动态 import 一个库,这不就拿到了库所有的导出了。
    2. 静态:第一步,我们找到库的 package.json 文件,第二步拿到 main 或 exports 字段,找到并读取文件拿到库的导出。

    第一种思路,直接 await import("vue") 完事了,我们看第二种思路,重点在第一步如何找到一个库的 package.json 文件,只有拿到它的绝对路径才能解析读取进行后续操作。

    如果实在 CJS 中我们好操作,直接上代码:

    const getPackageJsonPath = require.resolve('vue/package.json')
    
    console.log(getPackageJsonPath)
    

    看到这个 require.resolve 心中一凉,完了 ESM 没有这个 API 了,啊哈哈,不用担心,有替代还不止一个:

    createRequire

    import { createRequire } from 'node:module'
    const require = createRequire(import.meta.url)
    
    const getPackageJsonPath = require.resolve('tsup/package.json')
    console.log(getPackageJsonPath)
    

    import.meta.resolve

    不建议使用,目前还在试验阶段没有稳定。

    参考

    相关文章

      网友评论

        本文标题:ES Modules 中的 __dirname 和 __file

        本文链接:https://www.haomeiwen.com/subject/cehpwrtx.html