2.4.ES6 Module

ES Module和CommonJS的模块化有一些不同之处:

  1. 一方面它使用了import和export关键字;
  2. 另一方面它采用编译期的静态分析,并且也加入了动态引用的方式(import()函数)

ES Module模块采用export和import关键字来实现模块化:

  1. export负责将模块内的内容导出;
  2. import负责从其他模块导入内容;

了解:采用ES Module将自动采用严格模式:use strict

 

2.4.1.使用的时候可能遇到的错误

使用ES6 Module时要在script标签里添加type=“module”来开启模块化

当打开浏览器的时候 , 如果时使用默认浏览器打开会报以下错误

 

 

esbuild 与node版本_标识符

这个在MDN上面有给出解释:

https://developer.mozilla.org/zh-CN/docs/Web/JavaScript/Guide/Modules

你需要注意本地测试 — 如果你通过本地加载Html 文件 (比如一个 file:// 路径的文件), 你将会遇到 CORS 错误,因 为Javascript 模块安全性需要。

你需要通过一个服务器来测试。VSCode中有一个插件:Live Server

 

2.4.2.export关键字

export关键字将一个模块中的变量、函数、类等导出;

导出的方式有三种:

  1. 在声明的前面直接加export关键字
  2. 将所有需要导出的标识符放在export{}
  • 注意:这里的 {}里面不是ES6的对象字面量的增强写法,{}也不是表示一个对象的;
  • 所以: export {name: name},是错误的写法
  1. 导出时给标识符起别名
// 第一种导出的方式
// export const name = 'xxx';
// export const age = 18;
// export function say (value) {
//   console.log('value');
// };


const name = 'xxx';
const age = 18;
function say (value) {
  console.log('value');
}
// 第二种导出的方式 , 注意导出的不是es6中对象的方式 , 这里导出的是
export {
  name,
  age,
  say
};

// 第三种 , 如果使用了起别名的方式,那么,在使用import引入的时候,必须使用别名,因为标识符的名字已经改变了
//此时导入的使用应该是 import {fname , fage , fsay} from './module/bar.js'
export {
  name as fname,
  age as fage,
  say as fsay
};

2.4.3.import关键字

import关键字负责从另外一个模块中导入内容

 

导入内容的方式也有多种

  1. import {标识符列表} from '模块';
  2. :导入时给标识符起别名
  3. 通过 * 将模块功能放到一个模块功能对象(a module object)上
// 第一种方式
// import { name, age, say } from './module/bar.js';
// console.log(name);
// console.log(age);
// say('hello world');

// 第二种方式
// import { name as fname, age as fage, say as fsay } from './module/bar.js';
// console.log(fname);
// console.log(fage);
// fsay('hello world');


// 第三种方式
import * as info from './module/bar.js'
console.log(info.name);
console.log(info.age);
info.say('hello world');import { name, age, say } from './module/bar.js'

2.4.4.default用法

前面我们学习的导出功能都是有名字的导出(named exports):

  1. 在导出export时指定了名字;
  2. 在导入import时需要知道具体的名字;

还有一种导出叫做默认导出(default export)

  1. 默认导出export时可以不需要指定名字;
  2. 在导入时不需要使用 {},并且可以自己来指定名字;
  3. 它也方便我们和现有的CommonJS等规范相互操作;

 

在一个模块中,只能有一个默认导出(default export);

 

2.4.4.import()函数

当时用按需加载的时候,如果想要当满足某种条件的时候,然后再加载一个模块,如:

let flag = true;
//当flag为true的时候,才能加载这个模块 
if (flag) {
  import { name, age } from './module/bar.js';
}

像这样做是错误的,因为import { name, age } from './module/bar.js';是帮助我们在解析阶段确定依赖的,而现在,直接放在了执行阶段,也就是前面的代码执了,然后再来加载这个模块,但是执行的时候并不认识这种语法。

 

补充:

为什么commonJS中可以在执行阶段使用require()?

因为require()是一个函数

 

但是es6也为我们提供了import()函数,import函数是个异步函数,

使用方法

let flag = true;
//当flag为true的时候,才能加载这个模块 
if (flag) {
  import('./module/bar.js').then(res => {
    console.log(res.name, res.age);
    res.say('hello world');
  }).catch(err => {
    console.log(err);
  })
}

2.4.5.ES Module加载过程

ES Module加载js文件的过程是编译(解析)时加载的,并且是异步的:

  1. 编译时(解析)时加载,意味着import不能和运行时相关的内容放在一起使用:
  2. 比如from后面的路径需要动态获取;
  3. 比如不能将import放到if等语句的代码块中;
  4. 所以我们有时候也称ES Module是静态解析的,而不是动态或者运行时解析的;

异步的意味着:JS引擎在遇到import时会去获取这个js文件,但是这个获取的过程是异步的,并不会阻塞主线程继 续执行;

  1. 也就是说设置了 type=module 的代码,相当于在script标签上也加上了 async 属性
  2. 如果我们后面有普通的script标签以及对应的代码,那么ES Module对应的js文件和代码不会阻塞它们的执行;

<!DOCTYPE html> <html lang="en"> <head>  <meta charset="UTF-8">  <meta name="viewport" content="width=device-width, initial-scale=1.0">  <title>Document</title> </head> <body>  <script src="./index.js" type="module"></script>  <script>    console.log('加载了');  </script> </body> </html>

 

 

esbuild 与node版本_esbuild 与node版本_02

 

 

ES Module通过export导出的是变量本身的引用:

export在导出一个变量时,js引擎会解析这个语法,并且创建模块环境记录(module environment record);

  1. 模块环境记录会和变量进行 绑定(binding),并且这个绑定是实时的;
  2. 而在导入的地方,我们是可以实时的获取到绑定的最新值的;

所以,如果在导出的模块中修改了变化,那么导入的地方可以实时获取最新的变量;

注意:在导入的地方不可以修改变量,因为它只是被绑定到了这个变量上(其实是一个常量)

思考:如果bar.js中导出的是一个对象,那么main.js中是否可以修改对象中的属性呢?

答案是可以的,因为他们指向同一块内存空间;

如const name='xxx'; 不能修改name

但是const info = {name:'xxx' , age:18} , 就可以修改name和age

 

赋值过程

//bar.js
let name = 'xxx';
const age = 18;
function say (value) {
  console.log(value);
}

setTimeout(() => {
  name = 'zdd'
}, 1000);
export {
  name,
  age,
  say
}

//main.js
import { name, age, say } from './module/bar.js';
console.log(name);
console.log(age);
say('hello world');
setTimeout(() => {
  console.log(name);
}, 2000)

 

 

esbuild 与node版本_模块化_03

2.4.6.Node对ES Module的支持

在最新的Current版本(v14.13.1)中,支持es module我们需要进行如下操作:

方式一:在package.json中配置 type: module(后续学习,我们现在还没有讲到package.json文件的作用)

方式二:文件以 .mjs 结尾,表示使用的是ES Module;

这里我们暂时选择以 .mjs 结尾的方式来演练