为什么会出现模块?
- 解决变量冲突,如果变量都定义在全局作用域中,难免会冲突,模块独立成各自作用域
- 将复杂的业务封装成一个个模块,业务逻辑更清晰
- 模块可以控制暴露什么,不暴露什么(内部调用),可以引入其他模块
模块是什么?
模块就是将复杂的业务代码拆分成独立的块,各个模块之间存在依赖,但各自又是独立的。
在没有模块之前,如何模拟模块?
为模仿模块模式封装,ES6之前会使用函数作用域和立即调用函数表达式(IIFE)将模块定义风转载匿名包中。
(function(){
//私有模块代码
console.log('模块');
})();
模块
讲解模块内容:模块标识符,模块依赖,模块加载,入口,异步加载,动态加载,静态分析,循环依赖
模块标识符
每个模块都有个可用于引用它的标识符,可以是字符串,也可以是文件实际路径(相对路径和绝对路径)
如果你做过vue开发,用到Node.js和Webpack的话,对下面的代码应该不会陌生
入口
相互依赖的模块必须指定一个模块作为入口,这也是代码执行起点。
ES6模块
在过去JS不支持模块时我们使用AMD/CMD(浏览器端使用)
、CommonJS(Node.js使用)
、UMD(两者都支持)
等形式定义模块。
AMD代表性的是 require.js
,CMD 代表是淘宝的 seaJS
框架。
ECMAScript6模块借鉴CommonJS和AMD的很多特性。
模块标识type="module"
标签使用
<script type="module">
//模块代码
</script>
也可以引入外部模块文件
外部js文件:1.js
console.log(1)
引入
<script type="module" src="./1.js"></script>
总结:只要在标签上加上type="module"模块标识,浏览器就会把这部分代码当模块处理。
模块有哪些特点?
延迟解析执行
我们知道,页面中个js代码块默认会按顺序执行,而模块代码会延迟执行。
外部1.js文件
console.log(1)
外部4.js文件
console.log(4)
页面引入:
<script type="module" src="./1.js"></script>
<script type="module">
//模块代码
console.log(2);
</script>
<script type="module">
//模块代码
console.log(3);
</script>
<script src="./4.js"></script>
<script>
console.log(5);
</script>
打印结果是:4,5,1,2,3,即延后执行模块代码。
只执行一次
多次引入同意模块,只执行一次
<script type="module" src="./1.js"></script>
<script type="module" src="./1.js"></script>
<script src="./4.js"></script>
<script src="./4.js"></script>
打印结果是:4,4,1 只会打印一次1。
模块默认严格模式
<script>
console.log(this); //Window
</script>
<script type="module">
console.log(this); //undefiend
</script>
导出导入
模块具有单独的作用域,唯一使用的方式就是先导出export,再导入import使用。
导出和导入方式都是一 一相对的,命名导出-命名导入;默认导出-默认导入;批量导出-批量导入
命名导出导入
导出的是一个module对象,对象里面添加要导出的属性名方法名,导入时必须名称保持一致
外部 1.js文件
const foo = 'foo'
export { foo }
页面引入使用:
<script type="module">
import { foo } from './1.js'
console.log(foo);//foo
</script>
默认导出导入
默认导出使用default,导入可以使用任意变量名接收,这也就时为什么一个模块只能有一个默认导出。默认导出多个会报错。
外部1.js文件:
const foo = 'foo'
// 等同于export default foo;但我们更推荐下面这种写法
export { foo as default }
页面导入使用:
<script type="module">
//可以使用任意变量接收
import hh from './1.js'
console.log(hh);//foo
</script>
默认导出还要注意一个问题:默认导出和声明变量不能同时进行,但函数是可以的。
export default const foo = 'foo'//报错
export default function run() { }// 正常
一些规则限制,我们很难记住,多以推荐声明赋值和导出分开写
const foo = 'foo'
export { foo as default }
混合导出导入
即模块中既有命名导出,又有默认导出。
外部1.js文件:
const foo = 'foo'
const bar = 'bar'
export { foo as default, bar }
页面导入使用:
<script type="module">
import hh, { bar } from './1.js'
console.log(hh);//foo
console.log(bar);//bar
</script>
批量导入
外部1.js文件:
const foo = 'foo'
const bar = 'bar'
export { foo, bar }
页面导入使用:
<script type="module">
import * as multiple from './1.js'
console.log(multiple.foo);//foo
console.log(multiple.bar);//bar
</script>
as别名
细心的同学可能发现批量导入时已经使用到了as,我们还常常用as解决引入多个文件变量名称冲突问题。
外部1.js文件:
const foo = 'foo'
const bar = 'bar'
export { foo, bar }
外部2.js文件:
const foo = 'foo'
export { foo }
页面导入使用:
<script type="module">
import { foo } from './1.js'
import { foo as foo2 } from './2.js'
console.log(foo)//foo
console.log(foo2)//foo
</script>
ES6模块的基本使用就是这样了,其实模块一般是和class(上一章讲解)一起使用的,实现面向对象的封装。