Javascript模块导入导出

1、什么是模块

一个脚本就是一个模块,一个模块就是一个文件,模块可以相互加载,并可以使用特殊指令exportimport来交换功能,从另一个模块调用一个模块的函数。

  • export标记了外部可以从当前模块访问的变量和函数
  • import允许从其他模块导入功能

2、模块核心功能

  1. 始终默认使用use strict
  2. 模块级作用域,每个模块都有自己的顶级作用域
  3. 模块代码仅在第一次导入时被解析,模块代码只执行一次,导出仅创建一次,然后会在导入之间共享
  4. import.meta对象包括关于当前模块的信息
  5. 在一个模块中,“this”是undefined
  6. 模块脚本是延迟的
    (1)下载外部模块脚本不会阻塞HTML处理,会和其他资源并行加载
    (2)模块脚本回等到HTML文档完全准备就绪才会运行
    (3)排在前面的脚本先执行
<script type="module">
  alert(typeof button); // object:脚本可以“看见”下面的 button
  // 因为模块是被延迟的(deferred,所以模块脚本会在整个页面加载完成后才运行
</script>

相较于下面这个常规脚本:

<script>
  alert(typeof button); // button 为 undefined,脚本看不到下面的元素
  // 常规脚本会立即运行,常规脚本的运行是在在处理页面的其余部分之前进行的
</script>

<button id="button">Button</button>

  1. Async适用于内联脚本
<!--
			外联的写法:
			注意:外联的脚本块中不要再写代码,
			尽量不要用空标签简写方法。
		-->
		<!--<script language="JavaScript" src="../js/js1.js"></script>-->

<!--内联的写法1(不推荐)-->
		<!--<script>
			alert("Hello Wolrd!");//显示消息框
			//alert有类似于断点的作用,
			//所以之前调试代码是用的它。
		    </script>-->
		    
		<!--内联的写法2-->
		<!--<script launguage="JavaScript">
		    	console.log("Hello Wolrd!");
		 	</script>-->
		    
		<!--内联的写法3-->
		<!--<script type="application/javascript"></script>-->
		
		<!--内联的写法4-->
		<script type="text/javascript"></script>

<!-- 所有依赖都获取完成(analytics.js)然后脚本开始运行 -->
<!-- 不会等待 HTML 文档或者其他 <script> 标签 -->
<script async type="module">
  import {counter} from './analytics.js';

  counter.count();
