// common/index.js
// 初始化SelectMixin
let selectUrl = '/example/common/getSelectBykeys'
let setSelectUrlPrefix = () => {}
const SelectMixin = select(selectUrl, setSelectUrlPrefix)
// 初始化AuthProviderMixin
let authUrl = '/example/common/authUrl'
let transferAuthResult = () => {}
const AuthProviderMixin = authProvider(authUrl, transferAuthResult)
// 初始化request
let handleRequestError = () => {}
const RequestUtil = request(handleRequestError)
export default {
SelectMixin,
AuthProviderMixin,
RequestUtil
}
复制代码
import { SelectMixin } from '../common/index.js'
复制代码
"export 'SelectMixin' was not found in '../common/index.js' 复制代码
改为javascript
import SelectMixin from '../common/index.js'
复制代码
export default {
SelectMixin,
AuthProviderMixin,
RequestUtil
}
复制代码
module.exports.default = {
SelectMixin,
AuthProviderMixin,
RequestUtil
}
复制代码
import 语句中的"解构赋值"并非解构赋值,而是 named imports,语法上和解构赋值很像,但仍是有所差异html
上面的问题能够想着先,而后咱们来看看下面这几个知识点以后再回来讲这个问题。 上面的代码用了 export default ,而对应的import的在这个时候被称做 default import。他们是成对使用的。因此用了 export default 则必定要用到 default import。这里须要记住的知识点有如下几点。java
// this is default export
// A.js
export default 42
复制代码
// this is default import
// B.js
import A from './A'
复制代码
一个模块只能有一个default exportsnode
default imports 只对 default export 有用, default exports 须要用 default imports 去获取。webpack
在 default imports 中,导出的时候,能够为其任意命名,由于 default export 是匿名的。在下面的例子里,A, MyA, Something 都是内容相同的。只是其名字不一样而已,这个语法就是获取./A值的同时,为其匿名的对象取个名字git
import A from './A'
import MyA from './A'
import Something from './A'
复制代码
上面的问题能够想着先。而后咱们来看看下面这几个知识点以后再回来讲这个问题。上面的代码用了 named default ,而对应的import的在这个时候被称做 named import。他们是成对使用的。因此用了 named default 则必定要用到 named import。这里须要记住的知识点有如下几点。es6
// this is named export
// A.js
export const A = 42
复制代码
// this is named import
// B.js
import { A } from './A'
复制代码
// B.js
import { A } from './A'
import { myA } from './A' // Doesn't work! import { Something } from './A' // Doesn't work!
复制代码
// A.js
export const A = 42
export const myA = 43
export const Something = 44
复制代码
// B.js
import { A, myA, Something } from './A'
复制代码
// B.js
import anyThing, { myA as myX, Something as XSomething } from './A'
复制代码
咱们能够把 Default export 当成一个特殊的 named export ,其实default export 也能够像这样来解析。只是他默认是叫default的一个对象。切默承认以有任意一个名字去覆盖他的匿名。github
import { default as anything } from './A'
复制代码
不过这样写也是不被容许的,毕竟default 是一个保留字段。系统会直接报错,不过讲道理,抛去这个保留字段问题,这个写法按道理也能获取到default exportweb
import { default } from './A'
复制代码
在了解named import 过程当中碰到一个有意思的点就是 模块点循环引用。文献里说到es6 对循环引用支持比CommonJs更好。特对这个进行了一番了解。segmentfault
"循环引用"(circular dependency)指的是,a脚本的执行依赖b脚本,而b脚本的执行又依赖a脚本。
一般,”循环引用"表示存在强耦合,若是处理很差,还可能致使递归加载,使得程序没法执行,所以应该避免出现。
可是实际上,这是很难避免的,尤为是依赖关系复杂的大项目,很容易出现a依赖b,b依赖c,c又依赖a这样的状况。这意味着,模块加载机制必须考虑”循环引用”的状况。即便在设计初期经过很好的结构设计避免了,可是代码一重构,”循环引用”仍是很容易就出现的。因此”循环引用”的状况是不可能避免的。
这里不讨论其余静态文件只考虑脚本文件,CommonJS的一个模块,就是一个脚本文件。require命令第一次加载该脚本,就会执行整个脚本,而后在内存生成一个对象。以下:
{
id: '',
exports: '',
parent: '',
filename: null,
loader: false,
children: [],
paths: ''
// ...
}
复制代码
这个对象里,id属性是模块名,exports属性是模块输出的各个接口,loaded属性是一个布尔值,表示该模块的脚本是否执行完毕。其余还有不少属性,省略。 具体能够去看 www.ruanyifeng.com/blog/2015/1… 也能够参考node源码 github.com/nodejs/node…
当代码用到这个模块的时候,就会到exports属性上面取值。即便再次执行require命令,也不会再次执行该模块,而是到缓存之中取值。
不说答案,咱们直接说这个在运行c.js时当过程。上边代码之中,c.js先引入了a.js。则按照required的原理,会执行a.js整个脚本。
a.js脚本先输出一个done变量,而后加载另外一个脚本文件b.js。注意,此时a.js代码就停在这里,等待b.js执行完毕,再往下执行。 再看b.js的代码
b.js执行到第二行,就会去加载a.js,这时,就发生了”循环引用”。(CommonJs的循环引用的重要原则:一旦出现某个模块被”循环引用”,就只输出已经执行的部分,还未执行的部分不会输出。) 这里有得小伙伴会认为是去执行a.js以前没执行完的代码。可是规则不是这么定义的。由于a.js触发了循环引用,则a.js会返回已经执行的部分代码。
系统会去a.js模块对应对象的exports属性取值,a.js虽然尚未执行完,可是其exports里确实有值的,从exports属性取回已经执行的部分的值,而不是最后的值。 a.js已经执行的部分,只有一行,即 exports.done = false;
因此对于b.js来讲,引入的a.js值为false。而后,b.js接着往下执行,等到所有执行完毕,再把执行权交还给a.js。因而,a.js接着往下执行,直到执行完毕。
c.js就第一句就将a.js和b.js所有加载完毕了。a.js和b.js最终返回的都是true。
因此最终会选择答案B。
执行a.js 以后的结果是右边两种状况。这个比前面一个例子好理解。咱们先来看一下es6 modules 的加载原理。
ES6模块的运行机制与CommonJS不同,它遇到模块加载命令import时,不会去执行模块,而是只生成一个引用。 等到真的须要用到时,再到模块里面去取值。
所以,ES6模块是动态引用,不存在缓存值的问题,并且模块里面的变量,绑定其所在的模块。
若是按照CommonJS规范,上面的代码是无法执行的。a先加载b,而后b又加载a,这时a尚未任何执行结果,因此输出结果为null,即对于b.js来讲,变量foo的值等于null,后面的foo()就会报错。
在commonJs里常常会看到exports 和 module.exports 。这个会比较混淆。
CommonJS定义的模块分为: 模块标识(module)、模块定义(exports) 、模块引用(require)。
在一个node执行一个文件时,会给这个文件内生成一个 exports和module对象, 而module又有一个exports属性。他们之间的关系以下图,都指向一块{}内存区域。
简而言之,区分他们之间的区别就是 exports 只是 module.exports的引用,辅助后者添加内容用的。
对于基本数据类型,属于复制。即会被模块缓存。同时,在另外一个模块能够对该模块输出的变量从新赋值。
对于复杂数据类型,属于浅拷贝。因为两个模块引用的对象指向同一个内存空间,所以对该模块的值作修改时会影响另外一个模块。
当使用require命令加载某个模块时,就会运行整个模块的代码。
当使用require命令加载同一个模块时,不会再执行该模块,而是取到缓存之中的值。也就是说,CommonJS模块不管加载多少次,都只会在第一次加载时运行一次,之后再加载,就返回第一次运行的结果,除非手动清除系统缓存。
循环加载时,属于加载时执行。即脚本代码在require的时候,就会所有执行。一旦出现某个模块被"循环加载",就只输出已经执行的部分,还未执行的部分不会输出。
ES6模块中的值属于【动态只读引用】。
对于只读来讲,即不容许修改引入变量的值,import的变量是只读的,不管是基本数据类型仍是复杂数据类型。当模块遇到import命令时,就会生成一个只读引用。等到脚本真正执行时,再根据这个只读引用,到被加载的那个模块里面去取值。
对于动态来讲,原始值发生变化,import加载的值也会发生变化。不管是基本数据类型仍是复杂数据类型。
循环加载时,ES6模块是动态引用。只要两个模块之间存在某个引用,代码就可以执行。
stackoverflow.com/questions/3…
2ality.com/2014/09/es6… exploringjs.com/es6/ch_modu…
www.ruanyifeng.com/blog/2015/1…