`
jgnan
  • 浏览: 88890 次
  • 性别: Icon_minigender_1
  • 来自: 广州
社区版块
存档分类
最新评论

nodejs之旅,模块机制

阅读更多
昨晚睡觉前纠结了一下,在考虑之后这个系列应该怎么写下去,我发现现在学习nodejs的难点就是官网没有提供一些简单的tutorial,只是提供了api的接口说明和一些示例代码,这让我们这些初学者在刚看的时候的确是丈二和尚摸不着头脑。所以我决定了今后的章节我会凭借我个人对于接口的理解进行比较合理的编排,来便于大家对于整个node的接口有一个系统的了解吧。

首先我们先来说说modules机制。先说这个模块的API有两个原因吧,一个是这个模块我算是比较熟悉,另外一个原因是我觉得大家对这个模块了解清楚了的话,就可以自己去翻其他插件的源代码了。为什么我会对这个模块比较熟呢?其实我一直都很喜欢一个叫dojotoolkit的javascript框架,我也知道google曾经有一段时间在使用这套框架来构件他们的GWT这套mvc框架。dojo是最早提出了javascript模块化管理这个概念的,而其后这台机制在commonjs的模块接口中得以规范化。所以node的这一块如果我的猜测没错的话也是这样一路走过来的。

简单示例
那究竟node的模块管理是怎么实现的呢?首先我们先来看一个模块调用的示例:
loadModuleExample.js
//加载全局模块http
var http = require("http");

//加载当前目录下的duowan.js模块里面的myname
var myname = require("./duowan").myname

//加载绝对路径/node/modules下面的common.js模块
var common = require("/node/modules/common")


上面的例子里我们说明了三个事情:
  • 通过require(moduleName)我们可以加载其它的模块
  • 如果模块名不含有/或者或者./就会去加载通用模块,如果带有./或者../则按照相对路径规则从当前文件所在文件夹开始寻找模块,如果以/开头则从系统根目录开始找
  • 模块引入后可以把整个模块赋予到一个变量中,也可以直接暴露模块中的某个对象或者属性或者方法。其实这点和javascript一样。


模块文件类型
node可以加载的文件类型有三种:.js,  .json, .node
.js就是文本格式的javascript文件,而.json则是json格式对象,这两者加载的时候被视为javascript被动态编译和处理使用,而.node则是预编译好的插件模块,会在以后说plugin的时候说明。

核心模块和文件模块
需要补充说明一下的是惯于上面的第二点说法中关于全局模块的说明。这个说明其实不太正确。下面我们就按照官方API的说明来正确地解释一下当你的模块名里面不带有./或者../时会怎么去找包:

首先,node会最优先加载核心模块。所谓的核心模块存在于node安装目录下的lib目录里面,他们的加载是在node容器初始化的时候就加载完成的,不需要在加载过程中进行编译,例如http这个模块。另外这些模块不会被覆盖,关于这点下面会有额外说明。

如果加载的不是核心模块,则会从当前目录寻找node_module里面的同名模块;如果当前目录没有该模块,则往上一级目录寻找node_module中的同名模块;一直找到系统根目录的node_module中有同名模块为止,没有则会抛出一个MODULE_NOT_FOUND异常。例如有以下的目录结构:
/
|_ node_module
|          |_ common.js
|_ app
       |_ node_module
       |           |_ http.js
       |           |_ hello.js
       |_ index.js

如果index.js里面require("hello.js"),则加载的将会是/app/node_module/hello.js这个文件。如果加载的是require("common.js"),则会加载/node_module/common.js这个文件。但是如果require("http"),加载的将是核心模块http,而不是/app/node_module/http.js这个文件,因为核心模块不允许被重写。

目录模块
除了上述两种模块以外,node还允许你使用目录模块。
例如有以下目录结构:
/ app
       |_ myModules
       |           |_ index.js
       |_ index.js
如果我们在index.js里面引入require("./myModules"),则会加载/app/myModules/index.js。如果我们引入的是一个目录而不是一个文件,node会尝试加载这个目录下面的index.js或者index.node文件。

但是如果你在目录下定一个叫package.json文件,那么你就可以改变这一默认模块加载行为。在这个文件里面可以保存一个json对象,而里面最关键的一个属性main可以让你定义这个目录下的哪个文件是模块的主文件。例如以下的目录结构中:
/app
       |_ myModules
       |           |_ package.json
       |           |_ myname.js
       |           |_ index.js
       |_ index.js

其中packages.json定义如下:
{main:"./myname.js"}


如果我们在index.js中require("./myModules"),则会加载/app/myModules/myname.js,而不是/app/myModules/index.js

当然这个package.json可不单单能够做这个事情,里面还能够定义很多像依赖管理之类的事情。由于这里是基础篇,我就先不在这里展开讨论了,等之后我们遇到相关的场景再作介绍吧。

模块缓存
对于node来说,你在主进程加载过一次的模块,就会被永久缓存起来。即使你修改过模块文件的内容,然后再在主进程尝试调用require进行重新加载,但是你会发现最后还是拿到了第一次加载的那个对象。只能通过重启node,或者通过删除保存在require.cache中的模块缓存对象,才能够重新加载模块内容。

暴露模块属性
如果我们引入一个模块,其实node就是简单地在当前环境调用某个模块文件并且解释一遍。例如下面的例子:
文件名: hello.js
console.log("Hello World!");


文件名: test.js
var hello = require("./hello");
console.log(hello);


我们执行node test你会发现直接打印出Hello World!来。而第二行则会打印出一个空对象。

但是如果我们希望暴露出模块中的一些方法或对象供外部调用者使用,那应该怎么做呢?很简单,就是利用给module.exports赋值。我们来修改一下我们的 hello.js:
文件名: hello.js
module.exports={
    "name":"Justice",
    "myName":function(){
        return this.name;
    }
}


然后再次执行node test,我们可以看到输出了一个{ name: 'Justice', myName: [Function] }对象。

值得注意的是exports的定义必须是在主进程里面完成,不可以延迟加载,即以下代码中,timeout部分无效:
setTimeout(function(){
    module.exports = {"a":"Test"};
},50);

原因我猜测是因为node只会在调用require那一刹那对脚本进行解释,但是setTimeout会在主线程完成工作以后才会调用,那个时候由于module已经被编译好并且缓存下来了,所以是无法生效的。

最后提出一些小建议。大家想下如果我在一个模块中给module.exports赋值两遍会有什么后果呢?看看我们的hello.js的又一次改造版本:
文件名: hello.js
module.exports={
    "name":"Justice",
    "myName":function(){
        return this.name;
    }
}

module.exports={"name":"Moc"}

无容置疑,最后模块导出的就只有{"name":"Moc"}这个对象了。所以如果大家要调用module.exports,最好放到你的文件尾部来调用,以防止这种覆盖问题。

module的其它API
module的API还有以下的属性:
module.id = 模块的ID,通常是当前模块文件路径,含文件名
module.filename = 当前模块文件路径,含文件名
module.loaded = 判断模块当前是否已加载
module.parent = 加载当前脚本的模块对象。
module.children = 当前模块加载的模块对象集合,是一个数组

未完成的内容
说过了这里作为基础篇,我就暂时不作进阶话题的讨论,这章里面还有以下的一些问题没有解决的,请同学们自己去思考一下,我以后有机会也会补充进来:
  • 模块间的互相引用。例如a引用b, b又引用a会怎么办? node的官网文档中有这个问题的解决方案详述,http://www.nodejs.org/api/modules.html#modules_cycles
  • 闭包。如刚刚我们看到的一样,我们的hello.js里面的myName方法返回了this.name,这里马上就涉及到了javascript中一个坑爹的主题:闭包问题。究竟node的这套闭包机制是怎么回事呢?大家有兴趣的话可以自己做做试验。我发现node其实并不存在闭包这一说,它的this就是指exports的赋值对象。不过很危险的事情是你可以直接在外部修改这个exports对象,而且这些对exports对象的修改是可以互相影响的。这是一件很可怕的事情,所以我建议大家最好不要在外部对已加载模块的内容进行修改,否则出问题的时候就会很难调试了。


官方网址:http://www.nodejs.org/api/modules.html
好了,今天就到此为止了,之后我要先考虑下下一个模块写什么比较好。多些各位收看。
分享到:
评论

相关推荐

    NodeJS入门

    Node.js入门:开启服务器端JavaScript之旅 Node.js是一款基于Chrome V8引擎的JavaScript运行环境,由Ryan Dahl在2009年创立。它的出现打破了JavaScript只能在浏览器端使用的局限,让开发者可以用熟悉的JavaScript...

    NodeJS入门手册和64bit安装EXE

    NodeJS是一种基于Chrome V8引擎的JavaScript运行环境,它的出现使得JavaScript可以被用于服务器端编程,打破了以往...通过这个入门手册和安装程序,你将能够开始你的NodeJS之旅,探索这个充满潜力的后端世界。

    深入浅出Nodejs

    《深入浅出Node.js》这本书是Node.js领域的经典之作,旨在帮助读者全面理解并熟练掌握Node.js技术。...通过阅读本书,你将能够系统地学习Node.js技术,掌握服务器端JavaScript开发,开启你的全栈开发之旅。

    NodeJs.rar

    Node.js是一个基于Chrome V8引擎的JavaScript运行环境,它让JavaScript得以在服务器端执行,打破了...通过"NodeJs.rar"这个安装包,你可以轻松地在本地搭建起Node.js环境,开始你的JavaScript服务器端编程之旅。

    nodejs+mysql入门学习.zip

    通过这个"nodejs+mysql入门学习"教程,你将掌握如何在Node.js应用中与MySQL数据库进行交互,从而开启你的Web开发之旅。在实际项目中,你还可以探索ORM(对象关系映射)框架,如Sequelize或TypeORM,以简化数据库操作...

    read-nodejs-code:nodejs原始解析

    在本文中,我们将深入探讨"read-nodejs-code:nodejs原始解析"这一主题,这是关于理解和解析Node.js源代码的一个项目。Node.js是一款基于Chrome...对于任何想要成为Node.js专家的人来说,这都是一次必不可少的探索之旅。

    nodejs-20210315_sanyasavva

    《Node.js 20210315_sanyasavva:JavaScript编程之旅》 在编程领域,Node.js以其高效、跨平台的特性成为构建网络应用的强大工具。本资源包"nodejs-20210315_sanyasavva"是针对Node.js的实践教程,旨在帮助学习者...

    fundamentos-nodejs:火箭座位点燃Trilha NodeJS-Módulo1

    "火箭座位点燃"可能是一个比喻,意味着这个模块将迅速启动你的Node.js学习之旅,让你快速进入开发状态。 【描述】中的内容与标题相同,再次确认了这是Node.js入门学习的模块1。"Trilha"在葡萄牙语中是"轨迹"或"路线...

    curso-backend-nodejs

    总的来说,"curso-backend-nodejs"课程是一次全面的Node.js后端开发之旅,无论你是初学者还是有经验的开发者,都能从中获得宝贵的知识和实践经验。通过学习,你将具备独立构建高效、稳定、可扩展的Node.js后端应用的...

    NodejsExtraCredit281:NodeJS 额外学分

    NodeJS 额外学分:深入理解与实践 Node.js 是一个基于 Chrome V8 引擎的...现在,你可以打开提供的 NodejsExtraCredit281-master 压缩包,开始你的学习之旅,通过阅读源代码和实践项目,加深对 Node.js 的理解和应用。

    first-nodejs-project:这是我在Github中的第一个NodeJS项目。

    【Node.js基础概念】 Node.js是一个开放源代码、跨...总的来说,"first-nodejs-project"是一个很好的起点,通过它你可以开启Node.js的探索之旅,逐步掌握服务器端JavaScript开发,建立起从前端到后端的全栈开发能力。

    Nodejs_tutorial

    Node.js 是一个基于 Chrome V8 引擎的 JavaScript 运行环境...开始你的 Node.js 学习之旅吧,不断探索、实践,提升自己的技能。记得查看 "Nodejs_tutorial-Master" 压缩包中的资料,它们将为你提供更具体的示例和练习。

    node-v16.18.0-win-x64.zip

    "node-v16.18.0-win-x64.zip"是针对Windows 64位系统用户的一个便捷安装包,下载解压后,用户可以通过简单的命令行操作开始他们的Node.js开发之旅。无论是初学者还是经验丰富的开发者,都能从中受益,构建出高效、可...

    First-Nodejs

    【标题】"First-Nodejs" 是一个关于初学者使用Node.js进行服务器开发的项目,旨在帮助新手迈入...在"First-Nodejs"项目中,你将一步步学习这些知识,最终实现创建并运行自己的Node.js服务器,开启你的后端开发之旅。

    AviaturAPI-NodeJs:节点js项目

    【标签】:“JavaScript”表明项目的主要编程语言是JavaScript,这是Web开发中最常用的语言之一,尤其在前端领域,但通过Node.js,JavaScript也可以用于服务器端开发。 【详细知识点】: 1. **Node.js**:Node.js...

    -Deprecated-introduccion-a-nodejs:教训

    在开始"Deprecacion Introduction to Node.js"的学习之旅时,你可能会学到如何识别和处理弃用警告,了解新版本中引入的变化,以及如何优雅地迁移代码。同时,也会涉及到错误处理和调试技巧,这些都是Node.js开发中的...

    前端大厂最新面试题-2021.docx

    1. 滴滴面试尴尬+愉快之旅 字节跳动真题解析 1. 2021前端字节跳动真题解析 前端面试知识点 1. 前端面试知识点 基础知识 JavaScript 1. 达达前端个人 web 分享 92 道 JavaScript 面试题附加回答 2. 小白理解 ...

    Node.js安装、配置及开发环境搭建教程

    希望本文能为您提供足够的指导和支持,让您顺利踏上Node.js开发之旅。 #### 参考文献/资源链接 - [Node.js官方文档](https://nodejs.org/en/docs/) - [npm官方文档](https://docs.npmjs.com/) - [nvm GitHub仓库]...

Global site tag (gtag.js) - Google Analytics