We read every piece of feedback, and take your input very seriously.
To see all available qualifiers, see our documentation.
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
应用于服务器端,同步加载
nodejs有一个模块加载系统
nodejs
每个文件被视为一个独立的模块
module.exports属性可以被赋予一个新的值(例如函数或对象)
module.exports
module.filename === __filename
module === require.main
require.resolve返回路径
require.resolve
module.exports 对象是由模块系统创建, 可以导出一个对象或者函数。
module.exports === exports // true
也就是说exports和module.exports是指向同一内存的。而模块导出的时候实际是导出module.exports的值
exports
模块在第一次加载后会被缓存,是基于其文件名来缓存的
在nodejs/lib目录下,require(),会优先加载核心模块
nodejs/lib
require()
当循环调用require()时,一个模块可能在为完成执行被返回
例子:
a.js
console.log('a 开始'); exports.done = false; const b = require('./b.js'); console.log('在 a 中,b.done = %j', b.done); exports.done = true; console.log('a 结束');
b.js
console.log('b 开始'); exports.done = false; const a = require('./a.js'); console.log('在 b 中,a.done = %j', a.done); exports.done = true; console.log('b 结束');
main.js
console.log('main 开始'); const a = require('./a.js'); const b = require('./b.js'); console.log('在 main 中,a.done=%j,b.done=%j', a.done, b.done);
结果:
main 开始 a 开始 b 开始 在 b 中,a.done = false b 结束 在 a 中,b.done = true a 结束 在 main 中,a.done=true,b.done=true
在a.js中require b.js, 之后又在b.js中require a.js,此时的 const a = require('./a.js');a是在a.js中require(b.js)语句之前导出的对象
require
require a.js
const a = require('./a.js');
官方解释:
当 main.js 加载 a.js 时,a.js 又加载 b.js。 此时,b.js 会尝试去加载 a.js。 为了防止无限的循环,会返回一个 a.js 的 exports 对象的 未完成的副本 给 b.js 模块。 然后 b.js 完成加载,并将 exports 对象提供给 a.js 模块。
如果没有找到确切的模块,文件拓展名会尝试加上.js、.json 或 .node
.js
.json
.node
当require一个非核心模块,也没有'/' 、 '../' 或 './' 开头。则会从当前模块的父目录开始,尝试从它的 /node_modules 目录里加载模块。没有找到就继续父目录
'/'
'../'
'./'
/node_modules
用于浏览器端,由于要保证效率,所以需要异步加载
AMD规范的实现
AMD
特点:
预加载(加载某个模块前,其依赖模块需要先加载),在并行加载js文件同时,还会解析执行该模块
js
当在head中引用js时,是阻塞的,只也是为什么推荐放在body尾。但引用了RequireJS就能解决这个问题,引入RequireJS异步加载
head
body
RequireJS
判断是否支持AMD规范
typeof define === "function" && define.amd
起步:
index.html
requireJS
<script data-main="./js/app.js" src="./js/require.js"></script>
data-main指向config文件
data-main
config
// ./js/app.js require.config({ baseUrl:'../', paths: { co: '../node_modules/co/index' } }); require(['./js/a.js'], function (data) { console.log(data) }) // a.js是定义的一个模块,用define定义 define(function () { var result = { a: 1, b: 2 } alert('haha') return result })
require(arr, callback)
define(arr, callback)
require.config({ shim: { "underscore" : { exports : "_"; } } })
RequireJS使用head.appendChild()将每一个依赖加载为一个script标签。
head.appendChild()
script
RequireJS等待所有的依赖加载完毕,计算出模块定义函数正确调用顺序,然后依次调用它们。
如: <script type="text/javascript" charset="utf-8" async="" data-requirecontext="_" data-requiremodule="co" src="./js/../node_modules/co/index.js"></script>
<script type="text/javascript" charset="utf-8" async="" data-requirecontext="_" data-requiremodule="co" src="./js/../node_modules/co/index.js"></script>
标注async属性的脚本是异步脚本,即异步下载脚本时,不会阻塞文档解析,但是一旦下载完成后,立即执行,阻塞文档解析, 不能保证执行顺序。
async
优先级高于defer
defer
因此需要确认脚本间没有依赖关系
加载快速,尤其遇到多个大文件,因为并行解析,所以同一时间可以解析多个文件。
并行加载,异步处理,处理顺序不一定,可能会造成一些困扰,甚至为程序埋下大坑。
判断:
typeof define === "function" && define.cmd
既可以用exports对象来导出,也可以用return导出
return
模块定义:
define(id?, deps?, function(require, exports, module) { // 模块代码 });
数组 deps 是模块依赖,带有id和deps的define不属于CMD规范
deps
id
define
CMD
require.async
使用范例:
// index.html <script src="./js/sea.js"></script> <script> seajs.config({ base: "./", alias: { "c": "js/c.js", "d": "js/d.js", "jquery": "node_modules/co/index.js" } }); seajs.use('c'); </script> // c.js define(function (require, exports, module) { require.async('d', function (obj) { console.log(obj) }) console.log('sucess') // success // {a:1,b:2} }) // d.js define((function (require, exports, module) { return { a:1, b:2 } }))
SeaJS采用了和Node很相似的CMD规范
Seajs分为模块加载期和执行期,加载期需要将执行期所有用到的模块从服务端同步过来,在再执行期按照代码的逻辑顺序解析执行模块。
动态添加script标签, 在network中能看到js加载,但是Element中没有插入
network
Element
只有在使用的时候才会解析执行js文件,因此,每个JS文件的执行顺序在代码中是有体现的,是可控的
执行等待时间会叠加。因为每个文件执行时是同步执行(串行执行),因此时间是所有文件解析执行时间之和,尤其在文件较多较大时,这种缺点尤为明显。
npm install --save-dev babel-cli
npm babel-node your-script.js
或者
js文件命名为.mjs
node --experimental-modules your-script.mjs
如CommonJS规范,在require的时候是全部加载的
如import,只加载导入的变量或方法,其它未导入的不加载
use strict
export var a = 1; export const b = function () {} 或者 var a = 1; var b = 2; var c = 3; export { a, b ,c} 或者 function v1 () {} export { v1 as a }
错误应用及修正
function f () {} export f // 报错 export function f () {} // 正确 export default function() {} // 正确, 加载时整体加载 等同于 export {add as default};
注意
export 输出的是与值动态绑定的接口
// b.js var a = 1 function add () { a++ } export {a, add} // a.js import { add, a } from './b.js' console.log(a); add(); console.log(a)
而CommonJS中输出的是值的缓存
// b.js var a = 1 function add () { a++ } module.exports = {a, add} // a.js var obj = require('./b.js') console.log(obj.a); // 1 obj.add(); console.log(obj.a) // 1
export只能出现在模块顶层
加载的变量是const常量,是只读的。
const
import命令具有提升效果,意思加载的函数可以再之前执行
import
import是静态执行,不能使用表达式或者变量
export { foo, bar } from 'my_module'; foo, 和bar在当前模块不能用,相当于转发出去了
import和export命令只能在模块的顶层,不能在代码块之中
typeof define === "function"
typeof module === "object" && typeof module.exports === "object"
arguments
module
__filename
__dirname
import {readFile} from 'fs'
// CommonJS // a.js exports.done = false; console.log('begin'); // 在循环加载a.js的时候不再执行,只是取得已经导出的 var b = require('./b.js'); console.log('在 a.js 之中,b.done = %j', b.done); exports.done = true; console.log('a.js 执行完毕'); // b.js exports.done = false; var a = require('./a.js'); console.log('在 b.js 之中,a.done = %j', a.done); exports.done = true; console.log('b.js 执行完毕');
ES6模块循环加载时,那些变量不会被缓存,而是指向被加载模块的引用
// a.mjs let done = 1 console.log('haha') let foo = function foo () { console.log('foo') } export {foo, done}; import './b.mjs' // b.mjs import {foo} from './a.mjs' console.log('b====') foo(); // b==== // ReferenceError: foo is not defined
解决:写成函数声明,函数具有提升作用
// a.js let done = 1 console.log('haha') function foo () { console.log('foo') } export {foo, done}; import './b.mjs'
也就是说,AMD先加载,先执行,CMD先加载,延迟执行
The text was updated successfully, but these errors were encountered:
No branches or pull requests
模块化机制
CommonJS规范
应用于服务器端,同步加载
nodejs
有一个模块加载系统每个文件被视为一个独立的模块
module.exports
属性可以被赋予一个新的值(例如函数或对象)module.filename === __filename
module === require.main
require.resolve
返回路径module.exports
module.exports
对象是由模块系统创建, 可以导出一个对象或者函数。module.exports === exports // true
也就是说
exports
和module.exports
是指向同一内存的。而模块导出的时候实际是导出module.exports
的值缓存:
模块在第一次加载后会被缓存,是基于其文件名来缓存的
核心模块:
在
nodejs/lib
目录下,require()
,会优先加载核心模块循环:
当循环调用
require()
时,一个模块可能在为完成执行被返回例子:
a.js
b.js
main.js
结果:
在
a.js
中require
b.js
, 之后又在b.js
中require a.js
,此时的const a = require('./a.js');
a是在a.js中require(b.js)语句之前导出的对象官方解释:
当
main.js
加载a.js
时,a.js
又加载b.js
。 此时,b.js
会尝试去加载a.js
。 为了防止无限的循环,会返回一个a.js
的exports
对象的 未完成的副本 给b.js
模块。 然后b.js
完成加载,并将exports
对象提供给a.js
模块。文件模块:
如果没有找到确切的模块,文件拓展名会尝试加上
.js
、.json
或.node
从 node_modules 目录加载
当
require
一个非核心模块,也没有'/'
、'../'
或'./'
开头。则会从当前模块的父目录开始,尝试从它的/node_modules
目录里加载模块。没有找到就继续父目录AMD/CMD规范
用于浏览器端,由于要保证效率,所以需要异步加载
AMD规范实现: RequireJS
AMD
规范的实现特点:
预加载(加载某个模块前,其依赖模块需要先加载),在并行加载
js
文件同时,还会解析执行该模块当在
head
中引用js
时,是阻塞的,只也是为什么推荐放在body
尾。但引用了RequireJS
就能解决这个问题,引入RequireJS
异步加载判断是否支持
AMD
规范typeof define === "function" && define.amd
起步:
index.html
中引入requireJS
<script data-main="./js/app.js" src="./js/require.js"></script>
data-main
指向config
文件require(arr, callback)
,也可以用define(arr, callback)
,再次导出一个对象,供别的模块使用原理
RequireJS
使用head.appendChild()
将每一个依赖加载为一个script
标签。RequireJS
等待所有的依赖加载完毕,计算出模块定义函数正确调用顺序,然后依次调用它们。如:
<script type="text/javascript" charset="utf-8" async="" data-requirecontext="_" data-requiremodule="co" src="./js/../node_modules/co/index.js"></script>
标注
async
属性的脚本是异步脚本,即异步下载脚本时,不会阻塞文档解析,但是一旦下载完成后,立即执行,阻塞文档解析, 不能保证执行顺序。优先级高于
defer
因此需要确认脚本间没有依赖关系
优点
加载快速,尤其遇到多个大文件,因为并行解析,所以同一时间可以解析多个文件。
缺点
并行加载,异步处理,处理顺序不一定,可能会造成一些困扰,甚至为程序埋下大坑。
CMD规范实现:SeaJS
判断:
typeof define === "function" && define.cmd
特点:
既可以用
exports
对象来导出,也可以用return
导出模块定义:
数组
deps
是模块依赖,带有id
和deps
的define
不属于CMD
规范异步加载
require.async
使用范例:
如何工作
SeaJS采用了和Node很相似的CMD规范
Seajs分为模块加载期和执行期,加载期需要将执行期所有用到的模块从服务端同步过来,在再执行期按照代码的逻辑顺序解析执行模块。
动态添加
script
标签, 在network
中能看到js加载,但是Element
中没有插入优点
只有在使用的时候才会解析执行js文件,因此,每个JS文件的执行顺序在代码中是有体现的,是可控的
缺点
执行等待时间会叠加。因为每个文件执行时是同步执行(串行执行),因此时间是所有文件解析执行时间之和,尤其在文件较多较大时,这种缺点尤为明显。
ES6模块化
解决在node中import报错问题
npm install --save-dev babel-cli
npm babel-node your-script.js
或者
js文件命名为.mjs
node --experimental-modules your-script.mjs
模块加载
如CommonJS规范,在require的时候是全部加载的
如import,只加载导入的变量或方法,其它未导入的不加载
import
好处
使用严格模式
use strict
export
错误应用及修正
注意
export 输出的是与值动态绑定的接口
而CommonJS中输出的是值的缓存
export只能出现在模块顶层
import
加载的变量是
const
常量,是只读的。import
命令具有提升效果,意思加载的函数可以再之前执行import
是静态执行,不能使用表达式或者变量import和export同时应用
import和export命令只能在模块的顶层,不能在代码块之中
UMD中判断模块如何加载
typeof define === "function"
typeof module === "object" && typeof module.exports === "object"
es6模块和CommonJS的区别
arguments
,require
,module
,exports
,__filename
,__dirname
import {readFile} from 'fs'
ES6模块循环加载时,那些变量不会被缓存,而是指向被加载模块的引用
解决:写成函数声明,函数具有提升作用
AMD和CMD的区别
AMD
推崇依赖前置,在定义模块的时候就要声明其依赖的模块 ,CMD
推崇就近依赖,只有在用到某个模块的时候再去require
require
语句的时候才执行对应的模块,这样模块的执行顺序和书写顺序是完全一致的。AMD在加载模块完成后就会执行改模块,所有模块都加载执行完后会进入require
的回调函数, 但是主逻辑一定在所有依赖加载完成后才执行。也就是说,AMD先加载,先执行,CMD先加载,延迟执行
The text was updated successfully, but these errors were encountered: