你知道require是什么吗?

引题

用过node的同学应该都知道require是用来加载模块的,那你是否存在如下的疑问呢?

1. require(path)是如何依据path找到对应module呢?

2. 为何在模块定义中,一定要通过module.exports暴漏出接口?module.exports与require存在什么关系

对上述问题进行概括可以抽象出如下两个问题:

1. module的路径分析
2. 文件加载

切入

   首先来直观地看看require是什么? 

// node环境下执行:
console.log(require.toString)

//输入结果为:
‘function require(path) {\n    return self.require(path);\n  }‘

  上述代码说明require函数仅仅是module.require的封装,这样就需要查看node中的module源代码了。

加载模块的方式

首先来直观来认识一下node的模块加载方式有哪些方式:

case 1:

// ‘path‘为node的核心模块var path = require(‘path‘)

case2:

// a.js,路径为: basePath/a.js
var myModule = require(‘./my-module‘)
// my-module的路径为basePath/node_modules/myModule.js

case 3:

// a.js, 路径: basePath/a.js
var main = require(‘./‘)
// basePath下还包括package.json, index.js

路径解析

在node的官方API中,我们可以找到这段描述:

To get the exact filename that will be loaded when require() is called, use the require.resolve() function.

Putting together all of the above, here is the high-level algorithm in pseudocode of what require.resolve does:

......

试试在node环境下用用require.resolve这个API:

require.resolve(‘./a.js‘)
// 这样就得到a.js的绝对路径

为了探索缘由,就从node核心代码中的mdoule.js找答案吧:

require.resolve = function(request) {
  return Module._resolveFilename(request, self);
}

Module._resolveFilename = function(request, parent) {
  // 判断是否为node的核心模块
  if (NativeModule.exists(request)) {
     return request;
   }
   // 得到查询路径,格式为数组:[id, [paths]]
   var resolvedModule =  Module._resolveLookupPaths(request, parent);	  	                 

   var paths = resolvedModule[1];
  // 根据path、fileName得到绝对路径
  var filename = Module._findPath(request, paths);
   return filename;
}

那Module._resolveLookupPaths是如何得到所有查询路径的呢?

  1. 为node的核心模块,stop
  2. 以./或../开头,本地查找, stop
  3. 沿着文件树,得到node_module的所有路径,直到/node_modules,在node_module中查找,stop
  4. path为目录,则检查package.json文件是否存在main属性,否则默认为index.js
  5. 最后返回new Error(‘Cannot find module"‘ + request + ‘"‘);

模块加载

先看require的源代码:

// 我们经常使用的require函数
function require(path) {
    return self.require(path);
}
// 调用_load函数,加载所需的模块
Module.prototype.require = function(path) {
  return Module._load(path, this);
}

这样模块函数的调用连接到了Module._load函数:

Module.cache = {};
Module._load = function() {
  // 检测模块是否已经加载过
  var cachedModule = Module._cache[filename];
   if (cachedModule) {
     return cachedModule.exports;
   }
   // 模块还未加载,则为模块创建module实例
   var module = new Module(filename, parent);
  // 新创建的实例存储于cache中
   Module._cache[filename] = module;
   // 开始获取模块的内容
   module.load(filename);
   // 对外提供接口
   return module.exports;
}

  接下来问题的关键就变成了module.load,该方法用于获取module的内容,然后进行解析:

Module.prototype.load = function(filename) {
  // 解析出文件的后缀, 存在[‘.js‘, ‘.json‘, ‘node‘]三种后缀
  var extension = path.extname(filename) || ‘.js‘;
  // 根据后缀,获取相关的模块
  Module._extensions[extension](this, filename);
}

  node会匹配按照.js、.json、.node三种格式进行模块匹配,根据文件类型的不同采取不同的加载策略,但是以实际开发中以加载.js最多,该种策略最后需要调用Module.prototype._compile进行编译处理:

Module._extensions[‘.js‘] = function(module, filename) {
  var content = fs.readFileSync(filename, ‘utf8‘);
  module._compile(stripBOM(content), filename);
};

Module.prototype._compile = function(content, filename) {
  //将内容放入到(function() { content }),形成闭包,创建私有作用域
  var wrapper = Module.wrap(content);
  // bind新的执行上下文
  var compiledWrapper = runInThisContext(wrapper, { filename: filename });
  // 向外暴漏接口:module.exports, require, module,__filename,  __dirname,
  var args = [self.exports, require, self, filename, dirname];
   return compiledWrapper.apply(self.exports, args);
}

  这样,我们就可以在require来获取相应地module。

