乐趣区

关于前端:浏览器端模块化方式es-module详解

在 es module 呈现之前还有社区推出 amd 和 cmd 的标准,这两者还有其特定的编写形式,应用起来不算很不便。es module 被官网推出来就成为了浏览器端实现模块化的一个很好的计划。

想要在浏览器端应用 es module,首先在 html 当中引入 js 文件的时候,就须要将 script 标签中的 type 设置为 module

index.html  
<script src="b.js" type="module"></script>

这样浏览器能力执行应用 es module 的 js 文件,定义之后就能够在对应的 js 文件中应用模块化的形式来编写文件,导出和导入的形式有几种,但都是雷同的关键字,export 与 import,一起来看看能够如何定义导入导出。

第一种,间接导出定义的变量,这种形式导出的内容互不关联,实用于导出本人定义的常量,redux 中定义 action 就常常应用这种导出

a.js
export const name = 'alice'
export const age = 16

b.js
import {name, age} from "./a.js";
console.log(name, age) // alice 16

第二种,先定义变量,再应用 export 一起导出,导入形式能够应用下面的形式,也能够通过一个 * 来将所有的导出内容定义为一个对象,从对象中再去取值,redux 中定义的 reducer、action 在 index.js 中导出会常常应用这种形式

a.js
const name = 'alice'
const age = 16
export {name, age}

b.js
import * as obj from "./a.js";
console.log(obj.name, obj.age) // alice 16

第三种,给导出的变量取别名,导入的变量同样能够取别名,当名字发生冲突时、导出变量名太长时,都能够取个别名,取完别名之后,原先的名字就不可用了

a.js
const name = 'alice'
const age = 16

export {name as myName, age as myAge}

b.js
import {myName as aliceName, myAge} from "./a.js";

console.log(aliceName, myAge)  // alice 16

第四种,默认导出,默认导出在一个 js 文件中只容许存在一个,默认导出能够不必定义变量名,在导入的时候能够随便起名,并且导入的时候不须要加 {},这样的定义形式在编写 redux 中的 reducer 函数时很常见

a.js
export default function(){return 'hello world'}

b.js
import foo from './a.js'

console.log(foo()) // hello world

第五种,合并导出,在 b.js 文件导入 a.js 文件中导出的内容,b.js 文件不对导入的内容做任何操作,间接导出,最初由 index.js 导入 b.js 并进行解决

a.js
export const name = 'alice'

b.js
export {name} from './a.js'

index.js
import {name} from './b.js'

console.log(name) // alice

以上是 es module 的具体的语法体现,导入导出的形式有很多,能够依据具体须要的场景进行判断和应用,另外,es module 还有一些特点。

1、异步加载,当 script 标签中定义 type=”module” 之后,相当于给 js 标签加上了 async 的标识,代表异步加载资源,不会阻塞其它内容的执行,依照如下代码,打印的 hi 有可能是在引入的 index.js 文件之前,要依据 index.js 的执行速度来判断。

<script src="index.js" type="module"></script>
<script type="text/javascript">
    console.log('hi')
</script>

2、编译时解析,简略来说 javascript 的执行过程须要将原代码编译成形象语法树,运行的时候再转成机器可辨认的语言,在编译阶段解析数据,并不知道该不该加载此 js 文件,只有等到文件运行时,才晓得文件里具体逻辑的执行过程,所以不可能在编译时解析的模块化形式呈现相似条件判断,动静引入等代码

const flag = true
if(flag){import xxx from './a.js'}

如果真的须要依据一些条件才执行代码,能够通过 require 函数来动静的引入,require 函数执行完是一个 promise 对象,能够通过 then 办法来获取所须要的数据

const flag = true

if(flag){import('./b.js')
  .then(({name})=>{console.log(name)
  })
}

3、export 关键字前面跟的大括号并不是代表对象,在对象中也没有通过 as 取别名这样的形式,如果咱们尝试以下把它当成对象来导出,肯定是会报错的

let name = 'alice'
export {name: name}

export 导出的 name 就对应着 name 这个变量,如果批改 name 的值,export 导出的内容会发生变化,import 导入的内容也会发生变化

a.js
let name = 'kiki', age = 18

setTimeout(()=>{name = '嘻嘻嘻'}, 1000)

export {
  name,
    age
}

b.js
import {name, age} from './a.js'

console.log(name)

setTimeout(()=>{console.log(name)
},2000)

// 顺次打印 kiki 嘻嘻嘻 

export 导出的内容有一个模块环境记录,用来记录导出时更改的变量,当变量更改时,应用新的变量值替换旧变量值

然而不能够反向的批改,因为 import 导入的内容是一个通过 const 定义的常量,常量是不能够被批改的,以下操作是不可行的

import {name} from './a.js'
name = '哈哈哈哈'

一般而言,咱们都是在浏览器端应用 es module,如果想要在 node 端编写 es module 代码,能够有两种形式,一种是在 package.json 中配置 type:module,另一种是间接把 js 文件的后缀名为改为 .mjs

node 端罕用的模块化形式是 commonjs,同样是模块化,那么 es module 和 commonjs 之间是否能相互调用呢,看看如下代码

a.mjs
const name = 'alice'
const age = 18

export {
  name,
  age
}

b.js
const a = require('./a.mjs')

console.log(a)

以上代码执行会报错 Must use import to load ES Module,而如下的形式在高版本的 nodejs 中是能够的

a.js
const name = 'alice'
const age = 18

module.exports = {
  name,
  age
}

b.js
import b from './b.js'

console.log(b) // {name: 'alice', age: 18}

以上就是浏览器端模块形式 es module 的概念与用法,模块化可能更好的将代码分块并复用,nodejs 端也有罕用实现模块化的形式,即 commonjs,如果不相熟能够看看这篇文章 -> nodejs 端模块化形式 comomjs 详解

退出移动版