ES6模块化

523 阅读5分钟

ES6模块化

ES6模块化简介

ECMA组织参考了众多社区模块化标准,终于在2015年,随着ES6发布了官方的模块化标准,后成为ES6模块化

ES6模块化具有以下的特点

1.使用依赖预声明的方式导入模块

依赖延迟声明

  1. 优点:某些时候可以提高效率
  2. 缺点:无法在一开始确定模块依赖关系(比较模糊)

依赖预声明

  1. 优点:在一开始可以确定模块依赖关系
  2. 缺点:某些时候效率较低

2.灵活的多种导入导出方式

3.规范的路径表示法:

  1. 所有路径必须以./或../开头

4-2. 基本导入导出

模块的引入

注意:这一部分非模块化标准

目前,浏览器使用以下方式引入一个ES6模块文件

<script src="入口文件" type="module">

模块的基本导出和导入

ES6中的模块导入导出分为两种:

  1. 基本导入导出
  2. 默认导入导出

基本导出

类似于CommonJs的 exports.xxx = xxxx

基本导出可以有多个,每个必须有名称

基本导出的语法如下:

export 声明表达式

export {具名符号}

由于基本导出必须具有名称,所以要求导出内容必须跟上声明表达式具名符号

例子:a.js

export var a = 1; //导出a,值为1,类似于CommonJS中的exports.a = 1
export function test() { //导出test,值为一个函数,类似于CommonJS中的exports.test = function (){}

}

export class Person {

}

export const name = "abc"

// 错误导出1
// export 1; 

var age = 18;
var sex = 1;

export { age, sex } //将age变量的名称作为导出的名称,age变量的值,作为导出的值

// 错误导出2
// export {
//    age:2
// }

基本导入

由于使用的是依赖预加载,因此,导入任何其他模块,导入代码必须放置到所有代码之前

对于基本导出,如果要进行导入,使用下面的代码

import {导入的符号列表} from "模块路径"

例子1: 浏览器会自动将导入代码放置到所有代码之前,但是我们编写代码时应该遵守规范写在前面

index.js

console.log(a)
import {a} from './a.js'
// 输出 这里导入的模块a是上面的a.js
1

例子2:不能放在语句中导入

index.js

if (1) {
    console.log(a)
    import {a} from './a.js'    
}

// 输出
Uncaught SyntaxError: Unexpected token '{'

注意以下细节:

  • 导入时,可以通过关键字as对导入的符号进行重命名
    • 例子 index.js
import {a as a2} from './a.js'
console.log(a2)
// 输出
1
  • 导入时使用的符号是常量,不可修改
    • 例子 index.js
import { a } from './a.js'
a = "前端"
console.log(a)

// 输出
Uncaught TypeError: Assignment to constant variable.
  • 可以使用*号导入所有的基本导出,形成一个对象
    • 例子 导入所有时不写别名会报错 index.js
import * from './a.js'
Uncaught SyntaxError: Unexpected identifier
    • 例子 导入所有时写别名 index.js
import * as moduleA from './a.js'
console.log(moduleA)

例子:仅导入模块运行,不使用它内部的任何导出。比如负责初始化的init.js

import "./a.js" 

例子:ES6导入模块也有缓存

b.js

console.log("b模块运行了")

a.js

import "./b.js"

index.js中

import "./b.js";
import "./a.js";
// 输出 只导入一次b模块
b模块运行了

4-3. 默认导入导出

默认导出

每个模块,除了允许有多个基本导出之外,还允许有一个默认导出

默认导出类似于CommonJS中的module.exports,由于只有一个,因此无需具名

具体的语法是

默认导出的数据 可以是数字、字面量、变量都行

export default 默认导出的数据

或(很少用)

export {默认导出的数据 as default}
// 例子
export {1 as default}

由于每个模块仅允许有一个默认导出,因此,每个模块不能出现多个默认导出语句

例子:

a.js

export default 3
export default {
    fn: function () { },
    name: "帅气"
}

// 输出
Uncaught SyntaxError: Identifier '.default' has already been declared

默认导入

需要想要导入一个模块的默认导出,需要使用下面的语法

import 接收变量名 from "模块路径"

类似于CommonJS中的

var 接收变量名 = require("模块路径")

由于默认导入时变量名是自行定义的,因此没有别名一说

例子:

a.js

export var a = 1;
export var b = 2;
export default {
    fn: function () { },
    name: "帅气"
}

index.js

import data from "./a.js" //将a.js模块中的默认导出放置到常量data中
console.log(data)
// 输出
{name: '帅气', fn: ƒ}

如果希望同时导入某个模块的默认导出和基本导出,可以使用下面的语法

import 接收默认导出的变量, {接收基本导出的变量} from "模块路径"

例子:index.js

import data, { a, b } from "./a.js"
console.log(data, a, b)
// 输出
{name: '帅气', fn: ƒ} 1 2

注:如果使用*号,会将所有基本导出和默认导出聚合到一个对象中,默认导出会作为属性default存在

index.js

import * as data from './a.js'
console.log(data)

4-4. ES6模块化的其他细节

1. 尽量导出不可变值

当导出一个内容时,尽量保证该内容是不可变的(大部分情况都是如此)

// 不推荐
export var name = "模块a";
// 推荐
export const name = "模块a";

例子 两种默认导出写法

function method () { 
    name = "module a";
}
export { method as default }
// 通常用这种
export default method;

因为,虽然导入后,无法更改导入内容,但是在导入的模块内部却有可能发生更改,这将导致一些无法预料的事情发生

例子 调用方法修改模块里的变量

a.js

export var name = "模块a";

export default function () {
    name = "module a";
}

index.js

import method, { name } from "./a.js"

console.log(name)
method();
console.log(name)

// 输出
模块a
module a

2. 可以使用无绑定的导入用于执行一些初始化代码

如果我们只是想执行模块中的一些代码,而不需要导入它的任何内容,可以使用无绑定的导入:

import "模块路径"

例子 arrayPatcher会对Array类型的原型链加一些方法,同时也没有export

import "./arrayPatcher.js"

3. 可以使用绑定再导出,来重新导出来自另一个模块的内容

有的时候,我们可能需要用一个模块封装多个模块,然后有选择的将多个模块的内容分别导出,可以使用下面的语法轻松完成

export {绑定的标识符} from "模块路径"

例子 m.js 注释代码为先导入、再导出; 其余是绑定再导出

//import { a, b } from "./m1.js";
//import m2, { k } from "./m2.js"

//export { a, b, k, m2 as default }
//export const r = "m-r"

export { a, b } from "./m1.js"
export { k, default, a as m2a } from "./m2.js"
export const r = "m-r"

未完连载中。。。