前端工程化开发(三)—— ESModule
认识 ESModule 模块化开发
首先的话我们需要知道一点的是,我们的前端的在 ES6 之前是没有任何的模块化的规范的
CommonJS也只是社区中通过商讨,从而诞生的一种比较具备权威性的模块化开发模式,所以说我们的 ESModule 才是真真的模块化开发的规范
ESModule 和 CommonJS 的区别
- 第一个不同的是使用的关键字的不同。esmodule 使用的是我们的 import 以及 export 关键字实现的模块化开发
- 编译器加入了静态解析和动态引入的方式
ESModule 实现导入导出的方式
- export 负责将我们的模块进行导出
- import 负责将我们的模块进行导入
- 同时我们的 esmodule 默认情况下使用的是我们的严格模式开展的以一种开发
use strict;
首先我们使用原生的进行开发的时候,不使用任何的脚手架的时候
- 我们使用
script
标签导入一个模块文件的时候需要进行增加属性type="module"
- 来表示我们的文件是以模块的形式导入的
- 同时使用
module
形式实现的导入模块默认是在不同的作用域下的- 这样的话就实现了指定了我们的一个JS 文件就是一个模块了来进行使用了
<script src="./index.js" type="module"></script>
模块内部的导出
export
规则
- 导出的形式 :
export {}
{}
内部存放的是我们的需要进行导出的一些模块内部的东西- 这种导出模式的话不是导出的对象,也不是字面量的增强
- 就是单纯的需要暴露给外面的是什么,就用什么来进行导出即可
导入规则
import
规则
import {} from "JS模块文件路径"
- 但是这个时候会出现我们的协议问题,导致无法正常加载文件的,这个就是跨域 CORS 问题出现的场景一
// index.js
const name = "hello world"
function get_value(param, ...args) {
console.log(param)
}
export {
get_value,
name
}
<!doctype html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport"
content="width=device-width, user-scalable=no, initial-scale=1.0, maximum-scale=1.0, minimum-scale=1.0">
<meta http-equiv="X-UA-Compatible" content="ie=edge">
<title>ESModule</title>
</head>
<body>
<!--下面的一句话就是表示的是我们下面文件是一个模块的类型,这个时候据不会有什么作用域冲突的问题存在了-->
<!--这个时候的模块化开发就不用书写IIFE 来进行我们的自己产生作用域了-->
<script src="./index.js" type="module"></script>
</body>
</html>
export 关键字的使用
导出方式一
export { get_value, name }
导出方式二: 导出的时候可以给我们的标识符取别名
export { name as index_name, get_value as index_get_value }
导出方式三: 在我们的标识符进行定义的时候直接导出
export function get_value {} export const name = "hello world"
import 导入方式
导入方式一
import {name, age, get_value} from "./index.js"
导入方式二:在我们的外部变量进行导入的时候取别名防止变量名冲突
import {name as index_name, age, get_value} from "./index.js"
导入方式三: 导入的时候就给我们的模块取别名
import * as index from "./index.js" console.log(index.name) index.get_value(index.name)
export 和 import 的结合使用
解析流程是我们的先从静态资源服务器中下载
index.html
文件然后对我们的 HTML 文档进行一步一步的解析,遇到了
link
元素,就去下载 CSS 文件或者其他然后继续的加载 HTML 文档,遇到了
script
标签后,直接去下载 JS 文件
- 但是在我们的 JS 文件使用了模块化的开发,实现导入了其他的 JS 文件,又进行下载
- 这里的话我们浏览器加载 JS 文件含有三种模式,一种是默认的模式,defer 模式以及 async 模式
在我们的实际的开发中,我们的每一个板块的话,我们是会进行编写一个主文件的
- 该文件不用书写任何的业务逻辑,该部分只是实现的是将所有的工具进行集成
- 然后统一暴露给外部进行使用的文件
- 该文件的一半取名的话是:
index.js
- 在这个目录中实现我们的将所有导入的文件全部导出即可
- 或者说使用我们的
export 和 import 的结合使用
export {} from "文件名路径"
实现混合使用一
// index.js import {util01} from "./util01.js" import {util02} from "./util02.js" import {util03} from "./util03.js" import {util04} from "./util04.js" // 然后进行导出即可、 export { util01, util02, util03, util04 }
实现混合使用二
// index.js export {util01} from "./util01.js" export {util02} from "./util02.js" export {util03} from "./util03.js" export {util04} from "./util04.js"
实现混合使用三
// index.js export * from "./util01.js" export * from "./util02.js" export * from "./util03.js" export * from "./util04.js"
export default 导出用法
这个就是我们的默认导出
第一种写法
// utils/formatCount.js function formatCount(timeStr) { // JS 函数代码体 } export defdault formatCount
import 别名 from "./utils/formatCount.js"
第二种写法
// utils/formatCount.js export default function(timeStr) { // JS 函数代码体 }
import 别名 from "./utils/formatCount.js"
注意我们的一个模块只能有一个默认导出,不能有多个
import 函数
- import 函数是一个异步的操作,利用在我们的逻辑处理中,返回的是一个 Promise 对象
import("./utils").then(res => { console.log(res) }).catch(err => { console.log(err) })
import.meta
- 该属性主要是用来实现保存的是我们当前模块的元数据,包含了我们当前模块 URL
console.log(import.meta)
ESModule 的解析流程
ESModule 的解析流程主要划分为了三步
阶段一: 构建(Construction),根据地址查询每一个 JS 文件,并下载,将其解析为模块记录(Module Record)
- 查找:实现的就是从哪里进行下载包含模块的文件,这一步也是模块解析
- 下载:获取 JS 文件
- 解析:将文件解析为模块记录
阶段二:实例化(Instantiation),对模块记录进行初始化,并且分配内存空间,解析模块的导入导出语句,把模块指向对应的内存地址
实例化步骤就是写入内存,生成模块环境记录,但是这一步并没有对任何的变量进行赋值操作
所以说在这一个过程中就可以解释为什么我们的 esmodule 模块化开发可以避免作用域问题了,就是因为其实现导出是
- 该模块中指定导出的被添加到环境记录中的变量
阶段三:运行(求值)(Evaluation),运行JS 代码,计算值,并且将其将值填充到内存地址中
- 内存区中填充绑定数据具体的值
总图解析
构建过程图
实例化以及求值阶段图
总结
- hacks 网站使用
- 该部分主要讲解了关于我们的前端模块化开发中的 ESMdule
- 同时我们也浅谈了一下关于导入导出的不同的表现形式以及 ESModule 的详细解析流程