require用于读取并执行js文件,并返回该模块的exports对象。若无指定模块,会报错。javascript
Node使用CommonJS模块规范,require
属于node的内置命令。CommonJS规范加载模块是同步的,也就是说,只有加载完成,才能执行后面的操做。css
// example.js var invisible = function () { console.log("invisible"); } //在另外一个文件中引入example.js var example = require('./example.js'); console.log(example); |
import用于引入外部模块、其余脚本等的函数、对象或者基本类型。html
import属于ES6的命令,ES6模块的运行机制与CommonJS不同,它遇到模块加载命令import
时,不会去执行模块,而是只生成一个引用。等到真的须要用到时,再到模块里面去取值。java
import defaultMember from "module-name"; import * as name from "module-name"; import { member } from "module-name"; import { member as alias } from "module-name"; import { member1 , member2 } from "module-name"; import { member1 , member2 as alias2 , [...] } from "module-name"; import defaultMember, { member [ , [...] ] } from "module-name"; import defaultMember, * as name from "module-name"; import "module-name"; |
第一次加载某个模块时,Node会缓存该模块。之后再加载该模块,就直接从缓存取出该模块的module.exports
属性。例如:node
require('./example.js'); require('./example.js').message = "hello Lily"; var test = require('./example.js').message console.log(test);// "hello" |
能够看到test的打印结果为:hello。webpack
这是由于即便./example.js模块引用了三次,可是第一次加载该模块时,Node会缓存该模块。之后两次再加载该模块,就直接从缓存取出该模块的module.exports
属性。因此,require('./example.js').message = "hello";只是往已缓存的模块添加一个属性,当再次取引用require('./example.js')时会发现message属性任然存在,说明example.js模块没有被从新加载,与上一次的引用使用的是同一缓存。es6
ES6模块的动态编译:遇到模块加载命令import
时,不会去执行模块,而是只生成一个引用。等到真的须要用到时,再到模块里面去取值。web
即,ES6模块是动态引用,不存在缓存值的问题,并且模块里面的变量,绑定其所在的模块。例如:缓存
// base.js export var foo = 'bar'; setTimeout(() => foo = 'baz change', 500); // import.js import { foo } from './baseEs6'; console.log(foo); // bar setTimeout(() => console.log(foo), 600);// baz changed |
上面代码中,base.js
的变量foo
,在刚加载时等于bar
,过了500毫秒,又变为等于‘baz changed’
。依据动态加载的原理,m2.js
正确地读取到了这个变化。babel
使用babel编译文件import.js
var _baseEs = require('./baseEs6'); console.log(_baseEs.foo); setTimeout(function () { return console.log(_baseEs.foo); }, 600); |
咱们能够清楚地看到遇到import文件./baseEs6的foo变量时,会先不会去执行模块,而是只生成一个引用_baseEs。等到真的须要用到变量foo时,再到模块里面去取值(_baseEs.foo)。因此在第一次打印foo时输出的是foo的初始值。等到模块中的setTimeout(() => foo = 'baz change', 500);执行完毕,foo的值变为'baz change',此时import.js文件再打印foo就发现foo确实变为'baz change'。
但这只能保证不会出现引用的异常,仍是会可能出现模块没有彻底加载完就在另外一个文件中掉用这个模块的状况,此时会出现undefined。
// baseEs6.js export var fish; setTimeout(() => fish = 'fish', 500); // es6.js import { fish } from './baseEs6'; console.log(fish); //undefined |
babel是一个普遍使用的转码器,能够将ES6代码转为ES5代码,从而在现有环境执行。
在咱们的项目中,可使用的语言特性有环境区分,对 server 端运行的代码,webpack将src/*下的es6代码 转换成 lib/* 下的符合commonJS规范的es5,而后node执行lib/* 的代码。这里,咱们主要来了解一下babel对于Import命令的转码。
// es6Test.js import * as actions from './searchAccount.actions'; import kdbTheme from '../../../common/Dialog/panguTheme.css'; import { createCommonAccount } from './createDialog.actions'; console.log('createCommonAccount###', createCommonAccount); // babel编译es6Test.js /* import * as actions from './searchAccount.actions' */ var _searchAccount = require('./searchAccount.actions'); var actions = _interopRequireWildcard(_searchAccount); /* import kdbTheme from '../../../common/Dialog/panguTheme.css' */ var _panguTheme = require('../../../common/Dialog/panguTheme.css'); var _panguTheme2 = _interopRequireDefault(_panguTheme); /* import { createCommonAccount } from './createDialog.actions'*/ var _createDialog = require('./createDialog.actions'); console.log('createCommonAccount###', _createDialog.createCommonAccount); function _interopRequireWildcard(obj) { if (obj && obj.__esModule) { return obj; } else { var newObj = {}; if (obj != null) { for (var key in obj) { if (Object.prototype.hasOwnProperty.call(obj, key)) newObj[key] = obj[key]; } } newObj.default = obj; return newObj; } } function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; } |
对于import XXX from 'a',a模块中首先会生成一个object来存储这个模块的全部要export出去的变量:Object.defineProperty(exports, "__esModule", {value: true});这里给模块引用添加__esModule属性是为了方便babel识别当前import的模块是不是已经被babel编译过的模块仍是第三方模块。
在经历一系列的属性添加后,import会首先返回这个模块的引用,而后根据具体的import的命令来处理这个引用。
若是import的目标是一个default的export,import会先获取目标模块的引用,再调用_interopRequireDefault函数处理这个引用。_interopRequireDefault的做用就是判断require的模块是不是已经被babel编译过的模块,若是是,则当前require的引用必定存在一个default属性;不然为他加一个default属性,这样便不会调用模块的default为undefined的状况了。
若是import的目标是一个整个模块全部export出去的属性,import会先获取目标模块的引用,再调用_interopRequireWildcard函数处理这个引用。_interopRequireWildcard首先判断require的模块引用视为已经被babel正确编译过,当判断结果为FALSE且在当前引用不为空的状况下,会对当前引用作浅度克隆,并为其添加一个default属性。防止调用模块的属性时出现undefined的状况了。
若是import的目标是一个整个模块的普通export出去的属性,import会先获取目标模块的引用,并不当即读取模块对象的A属性,等到真正调用目标属性时再去读取模块对象的A属性。
参考文档