JS模块化,ESM模块规范的 导入、导出、引用、调用详解

写在前面

在之前的 JS模块化的各种规范 之 CJS、AMD、CMD、UMD、ESM 文章中,介绍了关于JS模块化的各种规范的区别和简单使用。 由于ESM模块规范也是 ECMAScript2015(ES6)中的规范标准,在日常的Web项目开发、小程序开发、APP开发等都是很常用的,所以本文重点将 ESM模块规范中的 导出、导入、导出、引用、调用等,结合日常开发需求做进一步的介绍。

实例代码

这里分别以不同的实例代码,逐步介绍ESM模块规范的导出、 导入、引用、调用等使用方法。

1、模块导出 - export

模块导出 就是将js文件中的 变量、常量、函数、对象、类 等对象 向外导出(暴露)以方便外部的js文件所引用和调用。 在JS的ESM模块规范中,主要通过 export 关键字 来 向外导出想要导出(暴露)的对象。

导出之 - 独立导出

独立导出方式,是直接在要向外导出(暴露)变量、常量、函数、对象、类 等对象的对面加上 export 关键字即可!

js 复制代码
export let count = 666;

export const PI = Math.PI;

const random = Math.random();
export { random as default };

export const sum = (n1, n2) => {
    return n1 + n2;
};

export function isOdd(n) {
    return n % 2 === 1 || n % 2 === -1;
};

export class Person {
    constructor(name) {
        this.name = name;
    }
    sayHello() {
        console.log(`Hello! 我是${this.name}`);
    }
};

导出之 - 集中多个导出

集中多个导出方式,一般是在代码的最底部,将要向外导出(暴露)变量、常量、函数、对象、类等对象,通过 export export default 以对象的形式导出!

js 复制代码
let count = 666;

const PI = Math.PI;

const sum = (n1, n2) => {
    return n1 + n2;
};

function isOdd(n) {
    return n % 2 === 1 || n % 2 === -1;
};

class Person {
    constructor(name) {
        this.name = name;
    }
    sayHello() {
        console.log(`Hello! 我是${this.name}`);
    }
};

// 集中多个导出
export {
    count, // 注:如果对象中的 键key 和 值value 的命名完全相同 可省去 :random 后面这部分!!
    PI,
    sum,
    isOdd,
    Person
};

导出之 - 默认导出

默认导出方式,是使用 export default 关键字 直接导出,该导出方式只能导出单个内容。

js 复制代码
// 可直接向外导出(暴露)变量、常量、函数、对象、类等对象
export default function sayHello(name) {
    console.log(`Hello! 我是${name}`);
};
  • 注意:export default 默认导出 在一个js模块文件中,不能同时存在多个默认导出(也就是说,在一个js模块文件中 export default 能只出现一次!!!)

导出之 - 集中默认导出

集中默认导出方式,是使用 export default 关键字 以对象形式导出,该导出方式可以同时导出多个内容。

js 复制代码
const PI = Math.PI

function isOdd(n) {
    return n % 2 === 1 || n % 2 === -1;
};

class Person {
    constructor(name) {
        this.name = name;
    }
    sayHello() {
        console.log(`Hello! 我是${this.name}`);
    }
};

export default {
    count: 666,
    PI,
    sum: (n1, n2) => {
        return n1 + n2;
    },
    isOdd,
    Person,
    sayHello: function (name = '') {
        console.log(`Hello! 我是${name}`);
    },
};

导出之 - 混合导出

混合导出方式,就是 在一个JS模块文件中,既有独立导出、集中导出 又有 默认导出、集中默认导出等形式一起组合导出。

📢下面混合导出的代码内容 以 demo.js 文件命名,为接下来的模块 导入、引用、调用实例做演示用!!

js 复制代码
/**
 * demo.js
 */

let count = 666;

// 独立导出 常量
export const PI = Math.PI;

const sum = (n1, n2) => {
    return n1 + n2;
};

// 独立导出 函数
export function isOdd(n) {
    return n % 2 === 1 || n % 2 === -1;
};

// 独立导出 类
export class Person {
    constructor(name = '') {
        this.name = name;
    }
    sayHello() {
        console.log(`Hello! 我是${this.name}`);
    }
};

