node.js本地模块

来源:互联网 发布:网络综艺与电视综艺 编辑:程序博客网 时间:2024/06/06 03:44

nodejs有三种模块:内建模块(c++)、本地模块、用户模块

内建模块:使用c++编写,源码文件夹下src目录下很多node_开头的文件就是内建模块

本地模块:使用js编写,源码文件夹下lib目录下的都是本地模块,以上两种模块都会被编译到node二进制命令文件中去

用户模块:node_modules目录下的就是,分为全局安装和非全局的


本地模块的定义:

./lib/bootstrap_node.js
const ContextifyScript = process.binding('contextify').ContextifyScript;  //根据字符串代码,获取其中定义的匿名函数  function runInThisContext(code, options) {    const script = new ContextifyScript(code, options);    return script.runInThisContext();  }  //本地模块类,与普通模块相比,没有子模块,因为在其中不调用其他模块  //本地模块指的是源代码目录下lib子目录下的js模块  function NativeModule(id) {    this.filename = `${id}.js`;    this.id = id;    //导出对象    this.exports = {};
    //是否已加载    this.loaded = false;
    //是否正在加载    this.loading = false;  }  //本地模块脚本存储,c++中加载脚本,作为字符串存储  NativeModule._source = process.binding('natives');  //模块实例缓存  NativeModule._cache = {};  //内建配置对象  const config = process.binding('config');  //require函数,用来获取本地模块,也可以获取NativeModule类
  //注意require获取'native_module'时并不是获取对应的js文件  NativeModule.require = function(id) {    if (id === 'native_module') {      return NativeModule;    }    //先从缓存中获取    const cached = NativeModule.getCached(id);    //如果获取到,且已加载或正在加载,直接返回导出对象    if (cached && (cached.loaded || cached.loading)) {      return cached.exports;    }    //不包含指定模块,抛出错误    if(!NativeModule.exists(id)) {      // Model the error off the internal/errors.js model, but      // do not use that module given that it could actually be      // the one causing the error if there's a bug in Node.js      const err = new Error(`No such built-in module: ${id}`);      err.code = 'ERR_UNKNOWN_BUILTIN_MODULE';      err.name = 'Error [ERR_UNKNOWN_BUILTIN_MODULE]';      throw err;    }    //模块加载列表加入代表本地模块的字符串    process.moduleLoadList.push(`NativeModule ${id}`);    //构造本地模块    const nativeModule = new NativeModule(id);    //将本地模块实例缓存到构造函数上    nativeModule.cache();    //编译本地模块,执行脚本获取导出对象    nativeModule.compile();    //返回导出对象    return nativeModule.exports;  };
  //./lib/dep文件夹下的js模块使用这个函数来获取  NativeModule.requireForDeps = function(id) {    if (!NativeModule.exists(id) ||        // TODO(TimothyGu): remove when DEP0084 reaches end of life.        id.startsWith('node-inspect/') ||        id.startsWith('v8/')) {      id = `internal/deps/${id}`;    }    return NativeModule.require(id);  };  //获取指定名称缓存模块  NativeModule.getCached = function(id) {    return NativeModule._cache[id];  };  //指定名称本地模块是否存在  NativeModule.exists = function(id) {    return NativeModule._source.hasOwnProperty(id);  };  //如果配置指明暴露internal文件夹下模块  if (config.exposeInternals) {       //不区分是否是内部模块    NativeModule.nonInternalExists = NativeModule.exists;    //则没有模块是内部模块    NativeModule.isInternal = function(id) {      return false;    };  }  //如果不暴露内部模块  else {    //当模块存在且不是内部模块时返回true    NativeModule.nonInternalExists = function(id) {      return NativeModule.exists(id) && !NativeModule.isInternal(id);    };    //模块id以internal开头为内部模块    NativeModule.isInternal = function(id) {      return id.startsWith('internal/');    };  }  //根据id获取本地模块脚本字符串  NativeModule.getSource = function(id) {    return NativeModule._source[id];  };  //包裹本地模块脚本  NativeModule.wrap = function(script) {    return NativeModule.wrapper[0] + script + NativeModule.wrapper[1];  };  //用于包裹的字符串,最后会被包裹为一个括号括住的匿名函数表达式  NativeModule.wrapper = [    '(function (exports, require, module, internalBinding, process) {',    '\n});'  ];  //编译模块,相当于根据模块名初始化,执行获取导出对象  NativeModule.prototype.compile = function() {    //获取脚本    var source = NativeModule.getSource(this.id);    //包裹脚本    source = NativeModule.wrap(source);    //正在加载    this.loading = true;    try {      //获取本地模块被包裹之后的匿名函数      const fn = runInThisContext(source, {        filename: this.filename,        lineOffset: 0,        displayErrors: true      });      //获取对应的require函数      const requireFn = this.id.startsWith('internal/deps/') ?        NativeModule.requireForDeps :        NativeModule.require;        //执行函数,传入当前模块实例的属性      fn(this.exports, requireFn, this, internalBinding, process);      //已加载      this.loaded = true;    } finally {      this.loading = false;    }  };  //在构造函数上缓存本地模块  NativeModule.prototype.cache = function() {    NativeModule._cache[this.id] = this;  };

1.本地模块一开始只缓存了字符串脚本

2.有一个特殊的native_module并没有对应的js文件

3.internal文件夹下模块是否可用需要根据配置决定

4.模块脚本要被包裹为一个匿名函数定义来执行

5.包裹函数参数有exports、require、module、process等等,这里解释了在模块文件中exports与module.exports不能随意混用的原因,