前端模块化整理

132 阅读3分钟

1.导入导出

  • commonjs采用module.exports/require 的方式,最终返回的对象是module.exports对象,但在导出时,this,exports,module.exports是同一块内存空间,除非对三者重新赋值。
  • esm采用export/import的方式,可以使用默认导出和具名导出,对于默认导出的,可以直接使用import xxx from;

对于具名导出的,需要使用import { xxx } from;同时还可使用as重新命名,或者使用import * from,*是包含所有

具名导出的对象

2.查找策略

  • 文件名查找:commonjs支持模糊匹配,如果有对应路径的文件名,则直接使用该文件,如果没有,则会尝试添加.js​、.json​、.node​等后缀名
  • 目录查找:如果目录下有package.json,则会匹配main字段对应文件,如果没有,则会会尝试加载目录下的index.js​或index.node​文件
  • node_modules查找:会逐级向上查找node_modules,直至找到后执行目录查找策略
  • 文件名/目录查找:esm则遵循严格的路径名查找
  • node_modules查找:如果目录下有package.json,则会匹配exports字段对应文件,否则使用index文件

3.加载

  • commonjs是同步加载,相当于立即执行require函数,并缓存结果
  • esm是异步加载的,对于import,会在先解析依赖树,然后按依赖树到顺序异步加载模块,等到所有模块加载完后,才开始执行代码;对于import(),其返回一个promise,需等待它完成,才能获取加载结果

4.缓存机制

  • commonjs在每个模块代码执行完后,会将其结果缓存,当require相同路径时,会直接返回缓存结果
  • esm中每个模块的代码也会将相同路径的结果缓存,但是由于esm中导出的内容都是静态绑定的,所以会出现类似闭包的问题,导入的变量会随着模块内部状态的变化而更新
// counter.mjs
export let count = 0;
export function increment() {
  count++;
}

// main.mjs
import { count, increment } from './counter.mjs';

console.log("Initial count:", count); // 0
increment();
console.log("After increment:", count); // 1

5.清除缓存

  • commonjs可以通过require.cache手动清除缓存
  • esm不支持手动清除缓存,但是可以通过import()给url增加参数绕过缓存
(async () => {
  const moduleA = await import(`./example.mjs?${Date.now()}`);
  console.log(moduleA.value);
})();

6.TS中的查找策略

  • "moduleResolution": "node"

    • 文件查找:

      依次寻找:
      
      /src/myModule.ts
      
      /src/myModule.tsx
      
      /src/myModule.d.ts
      
      /src/myModule/package.json(访问 "types" 字段)
      
      /src/myModule/index.ts
      
      /src/myModule/index.tsx
      
      /src/myModule/index.d.ts
      
    • node_modules查找

      /src/node_modules/axios.ts
      
      /src/node_modules/axios.tsx
      
      /src/node_modules/axios.d.ts
      
      /src/node_modules/axios/package.json(访问"types"字段)
      
      /src/node_modules/@types/axios.d.ts
      
      /src/node_modules/axios/index.ts
      
      /src/node_modules/axios/index.tsx
      
      /src/node_modules/axios/index.d.ts
      
      如果当前目录没找到,继续向上再来一次
      
      /node_modules/axios.ts
      
      /node_modules/axios.tsx
      
      /node_modules/axios.d.ts
      
      /node_modules/axios/package.json(访问"types"字段)
      
      /node_modules/@types/axios.d.ts
      
      /node_modules/axios/index.ts
      
      /node_modules/axios/index.tsx
      
      /node_modules/axios/index.d.ts
      
      如果当前目录没找到,继续向上再来一次......
      ......
      
    • TS不会帮我们处理模块说明符,所以在编译后,在esm环境下,仍然无法识别我们的路径别名,这一点可以在import的时候采用js后缀的url路径

  • "moduleResolution": "node16/nodeNext"

    • 在声明了package中的exports配置时,是高于types指定的配置的,具体查找顺序如下

      /src/node_modules/axios.ts
      
      /src/node_modules/axios.tsx
      
      /src/node_modules/axios.d.ts
      
      /src/node_modules/axios/package.json(优先访问"exports"字段,后访问"types"/"main"/"module"字段)
      
      /src/node_modules/@types/axios.d.ts
      
      /src/node_modules/axios/index.ts
      
      /src/node_modules/axios/index.tsx
      
      /src/node_modules/axios/index.d.ts
      
      如果当前目录没找到,继续向上再来一次
      
      /node_modules/axios.ts
      
      /node_modules/axios.tsx
      
      /node_modules/axios.d.ts
      
      /node_modules/axios/package.json(优先访问"exports"字段,后访问"types"/"main"/"module"字段)
      
      /node_modules/@types/axios.d.ts
      
      /node_modules/axios/index.ts
      
      /node_modules/axios/index.tsx
      
      /node_modules/axios/index.d.ts
      
      如果当前目录没找到,继续向上再来一次......
      ......