// 默认导出
export default {
    // 默认导出 变量
    count: count,

    // 默认导出 函数
    sum,

    // 默认导出 类
    Person,

    // 默认导出 字符变量 (可直接创建 并 导出[ 变量、常量、函数、对象、类] 等对象)
    pi: '3.141592653589793',

    // 默认导出 函数 方式1
    is: function (val, type) {
        return toString.call(val) === `[object ${type}]`;
    },

    // 默认导出 函数 方式2
    isArray: (arr = []) => {
        return Object.prototype.toString.call(arr) === '[object Array]' || Array.isArray(arr);
    },
    // 默认导出 函数 方式3
    isObject(val) {
        // 注:在这里还可以通过 this关键字 来调用当前默认导出模块中的 其他导出属性内容
        // 如 this.pi; this.sum(1, 2);
        return val !== null && this.is(val, 'Object') || (typeof val === 'object' && val !== null);
    },
    // 默认导出 类
    AudioTrack: class {
        constructor(trackId = '', trackName = '', trackUrl = 'https://') {
            this.trackId = trackId;
            this.trackName = trackName;
            this.trackUrl = trackUrl;
        };
        start() {
            console.log(`开始播放${this.trackName}`);
        };
        stop() {
            console.log(`停止播放${this.trackName}`);
        };
    }
};

2、模块导入 - import

在JS的ESM模块规范中, 当通过 export 关键字 来 向外导出想要导出(暴露)对象以后, 就可以通 import 关键字 来 导入 对应的(注:要一一对应) 导出内容。

导入之 - 全部导入

全部导入方式,使用 ⁎星号 将模块文件中所有的import 独立导出、export default 默认导出 等导出的内容全部导入进来

js 复制代码
import * as all from "./demo.js";
// 注:通过export default 关键 默认导出(暴露)的内容在default属性中,和export独立导出的在同一级上!!
console.log('demo.js模块 所有导出的内容:', all);

导入之 - 默认导入

默认导入方式,只会将导出文件中通过export default 关键字向外 导出(暴露)的内容导入进来。

js 复制代码
import def from "./demo.js";
// 注:该导入方式只会 export default 关键字向外 导出(暴露)的内容导入进来!
console.log('demo.js模块 默认导出的内容:', def );

导入之 - 指定导入

指定导入方式,是将导出文件中通过export 关键字向外 导出(暴露)的内容 根据指定变量名(不分顺序)的导入进来。

js 复制代码
// 指定将PI, isOdd, Person 导入进来
import { PI, isOdd, Person } from "./demo.js";

导入之 - 混合导入

混合导入方式,就是将导出文件中通过export default 关键字 和 export 关键字 向外 导出(暴露)的内容 根据指定变量名(不分顺序)的导入进来。

js 复制代码
// 注:def 接收的就是在导出文件中通过export default 关键字向外导出内容,而在大括号{}中接收到在导出文件中通过export关键字向外导出内容。
// 注:def 这个变量名字是可能自定义的。
import def, { PI, isOdd, Person } from "./demo.js";

导入之 - 改名导入

改名导入,是在导入时的 变量名 和 当前文件中的 变量名 有冲突(相同)时,通过 as 关键字 将导入的变量 新更改一下名字,再导入进来。

js 复制代码
// 将原大写的PI 更改为 小写的pi
import def, { PI as pi, isOdd, Person } from "./demo.js";

// 当前文件中的 变量名 有冲突
const PI = '3.141592653589793 ';
console.log(PI, pi);

3、模块导入即导出

模块导入即导出方式,一般主要是用于,为了尽可能的减少 在各个JS模块文件之间出到多次导入问题的。 例如: 在A.js模块文件中,同时需要用到B.js模块文件 和 C.js模块文件。 而B.js模块文件中也要用到C.js模块文件。 此时: 我们可以只需在A.js模块文件中 导入 B.js模块文件,然后B.js模块文件中 导入 C.js模块文件即可。

导入即导出之 - 所有导入导出

所有导入导出,是将模块所有的内容导入 又 全部导出。

  • 这里扮演的B.js模块文件 向A.js模块文件 导出C.js模块文件中的所有导出 。
js 复制代码
export * from './demo.js';

导入即导出之 - 默认导入导出

默认导入导出,是将 demo.js模块文件中的 export default 默认模块向外导出。

js 复制代码
export  { default } from './demo.js';

导入即导出之 - 默认改名导入导出

将 demo.js模块文件中的 export default 默认模块改名在MyDemo下向外导出。

js 复制代码
export  { default as MyDemo } from './demo.js';

