Angular Material串串学客户端开发 2 - Node.js模块加载机制Require()

题外话
解一下博客标题,因为第一篇文章评论中,有人质疑离题很远,说了半天和Angular Material没有半毛关系。其实我的的中心在后半句《串串学客户端开发》。

require()

不要把这里的Require()和RequireJS混为一谈。不过有意思的是,Typescript的模块定义,甚至同时支持这两种模块机制。

导入和使用外部模块,只是简单的一句require(),看看angular/material/docs下的编译文件gulpfile.js的代码片段。对模块导入和使用有个直观的感觉。

var gulp = require(‘gulp‘);
var concat = require(‘gulp-concat‘);
var fs = require(‘fs‘);

... 

//对模块gulp的使用
gulp.task(‘demos‘, function() { ...  

//对模块gulp-concat的使用
gulp.src([
    ‘node_modules/angularytics/dist/angularytics.js‘,
    ‘dist/docs/js/**/*.js‘
  ])
    .pipe(concat(‘docs.js‘))

//对模块fs的使用
fs.writeFileSync(dest + ‘/demo-data.js‘, file);

gulp.task 用于定义了一个任务;cancat用于合并文件;fs是一个对磁盘文件操作的模块。可以看出,有模块的引入,代码更为清晰而明确,这些常用模块相当于对基本语言功能的扩展。

这里,关键词require()把一切联系在一起。那么这句简单的语句背后发生了什么事情呢?

  1. require其实不是一个语言的关键词,在文章后面的研究,我们就可以看到。
  2. 还没有使用过require()或者对它实现机制不感兴趣的开发人员,可以略个这一部分。确实,后面实现机制不太影响使用。

以下大部分内容都来自原文: How require() Actually Works

因为NodeJS是开源的,我们可以追溯require()到node的核心代码中去。但是,我们找到的不是一个简单的函数,而是一个文件module.js。这个文件实现了node的整个模块加载系统。涵盖的过程有加载、编译和缓存。而我们使用的require()只是其冰上一角。

module.js

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

我们可以看到module.js首先定义了一个类型(函数)Module。这个类型有两个功能。一个,它是所有模块的基类,之后每个模块都是这个Module的一个实例。这也是我们前面探讨的module.exports最终来源。
这个Module的第二个功能就是完成Node模块的加载过程。我们使用的require()最终就是调用module.require方法,而这个方法又调用了另外一个内部方法Module._load。最终的这个load方法才是真正加载模块文件的地方,也就是我么将要分析研究的。

Module._load

Module._load = function(request, parent, isMain) {
	//1. Check Module._cache for the cached module.
	//2. Create a new module instance if cache empty
	//3. Save it to the cache
	//4. Call module.load() with your the given filename, this will call module.compile() after reading the file contents.
	//5. If there was error loading /parsing the file, delete the bad module from cache.
	//6. return module.exports
};

Module._load负责装载新模块和管理模块的缓存。缓存机制在每个模块载入时减少重复读取文件,从而提高系统性能。另外,共享模块实例还可以使得单例模块在整个项目中保留状态。
如果在缓存中没有找到该模块,Module._load就会为该文件创建一个新的Module实例。并用该实例读取文件内容,然后发送给Module._compile。
注意到在上面第6步,返回了module.exports。这个返回语句可以解释,为什么在你的模块文件中要把公开的接口(方法)赋给module.exports(或者别名exports);这也解释了,require()返回的变量,可以直接调用导入模块的方法。到此,可以看到没有任何神奇或特别的地方。

module._compile

Module.prototype._compile = function(content, filename) {
	// 1. Create the standalone require function that calls module require.
	// 2. Attach other helper methods to require.
	// 3. Wraps the JS code in a function that provides require, module, etc. variables locally to the module scope.
	// 4. Run that function.

这个方法,一开始就创建require函数,这就是我们非常熟悉的那个require()(模块装载机制不仅仅有载入模块的处理,也有导入和调用的流程,这里可以看作调用方的流程,如我们提到的main.js)。而这个个函数本身只是简单的封装了Module.require和添加了一些帮助属性和方法,如下:

  • require() 就是我们使用的require()
  • require.main 主模块
  • require.cache 所有缓存的模块
  • require.extensions 不同文件类型(后缀)的编译方法

在构建require之后,所有原文件的代码被封装的一个新函数中,这个函数把require,module,exports作为参数。 这也可以解释为什么我们可以直接调用require(),为什么exports是module.exports的别名。

(function(exports,require, module, __filename, __dirname){
	//原模块文件的所有代码注入在这
});

了解模块模式(Module Pattern)的人,很容易看出这段看时毫无意义的重新分帐,就是模块模式,就是为了防止模块文件中的定义污染系统的命名空间(记住javascript时全局变量)。

最后,这个新创建的函数直接被运行,这其实也是完整模块模式的一部分,最后那对空括弧就是运行部分:

(function(...){
	//...
})();

如果不熟悉模块模式(Module Patter),可以看看我另外一篇文章深入探索AngularJS的一个章节《模块模式 - Module Pattern》

小结

至此,我们就走完了模块加载的全部流程。从创建模块module.exports到调用require,以及调用实现的内部过程。虽然这些对你代码还没有任何影响,原来该怎么做还怎么做。但是,可以让你写同样代码时,心里更有底,不再强行记忆模块的语法。最重要的事通过学习良好的代购架构,提高自己的架构水平。

时间: 2024-10-12 23:59:39

Angular Material串串学客户端开发 2 - Node.js模块加载机制Require()的相关文章

Anuglar Material串串学客户端开发

Angular Material不仅仅有本身框架的源代码,还有在这个框架上实现的一个应用docs.更为强大的是,这个应用是真正的产品网站:就是它的官网.我有理由相信,这个网站是从源代码直接发布的,从网址的最后那个/latest,我们可以看出端倪. 从这个产品本身入手不失为学习的捷径. 入口gulpfile.js C/C#命令行的应用,我们会寻找Main()方法:C#的Web应用我们会找Global.asax:那么一个NodeJS应用我们就要找gulpfile.js. 注意:以前的很多项目都是用g

node.js 模块加载原理

来自BYVoid的<Node.js+开发指南> 2015-9-14 11:23:30 有时候我们只是想把一个对象封装到模块中,例如: //singleobject.js function Hello() { var name; this.setName = function (thyName) { name = thyName; }; this.sayHello = function () { console.log('Hello ' + name); }; }; exports.Hello

Node.js【6】Web开发、进阶(模块加载、控制流、部署、弊端)

笔记来自<Node.js开发指南>BYVoid编著 实现过程:https://github.com/ichenxiaodao/express-example 第5章 使用Node.js进行Web开发 从零开始用Node.js实现一个微博系统,功能包括路由控制.页面模板.数据库访问.用户注册.登录.用户会话等内容. 会介绍Express框架.MVC设计模式.ejs模板引擎以及MongoDB数据库的操作. 5.1.准备工作 Express(http://expressjs.com/)除了为http

Node.js 模块机制及常见面试问题解答

Node.js 模块机制采用了 Commonjs 规范,弥补了当前 JavaScript 开发大型应用没有标准的缺陷,类似于 Java 中的类文件,Python 中的 import 机制,Node.js 中可以通过 module.exports.require 来导出和引入一个模块. 在模块加载机制中,Node.js 采用了延迟加载的策略,只有在用到的情况下,系统模块才会被加载,加载完成后会放到 binding_cache 中. 面试指南 require的加载机制?,参考:模块加载机制 modu

Node.js模块与npm包管理工具

在Nodejs中,一模块为单位划分所有功能,并且提供了一个完整的模块加载机制. 模块在Nodejs中是一个非常重要的概念,它允许我们将第三方类库引入我们的应用程序中. 在Nodejs中你可以编写或引入几种模块文件: 1)  后缀名为.js的js脚本文件. 2)  后缀名为.json的JSON文本文件. 3)  后缀名为.node的经过编译的二进制模块文件. 在模块外部访问模块内的成员有两种方式: 1.  使用exports对象将对象暴露出去.例如:exports.msg=msg;就将msg暴露出

