commonJS规范

模块引用

你可以通过require来引入你所需要的模块,这个方法接收模块标识,以此引入一个模块的API到当前上下文中。

const fs = require('fs');

模块定义

一个模块就是一个文件,文件中你可以定义或处理任何方法,但你必须要有一个出口, 用于导出你想要暴露的方法,上下文提供了exports对象就是用来导出的,它也是唯一的出口。在模块中,还存在一个module对象,它代表模块本身,而exportsmodule的属性。

// common.js
function add(a, b) {return a + b;
}
exports.add = add;
​
// index.js
const common = require('./common');
console.log(common.add(1, 2));   // 3

模块标识

模块标识是传递给require的参数,它必须是符合小驼峰命名的字符串,当然也可以是以"./"、"../"开头的相对路径或绝对路径。如果是以.js.json为后缀的文件,在引入时可以省略后缀,就像上面的例子一样。
每个模块之间是相对独立的,他们互不影响,引入多个模块也不必担心变量污染。

欢迎访问我的个人网站:www.dengzhanyong.com
个人公众号:【前端筱园】

Node中的模块

通过上面的介绍可以知道,通过moduleexportsrequire可以很方便的实现不同模块之间的相互穿插使用,但在node中,并非完全按照此规范实现,而是对模块进行了一定的取舍,并增加了一些自身的特性。
node在引入模块时会经历以下阶段:
(1)路径分析
(2)文件定位
(3)编译执行
在node中模块可以分为两大类,一类是node自身提供的(如:bufferfshttp等),称为核心模块;另外一类是用户自己编写的,称为文件模块;
在开发中,如果我们想用到一些第三方的插件时,在引入前,必须要先进行安装,然后才可以引入使用;

npm install md5

而使用node的核心模块时,只需要引入即可,这是因为核心模块在node源代码的编译过程中,编译进了二进制执行文件,当node进程启动时,部分核心模块就被直接加载进了内存中。

模块的加载过程

node在加载模块时会优先从缓存中加载,任何模块在第一次被引入后就会被缓存起来,当第二次引入时,会优先从缓存加载,与前端浏览器的缓存文件一样以提高性能。不同的是浏览器仅仅缓存文件,而node缓存的是编译和执行后的对象。

路径分析和文件定位

路径分析就是查找模块所在的路径,由于标识符的形式有多种,因此针对不用形式的标识符在查找和定位上有不同程度的差异。

核心模块

核心模块加载的优先级仅次于缓存加载,其加载速度是最快的,因为这些模块在node源代码的编译过程中就已经编译为二进制文件。如果我们自己编写一个模块,取名为与核心模块相同(如:fs),当我们去引入时自然是不会成功的,除非换成其他的标识符形式引入。
路径形式的文件模块

以…/、./或/开始的标识符,在分析路径模块时,require()方法会将路径转为真实路径,并以真实路径作为索引。由于文件模块知道了文件的位置,因此加载速度也是比较快的,仅次于核心模块。

自定义模块

自定义模块是一类特殊的文件模块,它可能是一个文件或者包的形式,这类模块的查找是最费时的,也是最慢的一种。node在查找模块时按照模块路径的查找策略,有点类似于JavaScript的原型链一样,逐级向上查找,直到顶级为止,看下面这个例子:

console.log(module.paths);

运行后输出结果如下:

结果是一个数组,node会按照数组中所列的路径顺序去查找你所需要的模块,在我们安装一些第三方包的时候,该包会默认安装在当前目录下的node_modules中,也就是数组中的第一项,虽然这种方式最慢,node还是尽可能的在允许的范围内做到做好。

文件扩展名

CommonJS模块规范允许我们在引入模块时不添加模块文件的扩展名,如require('test.js')可以写成require('test');Node会按照.js.node.json的顺序依次补全尝试。
因为node是单线程的,这个过程是同步阻塞的,会引起性能问题,因此建议在引入.node或.json文件时带上扩展名。

编译模块

在node中,每个文件模块都是一个对象,它的定义如下:

对于不同扩展名的文件模块,在载入方式上有所不同:

.js:通过fs模块同步读取文件后编译执行
.node:用C/C++编写的扩展文件,通过dlopen()方法加载编译后生成的文件
.json:通过fs模块同步读取文件后,用JSON.parse()解析返回结果
其他文件:统一按照.js文件处理
每个编译成功后的模块都会将其文件路径作为索引,缓存在Module._cache对象上,提到二次加载的速度。

JavaScript模块编译

在CommonJS模块规范中,我们可以直接使用变量requireexportsmodule,还有__filename__dirname这两个变量,这些变量我们并没有定义。他们是在编译的过程中,Node对JavaScript文件的内容进行了头尾包装。在头部加上了(function(exports, require, module, __filename, __dirname) {,在尾部加上了})。就像下面这样:

(function(exports, require, module, __filename, __dirname) {//模块文件内容
})

这样做还有一个好处,每个模块之间是相互独立的,不会引起变量污染。

核心模块的编译过程

node在编译核心模块时,首先把JavaScript代码转存为C/C++代码,采用V8附带的js2c.py工具,转成node_natives.h头文件。
在这个过程中,JavaScript代码以字符串的形式存储在node命名空间中,是不能直接执行的。在启动node进程时,JavaScript代码直接加载进内存中。在加载过程中,JavaScript核心模块经历标识符分析后直接定位到内存中,比普通文件模块查找要快很多。
lib目录下的所有模块文件也是没有定义requiremoduleexports这些变量的。在引入核心模块的过程中,也经历了头尾包装的过程,然后才执行和导出了exports对象。

包与NPM

虽然有了node的核心模块,我们也可以自定义模块,但还是存在两个问题:
自己的模块只能够自己使用
模块与模块之间比较零散,相互之间不能直接使用

包与NPM就是将模块联系起来的一种机制,在模块的基础上进一步组织JavaScript代码。
一个符合CommonJS规范的包目录应该包含以下文件:

  • package.json:包描述文件
  • bin:存放可执行二进制文件的目录
  • lib:存放JavaScript代码的目录
  • doc:存放文档的目录
  • test:存放单元测试用例的代码

package.json