导入即导出之 - 指定多个导入导出

指定多个导入导出,是将导出文件中通过export 关键字向外 导出(暴露)的内容 根据指定变量名(不分顺序)的导入和导出

  • 这里扮演的B.js模块文件 向A.js模块文件导出PI, random, isOdd,【在A.js模块文件中,同时需要用到B.js模块文件 和 C.js模块文件】。
js 复制代码
export  { PI, random, isOdd } from './demo.js';

导入即导出之 - 混合导入即导出

混合导入即导出,就是将 默认模块 和 指定多个变量一起导出。

js 复制代码
// 注:default 是关键字,它装着在导出模块文件中通过export default默认导出的内容。
export { default PI, isOdd, Person } from './demo.js';

// 将 demo.js模块文件中的 export  default 默认模块改名在myDef后向外导出。
export { default as myDef, PI, isOdd, Person } from './demo.js';

4、模块引用与调用

在模块导入以后 引用与调用,就是在模块导入以后 就和 我们平时在一个js文件中写代码一样去引用和调用 变量、常量、函数、对象、类 等对象一样,直接使用即可。

引用与调用之 - 本地资源加载

在vite + vue3 项目开发过程,有时需要在 js 代码中去加载本地图片,由于vite默认不支持 require()方法,所以我们可以使用 import来加载本地资源,而且 在项目构建(打包)后也能正常的显示资源。

  • 注:在下面代码中, 资源前面的../assets/ 是根据当前这个getLocalFile()函数所在的js文件的相对路径,在vue项目中,一般这个文件是放在src目录下面的utils文件夹里面,而资产文件则一般是放在src目录下面的assets文件夹里面,utils 和 assets是同一级目录的(都在src目录下);
js 复制代码
function getLocalFile(url) {
	return new URL(`../assets/${url}`, import.meta.url).href;
};
getLocalFile('img/logo.jpg');

// ts写法的简写
const getLocalFile = (url: string): string => new URL(`../assets/${url}`, import.meta.url).href;
getLocalFile('img/logo.jpg');

引用与调用之 - 异步(按需)加载模块

模块异步加载,在一些业务场景中是非常有用的,例如:聊天室,在用户进入聊天室之前需要先进行登录后才能进入,所以在用户没登录成功以前,WebSocket是不应该连接的,需要等到登录成功以后才连接。

js 复制代码
function load() {
    import('./demo.js').then((esm) => {
        console.log('esm就是./demo.js模块文件中所有导出的内容:', esm);
        console.log(esm.PI);

        const def = esm.default;
        console.log('esm.default就是./demo.js模块文件中默认导出的内容:', def);
        def.sum(100, 200);
    });

    import('./demo.js').then(({ PI, isOdd, Person }) => {
        console.log('也可以直接解构./demo.js模块文件分别独立导出的内容:', PI, isOdd, Person);
        isOdd(666);
        const p = new Person('沐枫');
        p.sayHello();
    });
};

// 在用户点击以后再去加载 想要引用和调用的模块。
btn.onclick = function () {
    load();
};


// 注:如果是在html文件中直接使用时,需要在script标签中加上type="module"属性,并且要在http服务器环境中打开才能正常使用哦!!
<script type="module">
	import('./demo.js').then((esm) => {
 		...
 	});
</script>
相关推荐
恋猫de小郭7 小时前
Flutter Zero 是什么?它的出现有什么意义?为什么你需要了解下?
android·前端·flutter
崔庆才丨静觅13 小时前
hCaptcha 验证码图像识别 API 对接教程
前端
passerby606114 小时前
完成前端时间处理的另一块版图
前端·github·web components
掘了14 小时前
「2025 年终总结」在所有失去的人中,我最怀念我自己
前端·后端·年终总结
崔庆才丨静觅14 小时前
实用免费的 Short URL 短链接 API 对接说明
前端
崔庆才丨静觅15 小时前
5分钟快速搭建 AI 平台并用它赚钱!
前端
崔庆才丨静觅15 小时前
比官方便宜一半以上!Midjourney API 申请及使用
前端
Moment15 小时前
富文本编辑器在 AI 时代为什么这么受欢迎
前端·javascript·后端
崔庆才丨静觅15 小时前
刷屏全网的“nano-banana”API接入指南!0.1元/张量产高清创意图,开发者必藏
前端
剪刀石头布啊16 小时前
jwt介绍
前端