欢迎您访问 最编程 本站为您分享编程语言代码,编程技术文章!
您现在的位置是: 首页

用于前端模块化的 ES 模块

最编程 2024-04-28 18:14:38
...

前言

ES modules 是原生 JavaScript 提供的模块功能,逐渐被更多的浏览器开始支持。

入门篇

先了解下基础用法。

html 文件中使用 ES modules 语法执行 js

在 script 标签中 设置 type = module,浏览器就会以 ES modules 的标准去执行 script 标签中的 JavaScript 代码。默认情况下,代码是以严格模式执行的。

<script type="module">
    console.log('this is es module')
</script>

私有作用域

每一个设置 type=module 的标签内都会形成一个私有作用域。作用域之间的变量不能直接共享。

<script type="module">
    var a = 1
    console.log(a)
</script>

<script type="module">
    console.log(a)
</script>

第 1 组 script 中的代码正常输出变量 a 的值,第 2 段会报以下错误:a is not defined。

外链 JS 文件加载

ES modules 会以 CORS 方式加载外部 js 文件。提供 js 文件的服务器需要设置 CORS 后,ES modules 的 script 标签才能正常加载文件。

<script type="module" src="https://unpkg.com/jquery@3.6.0/dist/jquery.js"></script>
<script type="module" src="https://abc.com/jquery@3.6.0/dist/jquery.js"></script>

第 1 组 script 标签加载成功,第 2 组加载失败。

延迟执行,作用与 defer 一致。

未设置 type = module 前,会先执行完 01_demo.js 的代码。p 标签的渲染会被阻塞。

设置 type = module 后,p 标签的渲染不会被阻塞。

<script src="./01_demo.js" type="module"></script>
<p>测试</p>

导出

导出方式
// module.js - 单独导出
export var name = "foo module";
export class A {}
export function bar() {}

// 统一导出
var name = "foo module";
class A {}
function bar() {}
export { name, A, bar }

// app.js - 导入
import { name } from "./module.js";
console.log(name);
导出重命名
const age = 25;
export { age as Age };

默认模块导出,导入时也需要重命名

const age = 25;
export { age as default };
export default age;

import { default as Age } from './module.js'
import Age from './module.js'
console.log(Age)

注意事项:

  • 导出时,export 后面不是字面量对象。import 后面也不是对象解构。
  • export default { name, age } 是等价于导出一个对象,export { a, b, c } 则是固定用法。
  • 进行导入操作时,导入的是同一份引用,是引用关系,并非值,这一点与 common.js 不一样。在导入之后,不能对导入变量进行修改,它是一个只读的关系。

导入

  1. 导入语句中,需要指定完整路径,不能忽略文件后缀名。而 common js 是支持忽略文件后缀名。
  2. 路径必须以 / 开头。导入路径支持使用 cdn url 外链 js 文件的形式。
  3. 以下导入方式等价于执行导入文件中的 js 代码。
import {} from './module.js'
import './module.js'
  1. 将模块中所有内容一次性导入,通过对象.变量方式访问。
import * as from './module.js'
  1. 动态导入,import() 等价于函数调用。非函数调用形式只能至于顶层。
import('./module.js').then(module => {
   console.log(module)
})
  1. 默认成员和具名成员导入。
import { name, age, default as title } from './module.js'
import title, { name, age } froom './module.js'

导出导入成员

一种代码组织方式,将散落模块组织成新模块。

// components/button.js
export const Button = "Button component";

// components/radio.js
export const Radio = "Radio component";

// components/index.js
import { Button } from "./button.js";
import { Radio } from "./radio.js";

export { ButtonRadio };

// app.js
import { ButtonRadio } from "./component/index.js";

console.log(Button);
console.log(Radio);

Polyfill 篇

ES modules 是 ES6 推出的新语法特性,在低版本浏览器上需要通过 Polyfill 方式去解决兼容性问题。

github.com/ModuleLoade…

script 添加 nomodule 设置,避免在正常浏览器出现代码执行两次的情况。nomodule 表示当前浏览器不支持 modules 的情况下加载使用。

<script nomodule src="https://unpkg.com/promise-polyfill@8.2.0/dist/polyfill.min.js"></script>
<script nomodule src="https://unpkg.com/browser-es-module-loader@0.4.1/dist/babel-browser-build.js"></script>
<script nomodule src="https://unpkg.com/browser-es-module-loader@0.4.1/dist/browser-es-module-loader.js"></script>
<script type="module">
    import { foo } from './module.js'
    console.log(foo)
</script>

Node 中应用篇

背景

Node 中对 ES modules 语法特性的支持还在实验阶段。node 版本大于 8.5,脚本执行命令添加 --experimental-modules 即可运行 ES modules 代码。

Node 对 ES modules 特性支持的源码,可点击链接了解:

github.com/nodejs/node…

应用

node --experimental-modules index.mjs。对于 .mjs 后缀名的文件,Node 会以 modules 特性去运行代码。

// 1. 自定义模块
import { foo, bar } from "./module.mjs";
console.log(foo);
console.log(bar);

// 2. 系统内置模块
import fs from "fs";
fs.writeFileSync("foo.txt""Foo txt content!");

// 3. 第三方模块,不支持按单个成员方式导入,一般都是导出默认成员的方式。
import _ from "lodash";
console.log(_.camelCase("ES module"));

// 4. 系统内置模块支持单个导入
import { writeFileSync } from "fs";
writeFileSync("bar.txt""Bar txt Content!");

与 CommonJS 交互

  1. ES modules 中可以导入 CommonJS 模块。
  2. CommonJS 中不能导入 ES modules 模块。
  3. CommonJS 始终只会导出一个默认成员。
  4. import 不是解构对象。

与 CommonJS 差异

新版本进一步支持

Node 项目的 package.json 中添加配置项 type: 'module',就可以使得项目中的 js 文件支持 es modules 方式进行模块的导入导出。配置完成后,项目中使用 CommonJS 导入方式的文件都需要将后缀名修改为 .mjs。

babel 兼容方案

babel 用于将新特性语法转换成低版本浏览器兼容的语法。

低版本 node 使用 babel 插件运行 es modules 代码

步骤如下:

  1. 安装 babel 插件

yarn add @babel/node @babel/core @babel/preset-env --dev

  1. 运行 index.js 文件

yarn babel-node index.js

  1. babel 转换机制

每一个 ES 新特性都有相应的 babel 插件进行转换。

  1. preset-env 是 js 新特性的集合。

yarn babel-node ./09-babel/index.js --presets=@babel/preset-env

  1. 第 4 步也可以通过 babelrc 设置。
{
    "presets": ["@babel/preset-env"]
}

运行命令可以直接去除 presets: yarn babel-node ./09-babel/index.js

  1. 使用单个插件(非插件集合)

yarn add @babel/plugin-transform-modules-commonjs --dev

babelrc 设置

{
    "plugins": [
        "@babel/plugin-transform-modules-commonjs"
    ]
}

yarn babel-node ./09-babel/index.js

总结

es modules 逐渐被更多浏览器所支持,意味着在浏览器端将会有统一的模块化解决方案。在模块化思想指导下,将代码分成若干模块进行组织,程序将会更具扩展性和条理性。