2024-05-18 前端模块化开发——ESModule模块化

目录

1、认识 ES Module

  • JavaScript没有模块化一直是它的痛点,所以才会产生我们前面学习的社区规范:CommonJS、AMD、CMD等,所以在ECMA推出了自己的模块化系统时,大家也是兴奋异常。
  • ES Module和CommonJS的模块化有一些不同之处:
  • 一方面它使用了import和export关键字;
  • 另一方面它采用编译期的静态分析,并且也加入了动态引用的方式;
  • ES Module模块采用export和import关键字来实现模块化:
  • export负责将模块内的内容导出;
  • import负责从其他模块导入内容;
  • 了解:采用ES Module将自动采用严格模式:use strict

2、ES Module基本使用

注意,export后面跟的{}不是一个对象,只是一种特定的语法。

复制代码
// foo.js
const name = 'lisi'
const age = 18
function sayHello() {
    console.log("sayHello");
}

// export { 标识符1, 标识符2, 标识符3 }
export {
    name,
    age,
    sayHello
}

// main.js
// 注意事项一:在浏览器中直接使用esmodule时,必须在文件后面加上后缀名.js
import { name, age, sayHello } from './foo.js'

console.log(name);
console.log(age);
sayHello()


<!--index.html-->
<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>Document</title>
</head>
<body>
    <!--注意事项二:在我们打开对应的html时,如果html中有使用模块化的代码,那么必须开启一个服务来打开-->
    <script src="./foo.js" typt="module"></script>
    <script src="./main.js" type="module"></script>
</body>
</html>

