浏览器端模块化方式es module详解

在es module出现以前还有社区推出amd和cmd的规范,这二者还有其特定的编写方式,使用起来不算很方便。es module被官方推出来就成为了浏览器端实现模块化的一个很好的方案。javascript

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

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

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

第一种,直接导出定义的变量,这种方式导出的内容互不关联,适用于导出本身定义的常量,redux中定义action就常用这种导出node

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 中导出会常用这种方式json

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

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

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函数时很常见segmentfault

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并进行处理promise

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 还有一些特色。浏览器

一、异步加载,当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>

二、编译时解析,简单来讲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)
  })
}

三、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详解

相关文章
相关标签/搜索