模块简介
一些模块历史,如:
- AMD,代表是
require.js,实现前端代码的模块化 - CommonJs,主要是
node使用 - UMD,兼容AMD和CommonJs
ES6开始有了JS模块,通过关键字 export import 分别来导出和导入模块资源。
现代浏览器比如chrome已经能支持ES的原生模块语法,但是大部分情况下我们还是会用打包工具来在浏览器端实现模块化。
// a.jsexport function hi() {}// main.jsimport {hi} from './a.js'hi()//index.html// 浏览器原生支持使用type=module标注这是一个模块脚本<sciprt type="module">import {hi} from './a.js'hi()</script>
核心功能
使用 use strict 模式
模块有自己的顶级作用域
<script type="module">// 变量仅在这个 module script 内可见let user = "John";</script><script type="module">alert(user); // Error: user is not defined</script>
模块代码仅仅在第一次导入时被解析(运行)
// a.jsalert('hello')// 1.jsimport 'a.js' // 仅仅这里弹框一次// 2.jsimport 'a.js'
模块是引用传递
// admin.jsexport let admin = {name: 'john'}// 1.jsimport {admin} from './admin'admin.name = 'pete'// 2.jsimport {admin} from './admin'console.log(admin.name) // pete// 这里1.js 和 2.js 导入的是模块admin的引用,而不是复制一份admin
模块中this为undefind
<script>alert(this); // window</script><script type="module">alert(this); // undefined</script>
模块脚本是延迟加载的,与 defer 特性对外部脚本和内部脚本的影响相同。也就是说:
<script type="module" src="...">不会阻塞HTML的解析,与其他资源并行加载。- 等到HTML文档就绪后(DOM解析完毕,在DomContentLoaded前)执行。因此可以访问到位于模块脚本后面的DOM元素 ```javascript
相较于下面这个常规脚本:
- 保证脚本的相对顺序,文档中排在前面的先执行<a name="Nm74s"></a>### Async适用于内联脚本对于非模块脚本(即没有 `type=module` )仅适用于引入外部脚本 `src=xxx` 。而模块脚本在内联脚本中也可用,同 `async` 的特性,异步加载,加载完就执行,不会等待任何东西。适用于广告,统计等等逻辑。<a name="C9kfl"></a>### 外部脚本- 加载多次,只执行一次,同模块引入。- 不同源外部脚本,需要有跨域响应头设置 `Access-Control-Allow-Origin`<a name="0LMGn"></a>### 不能裸模块即模块引入的路径需要是相对路径,或者是绝对路径。```javascriptimport _ from 'lodash'// Error了,不借助webpack等,直接在浏览器中这么使用,属于引用了裸模块// 需要使用绝对路径import _ from 'https://cdn/lodash.js'
导入和导出
在声明前导出
声明变量之前导出
export const a = 'a'export function b() {}export class c {}
导出和声明分开
const a = 'a'function b() {}export {a, b}
import * 导入所有
import * as Mod from './mod.js'// 通过Mod.xx 来使用
导入所有看起来方便,但是列明需要导入的项,可以方便现代构建工具做 tree shaking 。
更建议这样 import {a,b} from './mod.js'
import as
导入并重命名一个新的变量名称
// mod.jsexport function a() {}// index.jsimport {a as say} from './mod.js'say()
export as
以新变量名重新导出
// mod.jsexport {a as say};// index.js,这里引入就要用新变量名了import {say} from './mod'
export default
有两种模块形式,一种是导出很多模块,一种是只导出一个模块,这种通常用默认导出
// user.jsexport default class User {}// index.jsimport User from 'user'
如果只有一个默认导出,可以没有类名,函数名
export default class {}export default function() {}//没有这个export default const a = 1
default是一个名称,可以单独使用
function a() {}export {a as default}
同时导出默认和命名模块
// mod.jsexport default class User {}export function sayHi() {}// index.jsimport {default as User, sayHi} from 'mod'// 或者全部导进来import * as mod from 'mod'mod.default // class Usermod.sayHi()
重新导出
说白了就是导入和导出合并
export {say} from 'mod' // 从mod模块中引入并导出出去export {default as User} from 'mod' // 从mod模块中引入默认并重命名为User导出
通常这么做,有一种情况,编写了大量模块,有些功能是导出外部的,有些模块是内部自己使用,可以提供一个index.js入口文件,只暴露出对外使用的方法
重新导出默认的导出
export * from 'mod'// 只会重新导出命名的导出部分,忽略default// 所以也需要导出默认export {default} from 'user'
动态导入
import()函数,返回一个promise。
import() 只是一种语法,不是函数,不能当做一等功能传递,使用apply/call