Node.js入门:事件机制

Evented I/O for V8 JavaScript 基于V8引擎实现的事件驱动IO. 事件机制的实现 Node.js中大部分的模块,都继承自Event模块(http://nodejs.org/docs/latest/api/events.html ).Event模块(events.EventEmitter)是一个简单的事件监听器模式的实现.具有addListener/on,once,removeListener,removeAllListeners,emit等基本的事件监听模式的方法实现

网页前端开发,对于图片慢加载简介

http://www.cnblogs.com/qingseyuandi/p/loadingLater.html 网页前端的图片慢加载给网页显示的成本降低了不少,因此我在这边简单的介绍一下慢加载的一个技术原理,希望能起到抛砖引玉的作用,写的不好的地方希望指正,谢谢~~   技术背景 现在的网页系统,对于一些对图片资源比较多,并且一次性无法浏览完整个网页的情况下,图片慢加载可以提高客户端的体验,如IT大头:淘宝,网易,新浪等等...  技术原理   技术说穿了其实也就那么回事,懂了就简单了,呵呵~

【译】深入理解python3.4中Asyncio库与Node.js的异步IO机制

转载自http://xidui.github.io/2015/10/29/%E6%B7%B1%E5%85%A5%E7%90%86%E8%A7%A3python3-4-Asyncio%E5%BA%93%E4%B8%8ENode-js%E7%9A%84%E5%BC%82%E6%AD%A5IO%E6%9C%BA%E5%88%B6/ 译者:xidui原文: http://sahandsaba.com/understanding-asyncio-node-js-python-3-4.html 译者前言 如

Developer - 如何自我保证Node.js模块质量

组里正在做SaaS产品,其中一些模块(Module)是Node.js实现,这里我们主要使用Node.js实现Web Server来提供服务. 在做SaaS项目之前,组里的开发模式是传统的Deverloper + QA的模式,这是传统的协作模式,Developer负责写代码开发,当然也会有基本的自测,QA负责测试,遇到问题,提Bug给Developer去修复,Developer修复Bug后,由QA来验证并记录Bug.但这样的协作模式已不适合SaaS产品的开发,SaaS产品更新迭代快,模块众多,这就