用于表达代码的相关信息,它是一个JSON格式的文件,位于包的根目录下,是包的重要组成部分。CommonJS定义了一些必须的字段。

  • name:包名
  • description:包的简要介绍
  • version:包的当前版本,每次更新包时需要要更新版本号,通常为major.minor.revision的格式
  • keywords:包的关键词,使用数组的形式,有利于用户搜索到你的包
  • maintainers:报的维护者列表,为一个数组。每个维护者有nameemailweb属性组成。
  • contributors:贡献者列表。
  • bugs:一个可以反馈bug的地址,可以是网址地址或邮件地址。
  • licenses:当前包所使用的许可列表。
  • repositories:托管代码的位置列表。
  • dependencies:使用当前包所需要依赖的包列表。
  • homepage:当前包的网站地址。
  • os:操作系统支持列表。
  • cpu:CPU架构的支持列表。
  • engine:支持的JavaScript引擎列表。
  • builtin:标志当前包是否是内建在底层系统的标准组件。
  • directories:包目录说明。
  • implements:实现规范的列表。
  • scripts:脚本说明对象。
    下面是很受欢迎的KOA项目的package.json文件,可以对应着参考:
{"_args": [[
"koa@2.11.0",
"E:\\streamax\\workspace\\dev-ops-web"]],
"_from": "koa@2.11.0",
"_id": "koa@2.11.0",
"_inBundle": false,
"_integrity": "sha1-/lpRxG9WbSdjLdXcj9XX3UT5NaQ=",
"_location": "/koa",
"_phantomChildren": {},
"_requested": {"type": "version",
"registry": true,
"raw": "koa@2.11.0",
"name": "koa",
"escapedName": "koa",
"rawSpec": "2.11.0",
"saveSpec": null,
"fetchSpec": "2.11.0"},
"_requiredBy": [
"/@streamax/sword"],
"_resolved": "http://192.168.150.51:4873/koa/-/koa-2.11.0.tgz",
"_spec": "2.11.0",
"_where": "E:\\streamax\\workspace\\dev-ops-web",
"bugs": {"url": "https://github.com/koajs/koa/issues"},
"dependencies": {"accepts": "^1.3.5",
"cache-content-type": "^1.0.0",
"content-disposition": "~0.5.2",
"content-type": "^1.0.4",
"cookies": "~0.8.0",
"debug": "~3.1.0",
"delegates": "^1.0.0",
"depd": "^1.1.2",
"destroy": "^1.0.4",
"encodeurl": "^1.0.2",
"error-inject": "^1.0.0",
"escape-html": "^1.0.3",
"fresh": "~0.5.2",
"http-assert": "^1.3.0",
"http-errors": "^1.6.3",
"is-generator-function": "^1.0.7",
"koa-compose": "^4.1.0",
"koa-convert": "^1.2.0",
"on-finished": "^2.3.0",
"only": "~0.0.2",
"parseurl": "^1.3.2",
"statuses": "^1.5.0",
"type-is": "^1.6.16",
"vary": "^1.1.2"},
"description": "Koa web app framework",
"devDependencies": {"egg-bin": "^4.13.0",
"eslint": "^6.5.1",
"eslint-config-koa": "^2.0.0",
"eslint-config-standard": "^14.1.0",
"eslint-plugin-import": "^2.18.2",
"eslint-plugin-node": "^10.0.0",
"eslint-plugin-promise": "^4.2.1",
"eslint-plugin-standard": "^4.0.1",
"mm": "^2.5.0",
"supertest": "^3.1.0"},
"engines": {"node": "^4.8.4 || ^6.10.1 || ^7.10.1 || >= 8.1.4"},
"files": [
"lib"],
"homepage": "https://github.com/koajs/koa#readme",
"keywords": [
"web",
"app",
"http",
"application",
"framework",
"middleware",
"rack"],
"license": "MIT",
"main": "lib/application.js",
"name": "koa",
"repository": {"type": "git",
"url": "git+https://github.com/koajs/koa.git"},
"scripts": {"authors": "git log --format='%aN <%aE>' | sort -u > AUTHORS",
"bench": "make -C benchmarks",
"lint": "eslint benchmarks lib test",
"test": "egg-bin test test",
"test-cov": "egg-bin cov test"},
"version": "2.11.0"
}

NPM常用功能

对于node而言,NPM帮助完成了第三方模块的发布、安装和依赖等。借助NPM、Node与第三方模块之前形成了一个很好的生态系统。

查看npm版本:

$npm -v

安装依赖包:

$npm install 包名

执行完上面的命令后,NPM会在当前目录下新建一个node_modules的文件夹,你所安装的包就会被下载到这个文件夹中。

你也可以进行全局安装,只需要加上参数-g即可:

$npm install 包名 -g

你如果想把当前安装的包记录到package.json中,可以加入–save参数

$npm install 包名 --save

加到package.json的好处是,你在上传项目到git上时,默认是不会上传第三方包的,毕竟很浪费资源,当他人pull你的项目时,可以根据package.json中记录的所依赖了哪些包,通过npm install进行安装。

如果你想从非官方源安装,可以通过镜像源安装。

$npm install underscore --registry=http://registry.url

你如果大多数使用的包都采用镜像源安装,可以通过命令来设置默认源:

$npm config set registry http://registry.url

可以通过uninstall来写在一个已安装的包:

$npm uninstall 包名

