Node.js 的 commonJS 规范 ES6 导入 js 文件
Posted 知其黑、受其白
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了Node.js 的 commonJS 规范 ES6 导入 js 文件相关的知识,希望对你有一定的参考价值。
阅读目录
ES6
提示
本地 html 文件中的 script 标签引入 ES6 的模块,直接在浏览器中打开该 html 文件,会报错!
这里报错的原因是用了 es6 的语法, 浏览器默认将它作为 js 解析会出现问题,需要将它作为模块导入,script 标签默认 type=“text/javascript”,需要改为 type=“module”,更改后的index.html。
浏览时也需要服务器方式预览。
E:/pdf1/test/common.js
export default
add: function(a, b)
return a + b
E:/pdf1/test/index.js
import cal from './common.js'
console.log('sum: ', cal.add(1,2))
E:/pdf1/test/index.html
<script type="module" src="./index.js"></script>
Node.js 的 commonJS 规范
模块引用
你可以通过 require
来引入你所需要的模块,这个方法接收模块标识,以此引入一个模块的 API 到当前上下文中。
const fs = require('fs');
模块定义
一个模块就是一个文件,文件中你可以定义或处理任何方法,但你必须要有一个出口, 用于导出你想要暴露的方法,上下文提供了 exports
对象就是用来导出的,它也是唯一的出口。
在模块中,还存在一个 module
对象,它代表模块本身,而 exports
是 module
的属性。
// 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
为后缀的文件,在引入时可以省略后缀,就像上面的例子一样。
每个模块之间是相对独立的,他们互不影响,引入多个模块也不必担心变量污染。
Node 中的模块
通过上面的介绍可以知道,通过 module、exports 和 require 可以很方便的实现不同模块之间的相互穿插使用,但在 node 中,并非完全按照此规范实现,而是对模块进行了一定的取舍,并增加了一些自身的特性。
node 在引入模块时会经历以下阶段:
(1)路径分析
(2)文件定位
(3)编译执行
在 node 中模块可以分为两大类,一类是 node 自身提供的(如:buffer、fs、http
等),称为核心模块;
另外一类是用户自己编写的,称为文件模块;
在开发中,如果我们想用到一些第三方的插件时,在引入前,必须要先进行安装,然后才可以引入使用;
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 模块规范中,我们可以直接使用变量 require、exports、module
,还有__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 目录下的所有模块文件也是没有定义 require、module、exports 这些变量的。
在引入核心模块的过程中,也经历了头尾包装的过程,然后才执行和导出了 exports 对象。
包与NPM
虽然有了node的核心模块,我们也可以自定义模块,但还是存在两个问题:
1 自己的模块只能够自己使用
2 模块与模块之间比较零散,相互之间不能直接使用
包与 NPM 就是将模块联系起来的一种机制,在模块的基础上进一步组织 JavaScript 代码。
一个符合 CommonJS 规范的包目录应该包含以下文件:
package.json:包描述文件
bin:存放可执行二进制文件的目录
lib:存放JavaScript代码的目录
doc:存放文档的目录
test:存放单元测试用例的代码
package.json
用于表达代码的相关信息,它是一个JSON 格式的文件,位于包的根目录下,是包的重要组成部分。
CommonJS 定义了一些必须的字段。
name:包名
description:包的简要介绍
version:包的当前版本,每次更新包时需要要更新版本号,通常为major.minor.revision的格式
keywords:包的关键词,使用数组的形式,有利于用户搜索到你的包
maintainers:报的维护者列表,为一个数组。每个维护者有name、email和web属性组成。
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"
以上是关于Node.js 的 commonJS 规范 ES6 导入 js 文件的主要内容,如果未能解决你的问题,请参考以下文章
前端模块化方案:CommonJS/AMD/CMD/ES6规范