ES Modules
ES Modules 特性
- 自动采用严格模式,忽略 'use strict'
- 每个 ESM 模块都是单独的私有作用域
- ESM 是通过 CORS 去请求外部 JS 模块的
- ESM 的 script 标签会延迟执行脚本
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>ES Module - 模块的特性</title>
</head>
<body>
<!-- 通过 script 添加 type = module 的属性,就可以以 ES Module 的标准执行其中的 JS 代码 -->
<script type="module">
console.log('this is es module')
</script>
<!-- 1. ESM 自动采用严格模式,忽略 'use strict' -->
<!-- script[type=module] -->
<script type="module">
console.log(this) // undefined
</script>
<script>
console.log(this) // window
</script>
<!-- 2. 每个 ES Module 都是运行在单独的私有作用域中 -->
<script type="module">
var foo = 100
console.log(foo)
</script>
<script type="module">
console.log(foo) // foo is not defined
</script>
<!-- 3. ESM 是通过 CORS 的方式请求外部 JS 模块的 -->
<script type="module" src="https://libs.baidu.com/jquery/2.0.0/jquery.min.js"></script>
<!-- Access to script at 'https://libs.baidu.com/jquery/2.0.0/jquery.min.js' from origin 'null' has been blocked by CORS policy: No 'Access-Control-Allow-Origin' header is present on the requested resource. -->
<!-- 支持 CORS -->
<script type="module" src="https://unpkg.com/[email protected]/dist/jquery.min.js"></script>
<!-- 4. ESM 的 script 标签会延迟执行脚本,相当于 defer -->
<script type="module" src="demo.js"></script>
<p>需要显示的类容</p>
</body>
</html>
ES Modules 导出
// ./module.js
const foo = 'es modules'
export { foo }
// ./app.js
import { foo } from './module.js'
console.log(foo) // => es modules
export default
- export default 向外暴露的成员,可以使用任意的变量名来接收
- 在一个模块中,export default 只允许向外暴露1次
- 在一个模块中,可以同时使用 export default 和 export 向外暴露成员
导出:
var info = {
name: 'zs',
age: 20
}
export default info
导入:
aaa 可以任意命名,可以在控制台输出 info 对象
import aaa, from './module.js'
console.log(aaa)
export var name = ‘**’
- 使用 export 向外暴露的成员,只能使用 { } 的形式来接收
- 使用 export 导出的成员,导入时,必须严格按照导出时候的名称使用 {} 按需接收
- 使用 export 导出的成员,如果就想换个名称来接收,可以使用 as 来起别名
导出的第一种写法
导出:
export var name = 'foo module'
export function hello() {
console.log('hello')
}
export class Person {}
导入:
import { name, hello, Person } from './module.js'
导出的第二种写法(推荐)
能看直观描述该模块向外提供了哪些成员
var name = 'foo module'
function hello() {
console.log('hello')
}
class Person {}
export { name, hello, Person }
导出重命名
导出:
export {
name as default,
hello as fooHello
}
导入:
// default 是 js 的关键词,需重命名
import { fooName as fooName, fooHello } from './module.js'
导出注意事项
-
export {}
为固定语法,并不是对象字面量,并非导出了一个对象var name = 'robert' var age = 18 var obj = { name, age } export { name, age } // 导出一个对象的写法 export default { name, age }
-
导出的并非值,而是导出值所存放的地址,外部引入的值,会受到导出模块内部修改的影响
var name = 'robert' var age = 18 setTimeout(() => { name = 'robert1' }, 1000); export { name, age }
-
import {}
并非解构,而是固定的语法import { name, age } from './module.js' console.log(name, age)
-
导入的成员是只读的,并不能修改,可利用该特性,解决应用中常量的问题,例如全局的配置文件
name = 'tom' // app.js:20 Uncaught TypeError: Assignment to constant variable. setTimeout(() => { console.log(name, age) // robert1 18 }, 1500);
导入注意事项
// ./module.js
var name = 'robert'
var age = 18
console.log('module action')
// 同时导出命名成员和默认成员
export {
name,
age
}
export default 'default export'
-
导入的路径,必须使用完整的文件名称,不能省略 .js
// bad import { name } from './module' console.log(name) // GET http://localhost:3000/04-import/module net::ERR_ABORTED 404 (Not Found)
// good import { name } from './module.js'
-
import index.js
, 不可省略index.js
文件 ,不同于common.js
可省略index.js
// bad import { lowercase } from './utils' console.log(lowercase('HHH')) // GET http://localhost:3000/04-import/utils/ net::ERR_ABORTED 404 (Not Found)
// good import { lowercase } from './utils/index.js'
-
引用相对路径的文件,./ 不可省略,因为省略 ./ 后,文件则以字母开头,那么 ESM 会以为是引用第三方库
// bad import { name } from 'module.js' console.log(name) // Failed to resolve module specifier "module.js". Relative references must start with either "/", "./", or "../".
// good import { name } from './module.js'
-
引用文件也可以使用 / 开头的绝对路径,表示从网站根目录下开始寻找
import { name } from '/04-import/module.js'
-
引用文件也可以使用完整的 url ,意味着我们可以直接引用 cdn 上的一些模块文件
import { name } from 'http://localhost:3000/04-import/module.js'
-
如只想执行某个模块,而不需要提取模块中的某个成员,那么保持 import 后的 {} 为空即可
import { } from './module.js' // 以上简写语法 import './module.js'
-
使用 * 号提取出导出模块中的所有成员
import * as mod from './module.js' console.log(mod) // Module {Symbol(Symbol.toStringTag): 'Module'}
-
动态导入
// 不能 import from 一个变量 var modulePath = './module.js' import { name } from modulePath console.log(name) // Unexpected identifier // import 关键词只能出现在最顶层 if (true) { import { name } from './module.js' // 导入声明只能在模块的顶层使用 } import('./module.js').then(function(module) { console.log(module) })
-
同时导入命名成员和默认成员
import { name, age, default as title } from './module.js' // 以上简写,左侧导入默认成员命名可任意取 import title, { name, age } from './module.js' console.log(name, age, title)
导出导入成员
一般在 index 文件使用较多,将某个散落的模块组织在一起将其导出给外部一起使用
// ./components/avatar.js
export var Avatar = 'Avatar Components'
// ./components/button.js
export var Button = 'Button components'
// ./components/index.js
import { Button } from './button.js'
import { Avatar } from './avatar.js'
export {
Button,
Avatar
}
// 以上可简写成如下
export { Button } from './button.js'
export { Avatar } from './avatar.js'
导入:
// import { Button } from './components/button.js'
// import { Avatar } from './components/avatar.js'
// console.log(Button)
// console.log(Avatar)
import { Button, Avatar } from './components/index.js'
console.log(Button)
console.log(Avatar)
浏览器环境 Polyfill
ES Module 是2014年才被提出来的,意味着早起的浏览器不支持该特性,如 IE 和某些国产浏览器,那么就需要使用 Polyfill 让浏览器直接支持 ES Module 特性。
browser-es-module-loader
通过 unpkg 查找对应的 npm
包的 cdn
服务链接
- https://unpkg.com/browser-es-module-loader
- https://unpkg.com/[email protected]/dist/
- 点击
view Raw
复制链接地址
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>ES Module 浏览器环境 Polyfill</title>
<!-- 支持 module 的浏览器,设定上就不会执行 nomodule 属性的 script 脚本,所以它只会跑上方的 app.js 脚本 -->
<!-- 而老破旧的浏览器不支持 type="module",会跳过这个 script 标签;同时又由于它不认识 nomodule 属性,反倒会执行 nomodule script 里的 classic-bundle.js 文件了 -->
<script nomodule src="https://unpkg.com/[email protected]/dist/polyfill.min.js"></script>
<script nomodule src="https://unpkg.com/[email protected]/dist/babel-browser-build.js"></script>
<script nomodule src="https://unpkg.com/[email protected]/dist/browser-es-module-loader.js"></script>
</head>
<body>
<!-- https://unpkg.com/[email protected]/dist/ -->
<!-- unpkg 简介: https://www.jianshu.com/p/671c00b5c65d -->
<script type="module">
import { foo } from './module.js'
console.log(foo)
</script>
</body>
</html>
var foo = 'foo'
export {
foo
}
附录
Browser-sync 安装与使用
Browsersync能让浏览器实时、快速响应您的文件更改(html、js、css、sass、less等)并自动刷新页面。
安装 BrowserSync
npm install -g browser-sync
启动 BrowserSync
browser-sync start --server --files "*" / browser-sync . --files **/*.js