前端最基础的就是 HTML+CSS+Javascript
。掌握了这三门技术就算入门,但也仅仅是入门,现在前端开发的定义已经远远不止这些。前端小课堂(HTML/CSS/JS
),本着提升技术水平,打牢基础知识的中心思想,我们开课啦(每周四)。
Node 应用由模块组成,采用 CommonJS 模块规范。
每个文件就是一个模块,有自己的作用域。在一个文件里面定义的变量、函数、类,都是私有的,对其他文件不可见。
// 导出 addX
module.exports.addX = 1;
// 导入模块
example = require('./example.js');
console.log(example.addX)
commonjs 模块规范
特点
- 所有代码运行在模块作用域,不会污染全局作用域。
- 模块可以多次加载,但是只会在第一次加载时运行一次,然后运行结果就被缓存了,以后再加载,就直接读取缓存结果。要想让模块再次运行,必须清除缓存。
- 模块加载的顺序,按照其在代码中出现的顺序。
- CommonJS 规范加载模块是同步的,也就是说,只有加载完成,才能执行后面的操作。
module对象
Node内部提供一个 Module
构建函数。所有模块都是Module
的实例。每个模块内部,都有一个module
对象,代表当前模块。它有以下属性。
module.id
模块的识别符,通常是带有绝对路径的模块文件名。module.filename
模块的文件名,带有绝对路径。module.loaded
返回一个布尔值,表示模块是否已经完成加载。module.parent
返回一个对象,表示调用该模块的模块。if (!module.parent) { // ran with `node something.js` app.listen(8088, function() { console.log('app listening on port 8088'); }) } else { // used with `require('/.something.js')` module.exports = app; }
module.children
返回一个数组,表示该模块要用到的其他模块。module.exports
表示模块对外输出的值。
AMD 模块规范
CommonJS规范加载模块是同步的,也就是说,只有加载完成,才能执行后面的操作。AMD规范则是非同步加载模块,允许指定回调函数。由于Node.js主要用于服务器编程,模块文件一般都已经存在于本地硬盘,所以加载起来比较快,不用考虑非同步加载的方式,所以CommonJS规范比较适用。但是,如果是浏览器环境,要从服务器端加载模块,这时就必须采用非同步模式,因此浏览器端一般采用AMD规范。
AMD规范使用define方法定义模块,下面就是一个例子:
define(['package/lib'], function(lib){
function foo(){
lib.log('hello world!');
}
return {
foo: foo
};
});
AMD规范允许输出的模块兼容CommonJS规范,这时define
方法需要写成下面这样:
define(function (require, exports, module){
var someModule = require("someModule");
var anotherModule = require("anotherModule");
someModule.doTehAwesome();
anotherModule.doMoarAwesome();
exports.asplode = function (){
someModule.doTehAwesome();
anotherModule.doMoarAwesome();
};
});
ESM 模块规范
「 ESM 」 全称 ECMAScript modules,基本主流的浏览器版本都以已经支持。主要用于浏览器原生支持模块化,<script src="index.js" type="module"></script>
。
ESM 使用实时绑定逻辑,两个模块都指向内存中的相同位置。这意味着,当导出模块更改值时,该更改将显示在导入模块中。
这不同于 CommonJS 模块,在 CommonJS 中,整个导出对象在导出时被复制。这意味着 CommonJS 模块导出的任何值(例如数字)都是副本,如果 CommonJS 模块导出模块以后更改了该值,则导入模块将看不到该更改。
ESM 如何工作
- 浏览器会构建一个依赖关系图。
- 通过指定一个入口文件(
<script src="index.js" type="module"></script>
),然后从这个文件开始,通过其中的import
语句,查找其他代码。 - 通过指定的文件路径, 浏览器就找到了目标代码文件。 但是浏览器并不能直接使用这些文件,它需要解析所有这些文件,以将它们转换为称为模块记录的数据结构。
- 将
模块记录
转换为模块实例
。模块实例
, 实际上是 「代码
」(指令列表)与「状态
」(所有变量的值)的组合。
ESM 动态路径
因为 ESM 的特性,正常情况下是不能使用变量作为路径
// commonjs 其实是同步的,会挂起主线程去加载文件,
// 因为 commonjs 主要用于 node,从本地磁盘加载资源是很快的。
require(`${publicPath}/count.js`);
// 因为 ESM 规范是先静默分析资源去下载,所以其实在这里时变量还没有执行
import count from `${publicPath}/count.js`;
// 但是有时候又希望有这样功能,这个时候我们可以使用import动态导入功能,会启动一个新的加载过程。
import(`${path}/count.js`)
node 中使用
- 碰到这种兼容性的,上 babel 就 OK
- 社区(Node 14)尝试解决此问题的一种方法是使用
.mjs
扩展。使用该扩展名告诉Node,“此文件是一个模块”。
总结
名称 | 导入关键词 | 导出关键词 | 规范来源&备注 |
---|---|---|---|
CommonJS、CJS | require('path') | module.exports | NodeJS、同步、加载本地资源同步很快的然后马上执行 |
ESM、MJS | import a from 'path' 、import() | export 、export default | 浏览器、异步、因为网络因素不可靠会在所有资源都加载完成才执行 |
module.exports.foo = 'bar'
命名导出module.exports = 'baz'
默认导出。export const sum = (x, y) => x + y;
命名导出export default (x, y) => x + y;
默认导出
**粗体** _斜体_ [链接](http://example.com) `代码` - 列表 > 引用
。你还可以使用@
来通知其他用户。