结论
      node现在这么火,各种优势铺天盖地涌来,会让刚刚入行的人觉得深不可测,因而往往会让人望而却步。但是只要我们敢于突破第一步,深入下来仔细分析,就会发现其实没有那么晦涩难懂,踏出第一步真的很关键!

时间: 2024-10-11 04:36:34

你知道require是什么吗?的相关文章

php include require

includ和require都是把其他页面加载当前页面,不过不同的是,require如果没有找到,或者所包含的页面里面有错误就不会往下执行了 <?php include 'form.php'; require 'aasdfa.php'; ?> form.php 存在所以没有问题 aasdfa.php没有所以报错了 Warning: require(aasdfa.php): failed to open stream: No such file or directory in D:\phpStu

php include include_once require require_once 的区别与联系

一.require 与 include 的区别: The require() function is identical to include(), except that it handles errors differently. If an error occurs, the include() function generates a warning, but the script will continue execution. The require() generates a fa

require.js的简单使用

<script src="js/require.js"></script> <script src="js/require.js" data-main="js/main"></script> require(['jquery', 'underscore', 'backbone'], function ($, _, Backbone){ // some code here }); 使用require.

Angular学习心得之directive——require选项的细节

谈require选项之前,应该先说说controller选项,controller选项允许指令对其他指令提供一个类似接口的功能,只要别的指令(甚至是自己)有需要,就可以获取该controller,将其作为一个对象,并取得其中的所有内容.而require就是连接两个指令的锁链,它可以选择性地获取指令中已经定义好的controller,并作为link函数的第四个参数传递进去,link函数的四个参数分别为scope,element,attr和someCtrl,最后一个就是通过require获取的con

avalon学习笔记ui篇-如何将avalon默认的amd模型禁止,以及用require重写。

一.如何禁止avalon自带的amd模型 1.采用avalon.shim.js这个文件,这个文件删除了原本自带的amd模型,不需要手动删除,修改. 2.打开avalon.js这个文件,搜索avalon.config,将true改为false. 二,下载text.js和css.js 1.因为avalonUI依赖了html文件和css文件. 2.并且将text.js和css.js,在配置中预加载 priority:['text','css'] 三.完整配置项 require.config({ //b

Exported activity does not require permission

根据android资料解释,如题所示的警告:Exported activity does not require permission意味着:在一个应用程序中添加了多个antivity后,是因为在Activity中添加了intent-filter属性. 上述这个属性的添加意味着,该Activity已经暴露给了不同进程的应用.也就是说,其它的应用程序不需要任何权限就可以自由的实例化该Activity.显然,如果不是有特殊需求,没人会希望自己写得应用程序会有这么个隐患. 解决方法之一是在Activi

使用模块化思维和模板引擎搭建前端架构(require+underscore)

require.js 介绍: 是一个非常小巧的JavaScript模块载入框架,是AMD规范最好的实现者之一.最新版本的RequireJS压缩后只有14K,堪称非常轻量. 官网:http://www.requirejs.cn/    (PS:如果没接触过,刚开始看都是一头蒙蔽的,建议看下菜鸟教程) 新手教程:http://www.runoob.com/w3cnote/requirejs-tutorial-1.html 优点:可完成团队协作.模块复用.单元测试等等一系列复杂的需求 undersco

require js

Require原理 在require中,根据AMD(Asynchronous Module Definition)的思想,即异步模块加载机制,其思想就是把代码分为一个一个的模块来分块加载,这样无疑可以提高代码的重用. 在整个require中,主要的方法就两个:require和define,我们先来聊聊require require作为主函数来引入我们的"模块",require会从自身的存储中去查找对应的defined模块,如果没有找到,则这时这个模块有可以存在三种状态:loading,

Nodejs的模块系统以及require的机制

一.简介 Nodejs 有一个简单的模块加载系统.在 Nodejs 中,文件和模块是一一对应的(每个文件被视为一个独立的模块),这个文件可能是 JavaScript 代码,JSON 或编译过的C/C++ 扩展,例如: /** *foo.js *将这个js文件导出为模块 */ exports.hello = function() { console.log("hello Nodejs!"); } /** *main.js *main.js和foo.js在同一目录下 *在控制台中将会输出:

require.js JQ

require.js和sea.js的作用都是一样的. 为了解决两大问题,第一实现js文件的异步加载,避免网页失去响应,第二管理模块之间的依赖性. 基本的模板 define(function(require,exports,module){ exports.getStyle = function (obj,name){ //你初始的模块 } }) define(function(require,exports,module){ var get = require('get');//引入初始模块(基