37 # commonjs 规范流程梳理

简介: 37 # commonjs 规范流程梳理

require 源码大致过程

  1. mod.require 会默认调用 require 语法
  2. Module.prototype.require 模块的原型上有 require 方法
  3. Module._load 调用模块的加载方法,最终返回的是 module.exports
  4. Module._resolveFilename 解析文件名,将文件名变成绝对路径,默认尝试添加 .js .json
  5. Module._cache 默认会判断是否存在缓存
  6. new Module 创建模块(对象),里面有 id,exports
  7. Module._cache[filename] = module 把模块缓存起来,方便下次使用
  8. module.load 尝试加载模块
  9. module.paths 第三方模块查找的路径
  10. Module._extensions[extension] 获取当前模块的拓展名,策略是根据拓展名调用对应的方法
  11. fs.readFileSync 读取文件的内容
  12. module._compile 去除文本文件 BOM 头,编译文件的内容
  13. Module.wrap 将用户的内容包裹到一个函数中 (function (exports, require, module, __filename, __dirname) { }),用 vm.runInThisContext 创建沙箱环境,将字符串变成函数执行

简单实现一个自己的 require 去加载 js 文件

const path = require("path");
const fs = require("fs");
const vm = require("vm");
function Module(id) {
    this.id = id;
    this.exports = {};
}
Module.warp = function (script) {
    let arr = [`(function (exports, require, module, __filename, __dirname) {`, script, `})`];
    return arr.join("");
};
// 加载策略
Module._extensions = {
    ".js": function (module) {
        console.log("js---->", module);
        // 同步读取
        let content = fs.readFileSync(module.id, "utf-8");
        let fnStr = Module.warp(content);
        console.log("fnStr---->", fnStr);
        let fn = vm.runInThisContext(fnStr);
        console.log("fn---->", fn.toString());
        let exports = module.exports;
        let require = kaimoRequire;
        let __filename = module.id;
        let __dirname = path.dirname(module.id);
        // 改变 this 指向 exports
        fn.call(exports, exports, require, module, __filename, __dirname);
        // 用户会给 module.exports 赋值
    },
    ".json": function (module) {
        console.log("json---->", module);
        // 同步读取
        let content = fs.readFileSync(module.id, "utf-8");
        module.exports = JSON.parse(content);
    }
};
// 把文件名变成绝对路径
Module._resolveFilename = function (filepath) {
    // 根据当前路径实现解析
    let filePath = path.resolve(__dirname, filepath);
    // 判断当前文件是否存在
    let exists = fs.existsSync(filePath);
    if (exists) return filePath;
    // 尝试添加后缀
    let keys = Object.keys(Module._extensions);
    for (let i = 0; i < keys.length; i++) {
        let currentPath = filePath + keys[i];
        // 尝试加载后缀查找
        if (fs.existsSync(currentPath)) {
            return currentPath;
        }
    }
    throw new Error("模块不存在");
};
// 获取文件的后缀名进行加载
Module.prototype.load = function (filename) {
    let extname = path.extname(filename);
    Module._extensions[extname](this);
};
// 缓存模块
Module.cache = {};
// 加载模块
Module._load = function (filepath) {
    // 将路径转化成绝对路径
    let filename = Module._resolveFilename(filepath);
    console.log("filename---->", filename);
    // 获取路径后不要立即创建模块,先检查是否有缓存
    let cacheModule = Module.cache[filename];
    if (cacheModule) {
        return cacheModule.exports;
    }
    // 进行模块的创建,这里需要保证每个模块的唯一性,需要通过唯一路径进行查找
    let module = new Module(filename);
    // 缓存模块
    Module.cache[filename] = module;
    // 模块加载
    module.load(filename);
    return module.exports;
};
// 自己实现的 require 方法
function kaimoRequire(filepath) {
    // 根据路径加载模块
    return Module._load(filepath);
}
// 测试
let k = kaimoRequire("./37/a");
console.log("测试 kaimoRequire 输出 k---->", k);

新建 a.js 文件

let name = "kaimo313";
console.log("okk");
module.exports = name;

总结

  1. require 语法是同步的,用的是 fs.readFileSync
  2. 最终 require 语法返回的是 module.exports
  3. 模块的 exports 和 module.exports 引用的是同一个变量
  4. 模块是动态加载的,每次 require 都会获取最新的导出的结果,可以将 require 写到条件中
  5. 更改 exports 的引用,不会导致 module.exports 的变化
  6. 循环引用的解决方案就是不循环引用,一般不会出现,如果出现只能加载部分数据
目录
相关文章
|
6月前
|
安全 项目管理
一文搞懂需求流程规范的制定方法和落地技巧
随着业务和产品的发展、团队的不断扩大,很多团队都不可避免的会遇到需求流程混乱的问题。虽然有的团队也编写了一些“需求流程规范”的文档,但最终却流于纸面,难以在团队真正落地。如何科学制定并有效落实需求管理规范呢?对此,云效产品经理陈逊进行了非常详细的直播分享,本文是他经验的文字总结。
101993 19
|
存储 设计模式 人工智能
规范:前端代码开发规范
规范:前端代码开发规范
1571 0
|
6月前
|
SQL 设计模式 Java
Java编码规范与最佳实践
Java编码规范与最佳实践
141 0
|
6月前
|
前端开发 JavaScript 持续交付
前端代码审查规范
前端代码审查规范
155 0
CMMI流程规范—服务与维护
CMMI流程规范—服务与维护
387 0
|
监控 前端开发 持续交付
带你入门前端工程(二):统一规范(二)
带你入门前端工程(二):统一规范(二)
159 0
|
前端开发 JavaScript 搜索推荐
带你入门前端工程(二):统一规范(一)
带你入门前端工程(二):统一规范
144 0
|
算法 IDE 程序员
代码编写规范
代码编写规范
|
开发工具 git
代码统一风格、代码规范和提交规范
1、安装模块 全局安装 eslint、commitlint 、 check-prettier npm install eslint commitlint check-prettier -g 本地安装 npm install eslint-config-prettier  stylelint  stylelint-config-prettier stylelint-config-standard husky  @commitlint/config-conventional -D VSCode 安装 Eslint和Prettier插件
150 0
|
JSON 前端开发 JavaScript
规范(一):代码规范
规范(一):代码规范
规范(一):代码规范