ES6模块化理解

ES6之前,虽然有commonJS和AMD两种模块加载方案,但是JS在语言标准的层面上是没有模块体系的

为了解决这一问题,便引入了export import命令来实现模块化

ES6模块化与commonJS区别:

一、

// CommonJS模块
let { state, exiets, redafile } = require('fs');

// 等同于
let _fs = require('fs');
let stat = _fs.stat;
let exists = _fs.exists;
let readfile = _fs.readfile;

commonJS require的加载方式为‘运行时加载’,即require资源之后,生成一个对象_fs,然后再去_fs中读取需要的内容,只有在运行时才能得到这个引入的对象

// ES6模块
import { stat, exists, readFile } from 'fs';

上面代码的实质是从fs模块加载 3 个方法,其他方法不加载。这种加载称为“编译时加载”或者静态加载,即 ES6 可以在编译时就完成模块加载,效率要比 CommonJS 模块的加载方式高。

因为是编译时加载,所以使用import命令时不能使用表达式和变量,这些只有运行时才能的得到结果的语法结构

二、

export 输出的接口,与其对应的值是动态绑定关系,即暴露的接口对应的值发生变化,import导入时是会动态更新的

commonJS模块输出的是值的缓存,不存在动态更新

export let num = 12
setTimeout(() => num = 28, 500)

export输出的变量num,值为12,500ms后,变为28

export命令

一个模块就是一个独立的文件,该文件内部的所有变量,在外部都是访问不到的,如果想要被外部访问到,那么就需要使用export命令导出

export导出变量:

export let a = 'Luffy'
export let b = 'Zero'
export let c = 'Sanji'

还可以写成这样:

let a = 'Luffy'
let b = 'Zero'
let c = 'Sanji'
export {a, b, c}

导入时使用的方法都是一样的:

import {a, b, c} from '路径'

export导出函数和类:

export class myModule {
   constructor(){
     ...
   }
}
  
//或
class myModule{
  
}
export {
		myModule
}
export function add(x, y){
  return x + y
}

export 为导出的变量重命名

let a = 'Jack'
let b = 18
export {
	a as name,
	b as age,
	b as length
}

使用as 关键字 ,重新命名变量a和b的对外接口,并且变量b可以用不同的名字输出两次

export命令不能用于块级作用域中

function foo() {
  export default 'bar' // SyntaxError
}
foo()

将export命令放在函数中,会报错,因为export是编译时执行的,放在块级作用域中无法做静态优化

export defaut 命令

使用import命令时,我们需要知道需要加载的变量名和函数名,否则无法正确加载。

使用export default命令,可以使我们为模块指定默认输出,在导入时在为其命名

//a.js 导出一个匿名函数
export default funciton (){
  console.log('function')
}
//导入时可以为其随意命名,且import后面无需使用大括号
import fn from './a.js'
fn()    //funciton

export default命令用在非匿名函数前,也是可以的。

// export-default.js
export default function foo() {
  console.log('foo');
}

// 或者写成

function foo() {
  console.log('foo');
}

export default foo;

上面代码中,foo函数的函数名foo,在模块外部是无效的。加载的时候,视同匿名函数加载。

export default 本质就是导出一个叫做default 的变量, 并在语句后面为其赋值

因此:

// 正确
export var a = 1;

// 正确
var a = 1;
export default a;

// 错误
export default var a = 1;

import命令

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

// main.js
import { name, age} from '路径';

console.log(name, age)

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

重命名

同样是使用as关键字

import {name as username} from "路径"

console.log(name)    // 错误
console.log(username)   //正确

import载入的变量都是只读的

import载入的变量不允许修改,但是分数据类型

基本数据类型不允许修改

引用数据类型可以修改属性,但不能改变其指向,且修改过后,其他引入该变量的模块也会得到同样的结果

//基本数据类型
import {a} from '路径'
a = 12    //报错
//引用数据类型
import {a} from '路径'
a.name = 'Chopper'    //可以
a = {name: 'Chopper'}   //报错

路径

import路径可以是绝对路径,也可以是相对路径

但是,如果不带有路径名,只是一个模块名,必须通过相应配置,告知JS引擎该模块位置

变量提升

import语句存在变量提升,即会提升到模块头部,优先执行

say()
import {say} from '路径'

上面的代码不会报错,因为import的执行早于say的调用。这种行为的本质是,import命令是编译阶段执行的,在代码运行之前。

不能使用表达式和变量

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

// 报错
import { 'f' + 'oo' } from 'my_module';

// 报错
let module = 'my_module';
import { foo } from module;

// 报错
if (x === 1) {
  import { foo } from 'module1';
} else {
  import { foo } from 'module2';
}

上面三种写法都会报错,因为它们用到了表达式、变量和if结构。在静态分析阶段,这些语法都是没法得到值的。

加载整个模块

import命令可以加在整个模块,即用星号(*)指定一个对象,所有输出值都加载在这个对象上面。

//a.js
export function add(x,y){
  return x+y
}
export function multiply(x,y){
  return x*y
}
import * as methods from './a.js'

console.log(methods.add(1,2), methods.multiply(1,2))   // 3, 2

import方法

用于export import命令是编译时执行的,只能运行在模块顶层,不能运行在块级作用域中

因此导致无法在运行时加载模块。在语法上,条件加载就不可能实现。如果import命令要取代 Node 的require方法,这就形成了一个障碍。因为require是运行时加载模块,import命令无法取代require的动态加载功能。

ES2020提案中 引入import()函数,用于实现动态加载模块

import(specifier)

上面代码中,import函数的参数specifier,指定所要加载的模块的位置。import命令能够接受什么参数,import()函数就能接受什么参数,两者区别主要是后者为动态加载。

与require方法不同的是,import方法是异步的,返回一个promise对象,require方法是同步的

适用场合

下面是import()的一些适用场合。

(1)按需加载。

import()可以在需要的时候,再加载某个模块。

{
  path: '/login',
  component: () => import('@/views/login/index'),
  hidden: true
},

在我们实现路由懒加载的过程中,使用import() 命中 /login路由时,才会加载对应的模块

(2)条件加载

import()可以放在if代码块,根据不同的情况,加载不同的模块。

if (condition) {
  import('moduleA').then(...);
} else {
  import('moduleB').then(...);
}

上面代码中,如果满足条件,就加载模块 A,否则加载模块 B。

(3)动态的模块路径

import()允许模块路径动态生成。

import(f())
.then(...);

上面代码中,根据函数f的返回结果,加载不同的模块。

注意点

import()加载模块成功以后,这个模块会作为一个对象,当作then方法的参数。因此,我们可以随意操作得到的内容

参考:阮一峰-ES6入门

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值