</script>

  1. 不允许裸模块,import必须给出相对或者绝对路径
  2. 兼容性 nomodule`

3、导出和导入

3.1、在声明前导出

// 导出数组
export let months = ['Jan', 'Feb', 'Mar','Apr', 'Aug', 'Sep', 'Oct', 'Nov', 'Dec'];

// 导出 const 声明的变量
export const MODULES_BECAME_STANDARD_YEAR = 2015;

// 导出类
export class User {
  constructor(name) {
    this.name = name;
  }
}
  • 导出 class/function 后没有分号
    注意,在类或者函数前的 export 不会让它们变成函数表达式。尽管被导出了,但它仍然是一个函数声明。
export function sayHi(user) {
  alert(`Hello, ${user}!`);
}  // 在这里没有分号 ;

3.2、导出与声明分开

先声明函数,然后再导出它们

/ 📁 say.js
function sayHi(user) {
  alert(`Hello, ${user}!`);
}

function sayBye(user) {
  alert(`Bye, ${user}!`);
}

export {sayHi, sayBye}; // 导出变量列表

3.3、Import *

把要导入的东西列在花括号 import {…} 中

// 📁 main.js
import {sayHi, sayBye} from './say.js';

sayHi('John'); // Hello, John!
sayBye('John'); // Bye, John!

果有很多要导入的内容,我们可以使用 import * as 将所有内容导入为一个对象

// 📁 main.js
import * as say from './say.js';

say.sayHi('John');
say.sayBye('John');

为什么通常要明确列出我们需要导入的内容

  1. 现代的构建工具(webpack 和其他工具)将模块打包到一起并对其进行优化,以加快加载速度并删除未使用的代码,我们向我们的项目里添加一个第三方库 say.js,它具有许多函数而我们只在我们的项目里使用了 say.js 中的一个函数,优化器(optimizer)就会检测到它,并从打包好的代码中删除那些未被使用的函数,从而使构建更小。这就是所谓的“摇树(tree-shaking)”
  2. 明确列出要导入的内容会使得名称较短:sayHi() 而不是 say.sayHi()
  3. 导入的显式列表可以更好地概述代码结构:使用的内容和位置。它使得代码支持重构,并且重构起来更容易

3.4、Import “as”

用 as 让导入具有不同的名字

// 📁 main.js
import {sayHi as hi, sayBye as bye} from './say.js';

hi('John'); // Hello, John!
bye('John'); // Bye, John!

3.5、Export “as”

// 📁 say.js
...
export {sayHi as hi, sayBye as bye};
// 📁 main.js
import * as say from './say.js';

say.hi('John'); // Hello, John!
say.bye('John'); // Bye, John!

3.6、Export default

在实际中,主要有两种模块。

  • 包含库或函数包的模块
  • 声明单个实体的模块
    模块提供了一个特殊的默认导出 export default 语法,以使“一个模块只做一件事”的方式看起来更好
// 📁 user.js
export default class User { // 只需要添加 "default" 即可
  constructor(name) {
    this.name = name;
  }
}

然后将其导入而不需要花括号

// 📁 main.js
import User from './user.js'; // 不需要花括号 {User},只需要写成 User 即可

new User('John');

import 命名的导出时需要花括号,而 import 默认的导出时不需要花括号。

命名的导出默认的导出
export class User {…}export default class User {…}
import {User} from …import User from …

命名的导出会强制我们使用正确的名称进行导入:

import {User} from './user.js';
// 导入 {MyUser} 不起作用,导入名字必须为 {User}

对于默认的导出,我们总是在导入时选择名称


```javascript
import User from './user.js'; // 有效
import MyUser from './user.js'; // 也有效
// 使用任何名称导入都没有问题

3.7、重新导出

语法 export ... from ...

export {sayHi} from './say.js'; // 重新导出 sayHi

export {default as User} from './user.js'; // 重新导出 default
  • export {x [as y], …} from “module”
  • export * from “module”(不会重新导出默认的导出)。
  • export {default [as y]} from “module”(重新导出默认的导出)。

在 {…} 中的 import/export 语句无效。

if (something) {
  import {sayHi} from "./say.js"; // Error: import must be at top level
}

4、动态导入

上面的导出和导入语句称为“静态”导入。语法非常简单且严格。

  • 模块路径必须是原始类型字符串,不能是函数调用
import ... from getModuleName(); // Error, only from "string" is allowed
  • 无法根据条件或者在运行时导入
if(...) {
  import ...; // Error, not allowed!
}

{
  import ...; // Error, we can't put import in any block
}

4.1、import() 表达式

import(module) 表达式加载模块并返回一个 promise,该 promise resolve 为一个包含其所有导出的模块对象。我们可以在代码中的任意位置调用这个表达式。

let modulePath = prompt("Which module to load?");

import(modulePath)
  .then(obj => <module object>)
  .catch(err => <loading error, e.g. if no such module>)

动态导入在常规脚本中工作时,它们不需要 script type="module".

//say.js
export function hi() {
  alert(`Hello`);
}

export function bye() {
  alert(`Bye`);
}

export default function() {
  alert("Module loaded (export default)!");
}
//main.js
<!doctype html>
<script>
  async function load() {
    let say = await import('./say.js');
    say.hi(); // Hello!
    say.bye(); // Bye!
    say.default(); // Module loaded (export default)!
  }
</script>
<button onclick="load()">Click me</button>

尽管 import() 看起来像一个函数调用,但它只是一种特殊语法,只是恰好使用了括号(类似于 super())。因此,我们不能将 import 复制到一个变量中,或者对其使用 call/apply。因为它不是一个函数。

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值