JavaScript模块机制相关推荐

  1. [Nodejs学习之旅2-1] 模块机制

    CommonJs规范 在介绍Nodejs的模块机制之前,我们得先了解一下CommonJS规范.因为Node应用是由模块组合而成,像我们经常会用到的underscore.loadash.axios等都是 ...

  2. 若要加载模块二进制_春哥说 | 浅谈NodeJs的模块机制-2

    ★ 目录 ★ 01 Node的模块实现概述 02 优先从缓存中加载 03 路径分析和文件定位 Node的模块实现概述 Nodejs再集成CommonJs的模块机制的规范时进行了取舍,同时增加了特性. ...

  3. javascript 模块化机制

    1. 概述 js发展初期暴露了其缺陷:缺乏模块,后来提出了commonJS规范来规范其模块的规范.作为JavaScript新手,发现对于其JavaScript的模块机制,不是很理解.我查阅了一些资料整 ...

  4. Typescript学习笔记(五) 模块机制

    javascript从es5之前都缺少一种模块机制,无法通过js引入文件,于是requirejs等等的加载器应运而生.这些加载器的使用也并不统一,产生了amd,commonjs,umd等等的规范,各有 ...

  5. JavaScript模块

    在JavaScript编程中我们用的很多的一个场景就是写模块.可以看成一个简单的封装或者是一个类库的开始,有哪些形式呢,先来一个简单的模块. 简单模块 var foo = (function() {v ...

  6. JavaScript模块打包器rollup

    学习资料:拉勾课程<大前端高薪训练营> 阅读建议:搭配文章的侧边栏目录进行食用,体验会更佳哦. 内容说明:本文不做知识点的搬运工,技术详情请查看官方文档. 一:认识rollup rollu ...

  7. 傻傻分不清的javascript运行机制

    学习到javascript的运行机制时,有几个概念经常出现在各种文章中且容易混淆.Execution Context(执行环境或执行上下文),Context Stack (执行栈),Variable ...

  8. javascript模块_JavaScript模块第2部分:模块捆绑

    javascript模块 by Preethi Kasireddy 通过Preethi Kasireddy JavaScript模块第2部分:模块捆绑 (JavaScript Modules Part ...

  9. 【探讨】javascript事件机制底层实现原理

    前言 又到了扯淡时间了,我最近在思考javascript事件机制底层的实现,但是暂时没有勇气去看chrome源码,所以今天我来猜测一把 我们今天来猜一猜,探讨探讨,javascript底层事件机制是如 ...

最新文章

  1. Foxmail6密码获取案例
  2. Rust 2018 即将到来:设法从 Rust 2015 过渡
  3. linux ora01075,操作系统时间被修改导致ORA-01075和ORA-00600[2252]
  4. English learning
  5. openCV滑动条TrackBar事件实例
  6. Python编程从入门到实践~异常
  7. python爬去朋友圈_python爬虫24 | 搞事情了,用 Appium 爬取你的微信朋友圈。
  8. python3项目源代码下载_python3中文版下载
  9. javascript框架echarts插件实现超酷人立方效果图
  10. 用metasploit(msf)复现MS17-010(经典的永恒之蓝)SMB漏洞
  11. uni-app小程序生成海报,支持各种机型
  12. 幻灯片制作 新手制作幻灯片-来自于三人行慕课
  13. php 关于php时区时间错误问题 date 当前时间 时差(转载)
  14. Sentinel采用SphO方式定义资源,报错:The order of entry exit can‘t be paired with the order of entry
  15. Promise基础知识
  16. 学习笔记-Depth Map Prediction from a Single Image using a Multi-Scale Deep Network
  17. Python之axis函数
  18. http 请求的7 种方法
  19. 课上——HTML 表格 学生成绩表
  20. vue 实现级联选择器

热门文章

  1. HEVC Tile 编码器-kvazaar
  2. pyscripter与python的关系_详解python开发环境PyScripter中文乱码问题解决方案
  3. 前端架构,有什么能做的?
  4. Python学习:面向对象基础练习——士兵突击(代码演示) 及 身份运算符
  5. 计算机无法屏保,电脑屏幕保护程序为什么不能修改 原因及解决方法【详细介绍】...
  6. VLAN技术防黑+ vtp域VLAN数据同步
  7. 类图 顺序图 活动图 状态图 用法和比较
  8. 加速中小企业信息化,SaaS是砖还是宝?
  9. js 实现随机抽取选餐效果源码
  10. 字符串长度测量,大小比较