说明:

  • 如果直接在浏览器中运行代码,会报错:
  • 这个在MDN上面有给出解释:
  • 其他模块与标准脚本的不同
  • 你需要注意本地测试------如果你通过本地加载 HTML 文件(比如一个 file:// 路径的文件),你将会遇到 CORS 错误,因为 JavaScript 模块安全性需要。
  • 你需要通过一个服务器来测试。
  • 这里使用的VSCode插件:Live Server

3、export关键字

3.1、导出方式一------直接导出

复制代码
export {
    name,
    age,
    sayHello
}

3.2、导出方式二------通过as起别名

复制代码
export {
	// 导出时给标识符起一个别名,不过实际开发中不怎么用这个方式
    name as fname,
    age,
    sayHello
}

3.3、导出方式三------定义的时候就直接导出

复制代码
export const name = 'lisi'
export const age = 18
export function sayHello() {
    console.log("sayHello");
}

4、import关键字

4.1、导入方式一------直接导入

复制代码
import { name, age, sayHello } from './foo.js'

4.2、导入方式二------通过as起别名

一般在导入的时候起别名,而不是在导出的时候

复制代码
import { name as fname, age, sayHello } from './foo.js'

4.3、导入方式三------可以给整个模块起别名

复制代码
import * as foo from './foo.js'

console.log(foo.name);
console.log(foo.age);
foo.sayHello()

5、export和import结合使用

这是一种开发思想。

  • 补充:export和import可以结合使用
  • 为什么要这样做呢?
  • 在开发和封装一个功能库时,通常我们希望将暴露的所有接口放到一个文件中;
  • 这样方便指定统一的接口规范,也方便阅读;
  • 这个时候,我们就可以使用export 和import结合使用
  • 示例代码:

工具类库:

复制代码
// utils/format.js
export function formatCount() {
    console.log('formatCount');
}

export function formatDate() {
    console.log('formatDate');
}

// utils/parse.js
export function parseLyric() {
    console.log('parseLyric');
}

工具类统一导出出口(export和import结合使用):

复制代码
// utils/index.js
// 默认方式
// import {formatCount, formatDate} from './format.js'
// import {parseLyric} from 'parse.js'

// export {
//     formatCount,
//     formatDate,
//     parseLyric
// }

// 优化一:推荐使用这种,便于阅读
// export {formatCount, formatDate} from './format.js'
// export {parseLyric} from './parse.js'

// 优化二:
export * from './format.js'
export * from './parse.js'

使用工具类中的方法:

复制代码
// main.js
import * as utils from './utils/index.js'

utils.formatCount()
utils.parseLyric()


<!--index.html-->
<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>Document</title>
</head>
<body>
    <script src="./main.js"  type="module"></script>
</body>
</html>

6、default默认导出

  • 前面我们学习的导出都是有名字的导出(named exports);
  • 在导出export时指定了名字;
  • 在导入import时需要知道具体的名字;
  • 还有一种导出叫做默认导出(default export);
  • 默认导出export时可以不需要指定名字;
  • 在导入时不需要使用{},并且可以自己来知道名字;
  • 它也方便我们和现有的CommonJS等规范互相操作;
  • 注意:在一个模块中,只能有一个默认导出(export default);

6.1、导出方式一------默认导出

  • 导出一个常量

    const name = 'foo'

    const age = 18

    export default name

  • 导出一个对象

    const name = 'foo'

    const age = 18

    export default {

    fname:name,

    age,

    }

可以看出export default后面的{}表示导出为一个对象。

6.2、导出方式二------定义时直接导出

复制代码
export default name = 'foo1'

7、import函数

  • 通过import加载一个模块,是不可以把它放到逻辑代码中的,比如:

  • 为什么会出现这个情况呢?

  • 这是因为ES Module在被JS引擎解析时,就必须知道它的依赖关系;
  • 由于这个时候js代码没有任何的运行,所以无法在进行类似于if判断的逻辑代码中,根据代码的执行情况来获取模块是否加载;
  • 甚至拼接路径的写法也是错误的:因为我们必须到运行时才能确定path的值;
  • 但是某些情况下,我们确确实实希望动态的来加载某一个模块:
  • 如果根据不同的条件,动态来选择加载模块的路径;
  • 这个时候我们需要使用import()函数来动态加载;
    √ import函数返回一个Promise,可以通过then获取结果;
复制代码
let flag = true
if(flag) {
    import('./foo.js').then(res => {
        console.log(res.name); // 后打印
    })
    console.log('----'); // 先打印
}

8、import meta(了解)

  • import.meta是一个给 JavaScript 模块暴露特定上下文的元数据属性的对象。
  • 它包含了这个模块的信息,比如说这个模块的 URL。
  • 在ES11(ES2020)中新增的特性;
复制代码
// {url: 'http://127.0.0.1:5500/07_ESModule/main.js', resolve: ?}
console.log(import.meta,'meta'); 

9、ES Module的解析流程

  • ES Module是如何被浏览器解析并且让模块之间可以互相引用的呢?
  • ES Module的解析过程可以划分为三个阶段:
  • 阶段一:构建(Construction),根据地址查找js文件,并且下载,将其解析成模块记录(Module Record);
  • 阶段二:实例化(Instantiation),对模块记录进行实例化,并且分配内存空间,解析模块的导入和导出语句,把模块指向对应的内存地址;(备注:阶段二生成模块环境记录(类似于函数解析时生成的环境记录)。)
  • 阶段三:运行(Evaluation),运行代码,计算值并且将值填充到内存地址中;

9.1、阶段一:构建阶段

9.2、阶段二和三------实例化阶段-求值阶段

相关推荐
在云端易逍遥1 分钟前
前端必学的 CSS Grid 布局体系
前端·css
EMT1 分钟前
在 Vue 项目中使用 URL Query 保存和恢复搜索条件
javascript·vue.js
ccnocare2 分钟前
选择文件夹路径
前端
艾小码3 分钟前
还在被超长列表卡到崩溃?3招搞定虚拟滚动,性能直接起飞!
前端·javascript·react.js
闰五月4 分钟前
JavaScript作用域与作用域链详解
前端·面试
泉城老铁7 分钟前
idea 优化卡顿
前端·后端·敏捷开发
前端康师傅7 分钟前
JavaScript 作用域常见问题及解决方案
前端·javascript
司宸9 分钟前
Prompt结构化输出:从入门到精通的系统指南
前端
我是日安9 分钟前
从零到一打造 Vue3 响应式系统 Day 9 - Effect:调度器实现与应用
前端·vue.js