第三十一节: ES6 ESModule

2022-02-03  本文已影响0人  心存美好

1. Module 模块化

JavaScript 采用'共享一切'的方式加载代码, 也就是在ES6 之前, JavaScript中定义的一切都共享同一个全局作用域, 这样随着程序复杂度提升的同时,也会带来命名冲突等诸多问题. 因此ES6的一个目标就是解决作用域问题. 也为了使JavaScript 应用程序显得有序, 于是引进了模块

1.1 模块功能

模块功能主要由两个命令构成:export和import。

export命令用于规定模块的对外接口。

import命令用于输入其他模块提供的功能。

一个模块就是一个独立的文件。该文件内部的所有变量,外部无法获取。如果你希望外部能够读取模块内部的某个变量,就必须使用export关键字输出该变量。

2. 模块的使用

注意如果需要测试模块化需要放到服务器环境

  <!-- module模块化  import导入    -->
  <script src="02.js"></script> <!--  这里只是引入了脚本 -->
  <!-- 在浏览器上运行的基本上是前端模块化
在服务器上运行的基本是后端模块化
后台是给内部员工使用,用来添加数据的。前台给用户看的。都是前端
 -->

  <script type="module">
    // module表示模块化。注意:模块化只能在服务器环境运行。服务器环境有端口号,IP地址,协议
    //js的type类型通常是javascript,所以省略,里面就直接写脚本

    import './01.js'  //  这样导入js,只是执行了一遍该js文件,拿不到任何数据,不能使用该js中的变量和函数
    // console.log(a);   //此时a是在01.js中定义声明的,这里没法使用。
    //导出模块:想要使用a,要在定义a的文件里写上export let a =10; 导出的是模块对象,再回到需要的地方拿到这个导出的模块对象
    // import * as aa from './01.js';    //导入理解:从路径'./01.js'中导出模块* ,aa作为这个模块的别名
    // console.log(aa);
    // console.log('模块对象里的变量a为',aa.a);
    import { a } from './01.js'//导出的简单写法,很像解构
    console.log(a);   //很像解构因为前面从后面是对象中拿到a
//对应01.js文件中的代码
export let a =10;
export let b=20;
2.1. export 导出语法

使用export 关键字将模块中的一部分代码暴露给其他模块, 可以将export 关键字放在任何变量, 函数,类声明之前, 将其导出

导出模块

export 东西

2.1.1 export 导出(暴露)

// 单个导出数据(变量)   
export let a = 10;        //导出变量
export let b = 20;
export function add(){    //导出函数
    return a+b
    }
let c = 'c30'
console.log('这里是01.js')
//整体导出,一次性导出多个数据
let a = 10;
let b = 20;
let c = 'c30'      
function add(){    
return a+b
}
add()
export {    //不能按照对象的写法,只能数据的罗列,类似es6的简写方式
    a,
    b,
    add
}

