nodejs模块化

发布时间:2023-07-24 08:00

nodejs模块化

一、模块化发展历程

传统开发常见问题:

  1. 命名冲突和污染
  2. 代码冗余,无效请求多
  3. 文件的依赖关系复杂

模块:小而精,利于维护的代码片段
常见模块化规范

CommonJS规范    node.js(同步加载)
AMD规范        require.js(异步加载,依赖前置)
CMD规范        sea.js(异步加载,就近依赖)
ES6模块化        原生(编译时,异步加载)

当下常用的规范是Commonjs与ESM,参考文档:https://www.cnblogs.com/echoy...

二、CommonJS规范(语言层面上的规范)

三个基础实现:

 模块定义
 模块标识
 模块引用

node.js与CommonJS:

任意一个文件就是一个模块,具有独立的作用域
使用require导入其它模块
将模块ID传入require实现目标模块定位

CommonJS在nodejs中的实现:
module属性

任意一个js文件就是一个模块,可直接使用module属性
id:返回模块标识符,一般是一个绝对路径
filename:返回文件模块的绝对路径
loaded:返回布尔值,表示模块是否完成加载
parent:返回对象,存放了调用当前模块的模块
children:返回一个数组,存放当前模块调用的其它模块
exports:返回当前模块需要暴露的内容
paths:返回数组,存放不同目录下的node_modules的位置

module.exports与exports的区别

两者指向同一个内存地址,当exports重新赋值之后,exports就相当于一个局部变量了

\"nodejs模块化_第1张图片\"

require属性

基本功能是读入并执行一个模块文件,返回这个模块文件的moudule.exports对象
resolve:返回模块文件绝对路径
extensions:根据不同后缀名执行解析操作
main:返回主模块对象

nodejs中CommonJS规范的代码实现:

//主模块
// 一、模块的导入与导出
const m = require(\'./m\');
console.log(m)

// 二、module
// console.log(\'no_module\',module);

// 三、exports
// console.log(m)

// 四、同步加载

// 判断当前模块是否为主模块
console.log(require.main === module)        // require.main  指向自己



//外置模块:
// 一、模块的导入与导出
const age = 18;
const addFn = (x, y) => {
    return x + y;
}
// module.exports = {
//     age,
//     addFn
// }

// 二、module
// console.log(\'no_module\',module);

// 三、exports
exports.name = \'jiang\';

// 四、同步加载
let name = \'lg\';
// let iIime = Date.now();
// while(Date.now() - iIime < 4000) {}     // 同步加载,阻塞当前线程
exports.nickName = name;
console.log(\'m.js执行了\')

// 判断当前模块是否为主模块
console.log(require.main === module)        // require.main  parent

三、模块加载

缓存优先原则

模块分类:

内置模块:Node源码编译时写入到了二进制文件中,使用时加载比较快
文件模块:代码运行时,动态加载的,需要经历完整加载流程

模块加载流程:

路径分析:根据标识符(路径与非路径)确定模块位置(module.paths返回了模块加载策略)
文件定位:确定目标模块中具体的文件及文件类型(按照.js-->.json-->.node顺序补足拓展名)
编译执行:根据文件类型,采用对应的方式完成文件的编译执(.js编译:将其封装成一个立即执行函数,并传入exports、module、require等参数执行;.json编译:使用JSON.parse解析),返回exports可用对象

四、VM模块

创建独立运行的沙箱环境

类似沙箱的实现:

const fs = require(\'fs\');
const path = require(\'path\');
const vm = require(\'vm\');

let content = fs.readFileSync(path.join(__dirname,\'test.txt\'), \'utf-8\');

// eval
// eval(content);    content中变量需要用var,才能在这里访问 

// new Function
// let fn = new Function(\'age\', \'return age + 1\');
// console.log(fn(19))

let age = 88;

vm.runInThisContext(content);
console.log(age);

五、文件加载模拟实现

核心逻辑:

路径分析
缓存优先
文件定位
编译执行

代码实现:

const fs = require(\'fs\');
const path = require(\'path\');
const vm = require(\'vm\');

function Module(id) {
    this.id = id;
    this.exports = {};

}


Module.prototype.load = function (filename) {
    let extname = path.extname(this.id);
    Module._extensions[extname](this);
    console.log(\'111\')
}
Module.wrapper = [
    \'(function(exports,require,module,__filename,__dirname){\',
    \'})\'
]

Module._extensions = {
    \'.js\'(module) {
        // 读取
        let content = fs.readFileSync(module.id, \'utf-8\');
        // 包装成函数
        content = Module.wrapper[0] + content + Module.wrapper[1];
        let compileFn = vm.runInThisContext(content);

        // 准备参数值
        let exports = module.exports;
        let dirname = path.dirname(module.id);
        let filename = module.id;

        // 执行
        compileFn.call(exports, exports, myRequire, module, filename, dirname);
    },
    \'.json\'(module) {
        let content = JSON.parse(fs.readFileSync(module.id, \'utf-8\'));
        module.exports = content;
    },
}

Module._resolveFilename = function (filename) {
    // 1 绝对路径
    let absPath = path.resolve(__dirname, filename);
    // 2 判断文件是否存在
    if (!fs.existsSync(absPath)) {
        // 文件定位
        let suffix = Object.keys(Module._extensions);

        for (let i = 0; i < suffix.length; i++) {
            let s = suffix[i];
            let p = absPath + s;
            if (fs.existsSync(p)) {
                return p;
            }
        }
        throw new Error(`${filename} is not exists`);
    }
    return absPath;
}

Module._cache = {};

function myRequire(filename) {
    // 1 绝对路径
    let mPath = Module._resolveFilename(filename);
    console.log(\'mPath\', mPath);

    // 2 缓存优先
    let cacheModule = Module._cache[mPath];
    if (cacheModule) return cacheModule.exports;

    // 3 创建空对象,加载模块
    let module = new Module(mPath);

    // 4 缓存已加载的模块
    Module._cache[mPath] = module;

    // 5 执行加载(编译执行)
    module.load(mPath);

    // 6 返回数据
    return module.exports;
}

let obj = myRequire(\'./v\');
myRequire(\'./v\'); // 缓存优先

console.log(obj);

ItVuer - 免责声明 - 关于我们 - 联系我们

本网站信息来源于互联网,如有侵权请联系:561261067@qq.com

桂ICP备16001015号