翼度科技»论坛 编程开发 JavaScript 查看内容

commonjs

5

主题

5

帖子

15

积分

新手上路

Rank: 1

积分
15
Commonjs

什么是 CommonJs


  • CommonJs 是 js 模块化的社区规范
模块化产生的原因


  • 随着前端页面复杂度的提升,依赖的第三方库的增加,导致的 js 依赖混乱,全局变量的污染,和命名冲突
  • 单个 js 文件内容太多,导致了维护困难,拆分成为多个文件又会发生第一点描述的问题
  • v8 引擎的出现,让 js 有了媲美编译型语言的运行速度,大大激励了前端开发者
CommonJS 的使用环境


  • nodejs 实现了 CommonJS 模块化规范
CommonJs 有哪些规定


  • 每一个文件就是一个模块
  • 模块中的变量和函数不会污染全局(解决了全局污染和命名冲突)
  • 提供给外部使用的内容需要导出
  • 使用其他模块的内容需要导入 (模块的导入和导出共同解决了 js 依赖混乱的问题)
  • 模块不会重复加载,模块第一次导入后会将第一次导入的结果缓存,下次导入时,直接使用缓存的结构
  • 省略一些细碎的内容在下面代码中提及.....
commonJS 语法


  • 导入
  1. //这是导入一个模块,module.js;commonjs中规定require导入模块时可以省略.js后缀
  2. const module1 = require("./module1");
  3. //如果没有寻找到dir.js文件,而发现了dir路径,则寻找dir路径下package.json 的main属性指定的文件
  4. //如果package.json未指定路径,则触发默认规则 依次查找查找 index.js index.json
  5. const module2 = require("./dir");
  6. //如果require不是相对路径,则会去node_module中寻找该模块,重复module1 和module2 的步骤
  7. //如果没有node_modules 或node_modules 中不存在模块则继续向上级目录寻找node_modules,直到根目录
  8. const module3 = require("module3");
复制代码

  • 导出
  1. module.exports = {
  2.   //这里输入导出的内容
  3. };
  4. //这也是导出
  5. exports.a = "a";
  6. //注意 module.exports导出和exports[属性名]导出不可共存
  7. //module.exports会覆盖掉exports导出的内容
复制代码
简易实现类 nodejs 模块化环境
  1. const fs = require("fs");
  2. const Path = require("path");
  3. const vm = require("vm");
  4. const ModuleStack = [];
  5. function isRootDirectory(path) {
  6.   // Windows 根路径
  7.   const windowsRootDirectory = /^[a-zA-Z]:\\$/;
  8.   // Unix/Linux 根路径/
  9.   const unixRootDirectory = /^\//;
  10.   return windowsRootDirectory.test(path) || unixRootDirectory.test(path);
  11. }
  12. function isRelativeDirectory(path) {
  13.   //匹配 ../ 或者 ./开头的路径
  14.   const relativeDirectory = /^(\.\.\/|\.\/).+/;
  15.   return relativeDirectory.test(path);
  16. }
  17. // 计算node_modules路径
  18. let computePaths = (dirname) => {
  19.   let paths = [];
  20.   let path = dirname;
  21.   let node_modules = "./node_modules";
  22.   while (
  23.     !isRootDirectory(path) ||
  24.     !paths.includes(Path.resolve(path, node_modules))
  25.   ) {
  26.     paths.push(Path.resolve(path, node_modules));
  27.     path = Path.resolve(path, "../");
  28.   }
  29.   return paths;
  30. };
  31. function myRequire(path) {
  32.   let truelyPath;
  33.   if (isRelativeDirectory(path)) {
  34.     // 获取真实路径
  35.     truelyPath = Path.resolve(__dirname, path);
  36.   } else {
  37.     //获取可能的node_modules路径
  38.     let paths = computePaths(__dirname);
  39.     for (const item of paths) {
  40.       truelyPath = Path.resolve(item, path);
  41.       if (fs.existsSync(truelyPath)) {
  42.         break;
  43.       }
  44.     }
  45.     if (!truelyPath) {
  46.       throw new Error("Can't find module " + path);
  47.     }
  48.   }
  49.   // 如果缓存中有,直接返回
  50.   if (myRequire.cache[truelyPath]) {
  51.     return myRequire.cache[truelyPath].exports;
  52.   }
  53.   // 读取文件内容
  54.   const content = fs.readFileSync(path, "utf-8");
  55.   // 包装代码
  56.   const wrapper = [
  57.     "(function (exports, require, module, __filename, __dirname) { \n",
  58.     "\n})",
  59.   ];
  60.   // 拼接代码
  61.   const wrapperContent = wrapper[0] + content + wrapper[1];
  62.   // 获取文件路径和文件名
  63.   let dirname = Path.dirname(truelyPath);
  64.   let filename = truelyPath;
  65.   let parentModule =
  66.     ModuleStack.length > 0 ? ModuleStack[ModuleStack.length - 1] : null;
  67.   // 模块对象
  68.   const Module = {
  69.     id: Object.keys(myRequire.cache).length > 0 ? filename : ".",
  70.     path: dirname,
  71.     exports: {},
  72.     parent: parentModule,
  73.     filename: filename,
  74.     loaded: false,
  75.     children: [],
  76.     paths: computePaths(dirname),
  77.   };
  78.   if (parentModule) {
  79.     parentModule.children.push(Module);
  80.   }
  81.   //模块入栈
  82.   ModuleStack.push(Module);
  83.   // 需要运行的函数
  84.   const moduleScope = vm.runInThisContext(wrapperContent);
  85.   // 运行代码
  86.   moduleScope.call(
  87.     Module.exports,
  88.     Module.exports,
  89.     myRequire,
  90.     Module,
  91.     filename,
  92.     dirname
  93.   );
  94.   // 标记模块已加载
  95.   Module.loaded = true;
  96.   //模块出栈
  97.   ModuleStack.pop();
  98.   // 缓存模块
  99.   myRequire.cache[truelyPath] = Module;
  100.   return Module.exports;
  101. }
  102. myRequire.cache = Object.create(null);
复制代码
模块化的意义


  • 解决在模块化出现之前的js依赖混乱,全局污染命名冲突的问题
  • 模块化的出现让js代码可以拆分为多个模块共同协作,单个js文件过长的问题,降低了维护难度。
  • 模块化的出现让js开发大型项目出现了可能
ps:当前内容为学习commonjs理解,内容正确性请谨慎甄别。

来源:https://www.cnblogs.com/jiangyun/p/18141885
免责声明:由于采集信息均来自互联网,如果侵犯了您的权益,请联系我们【E-Mail:cb@itdo.tech】 我们会及时删除侵权内容,谢谢合作!

举报 回复 使用道具