2.1.2 export`命令输出函数或类(class)

// 导出函数
export function multiply(x, y) {
  return x * y;
};

// 导出类
export class Penson{
    constructor(name,age){
        this.name = name;
        this.age = age;
    }
}

2.1.3 as关键字重命名

通常情况下export 输出的变量, 函数或者类就是本来的名字,但是有时,我们可能并不希望使用它们的原始名称. 此时就可以通过as关键字修改导出元素的名称.

function v1() { ... }
function v2() { ... }

export {
  v1 as streamV1,
  v2 as streamV2,
};

2.1.4 特别注意

需要特别注意的是,export命令规定的是对外的接口

// 报错
export 1;

// 报错
var m = 1;
export m;

//正确写法
// 写法一
export var m = 1;

// 写法二
var m = 1;
export {m};

// 写法三
var n = 1;
export {n as m};

同样的,function的输出,也必须遵守这样的写法

// 报错
function f() {}
export f;

// 正确
export function f() {};

// 正确
function f() {}
export {f};

动态绑定

export语句输出的接口,与其对应的值是动态绑定关系,即通过该接口,可以取到模块内部实时的值。

  <!-- 动态数据导出,异步异出 -->
  <!-- 导入文件 -->
  <script type='module'>
    import { a, b } from './01.js';
    console.log(a, b);   //a,b不是新值
  </script>
  
  <!-- 导出文件 -->
  let a = 10;
 let b = 20;
 function add() {
    return a + b
 }
 setTimeout(() => {   //3秒后导出
    a = 100;
    b = 200;
 }, 3000)
 export { a, b, add }

 <!-- 动态数据导出,异步异出 -->
   <!-- 导入文件 -->
  <script type='module'>
    import { a, b } from './01.js';
    console.log(a, b);   //a,b不是新值

    setTimeout(()=>{
      console.log(a, b);    //这里的值就是更新的了
    },4000)
  </script>
  
    <!-- 导出文件 -->
  let a = 10;
 let b = 20;
 function add() {
    return a + b
 }
 setTimeout(() => {   //3秒后导出
    a = 100;
    b = 200;
 }, 3000)
 export { a, b, add }
2.1.5 整体导出

发现导入都是需要花括号的,如果希望导入不用花括号,那就需要改变导出方式

// 导出时使用export default
let a;
export default a = 10;

// 导入时可以不用花括号
<script type="module">
import a from './modules/1.js'
// 默认导出 export default  注意一个模块只能有一个默认导出
// export default [ 'a', 'b', 'add' ]  //也可以导出对象,函数等  只能有一个默认导出
//export default function aa(){
//    console.log(111);
//} 

导入文件
  <script type='module'>
    import aa from './01.js';
    console.log(aa);
  </script>
// 同时有默认导出 export default  和变量导出
export let a=10;
export let b =20;
export default function aa(){
    console.log(111);
}

导入文件
  <script type='module'>
    // 导入方法一
    import * as aa from './01.js';
    console.log(aa);
    console.log(aa.a);
    console.log(aa.b);
    console.log(aa.default);
    // 导入方法二
    import def, { a, b } from './01.js';   //大括号里面的是有名字的,外面的是默认导出的
    console.log(a);
    console.log(b);
    console.log(def);
  </script>
2.2. 导入模块

使用export命令定义了模块的对外接口以后,其他 JS 文件就可以通过import命令加载这个模块。

引入模块
import 模块路径

    //正常导入
    import * as js01 from './01.js'   
    console.log(js01);   //打印导入的模块对象
    console.log(js01.a); //这样导入的数据需要打点才能使用,通常不这么用
    
   //类似解构的简单的导入方式
    import {a,b,add}from './01.js'  
    console.log(a);  //不用打点,直接使用
    console.log(a,b); 
    console.log(add());
<script type="module">
    import "./modules/1.js";   // 这种写法只是相当于引入一个文件,也叫无绑定导入
    import {a} from "./modules/1.js"   // 导入模块中导出的a 
</script>
import {firstName, lastName, year} from './profile';
function setName(element) {
  element.textContent = firstName + ' ' + lastName;
}

上面代码的import命令,用于加载profile.js文件,并从中输入变量。import命令接受一对大括号,里面指定要从其他模块导入的变量名。大括号里面的变量名,必须与被导入模块(profile.js)对外接口的名称相同。

    import {a,b,add}from './01.js'   
    a =100   //导入的数据相当于常量const a =10 ,不能随意再赋值,会报错

2.2.1 as关键字改变量名

//导出文件里的内容
let a = 10;
let b = 20;
function add(){
    return a+b
    }
let c = 'c30'
export {
    a as aaa,     //将变量a通过as改名为aaa,导入时全部使用aaa
    b,
    add as bdd
}

//导入文件里的内容
    import {aaa,b,bdd}from './01.js'   
    console.log(aaa);     //使用as后的变量名
    console.log(b);
    console.log(bdd());

import后面的from指定模块文件的位置,可以是相对路径,也可以是绝对路径,.js后缀可以省略。

2.2.2 import 提升
注意,import命令具有提升效果,会提升到整个模块的头部,首先执行。

// 导入前引用
export let a =10;

 导入文件
   <script type='module'>
    console.log('我在导入之前的', a * a);   //导入前引用
    import { a } from './01.js';  //程序会将import提升,但最好自己写在最前面
  </script>
foo();

import { foo } from 'my_module';
//import的执行早于foo的调用。这种行为的本质是,import命令是编译阶段执行的,在代码运行之前。

2.2.3 import不能使用表达式
由于import是静态执行,所以不能使用表达式和变量,这些只有在运行时才能得到结果的语法结构。

// 报错   不能拆成表达式
import { 'f' + 'oo' } from 'my_module';

// 报错  导入不能使用表达式
let module = 'my_module';  
import { foo } from module;

// 报错   import只能使用在顶层
if (x === 1) {
  import { foo } from 'module1';
} else {
  import { foo } from 'module2';
}

import { foo } from 'my_module';
import { bar } from 'my_module';

// 等同于
import { foo, bar } from 'my_module';

2.2.4 模块化整体加载

除了指定加载某个输出值,还可以使用整体加载,即用星号(*)指定一个对象,所有输出值都加载在这个对象上面。

注意,模块整体加载所在的那个对象,不允许运行时改变。下面的写法都是不允许的。

2.2.5 import 特点总结

  1. 可以是相对路径也可以是绝对路径
    import "./modules/1.js";
  2. import模块只会导入一次,无论你引入多少次。模块的导入导出有缓存机制,只有第一次导入会执行一遍并且将数据放入缓存,后面的所有的导入直接到缓存中取数据使用,但不会再执行了。
  3. import "./modules/1.js"; 如果这么用,就相当于引入了一个js文件
  4. 导入模块中的一个变量或常量
import {a,b,c} from "./modules/1.js"

5.导入时可以取别名使用

// 导入时也可以修改别名
export let a =0;

导入的文件
  <script type='module'>
    let a = 1234;//导入的变量与模块中的变量a重名了
    console.log('a还是原来的a',a);   //a已经存在,所以要改别名
    import {a as aa} from './01.js'; 
    console.log('aa是导入时修改的别名',aa); 
  </script>
import {a as aa,b as bb,c as cc} from "./modules/1.js"

6.导入这个模块导出的对象

import * as obj from './modules/1.js';
// 这个时候你就以使用obj这个Module对象了
console.log(obj.a);

7.import 的导入语句会进行提升,提升到最顶部,首先执行

console.log(a + b);   // 能正常获取到a,b的值
import {a,b} from './1.js'

8.出去的模块内容,如果里面有定时器发生导出内容的改变,改变的内容会根据定时器自动同步缓存中的内容,所以外边导入的内容也会实时发生改变,不像Comment会缓存。

2.2.6 import() 动态引入

默认的import语法不能写在if之类的判断里的,因为是静态引入,先引入在使用

// 这种写法是错的
let a = 12;
if(a == 12){
    import {a} from './1.js'
}else{
    import {a} from './2.js'
}

import()是动态引入 类似于node里面的require

  //动态导入模块(按需导入)import()返回promise
  let flag=false;
  if(flag){
    import('./01.js')
    .then(data=>{
      console.log('data',data);
    })
  }else{
    import('./02.js')
    .then(res=>{
      console.log('res',res);
    })
  }

  //动态导入模块返回promise优化写法
    let flag = true;
    let url = flag ? './01.js' : './02.js'
    import(url)
      .then(res => {
        console.log('res', res);
      })

优点:

  1. 按需加载
  2. 可以写在if里面,判断加载
  3. 路径都可以是动态的

例子:

/ 这里是1.js
let a = 20;
let b = 30;
export {
  a, b
}

// 这里是2.js
import { a, b } from './1.js';
console.log(`a的值是:${a}, b的值是:${b}`);

const sum = () => {
  console.log(`我是a,b的和:${a + b}`);
  return a + b;
}
const show = () => {
  console.log('show 执行了')
  return 1;
}

function Person(name, age) {
  this.name = name;
  this.age = age;
  this.showName = function () {
    return `我的名字是${this.name}`;
  }
}

export {
  sum,
  show,
  a,
  b
}
export default { Person }

// 这里是HTML导入
import mod, { sum, show, a, b } from "./2.js"
console.log(mod);

let p = new mod.Person("wuwei", 18);
console.log(p);
console.log(p.showName());

show();

sum();

console.log(a, b);
// 所有导出只能在顶层导出
if(true){    
    export let a=10;    //这不是顶层所以报错,不能通过条件判断和函数再导出,嵌套导出会报错
}else{
    export let a=200;
}

导入文件
  <script type='module'>
    import {a} from './01.js';   //大括号里面的是有名字的,外面的是默认导出的
    console.log(a); 
  </script>
// 导出只能在顶层导出,可以这样写
let a =0;
if(true){    
    a=10;    
}else{
    a=200;
}
export{a}    //整体导出才可以

导入文件
  <script type='module'>
    import {a} from './01.js';   //大括号里面的是有名字的,外面的是默认导出的
    console.log(a); 
  </script>
上一篇下一篇

猜你喜欢

热点阅读