ES6中import和export使用

260 阅读5分钟

本文已参与「新人创作礼」活动,一起开启掘金创作之路。

ES6 中 export 和 import 的使用,以及与 node 中 module.export 和 require 的横向对比。

module、export、require、import 介绍

module、module.exports、exports 采用的是 CommonJS 模块规范。export 和 export default 采用的是 ES6 模块规范。

  • module.exports 输出的是值的拷贝;export 输出的是值的引用;
  • module.exports 在运行时加载;export 是在编译时输出;

module

每个文件就是一个模块。文件内定义的变量、函数等等都是在自己的作用域内,都是自身所私有的,对其它文件不可见。每个文件内部都有一个 module 对象,它包含以下属性:

  • id: 模块的识别符,通常是带有绝对路径的模块文件名
  • filename:模块的文件名,带有绝对路径
  • loaded:返回一个布尔值,表示模块是否已经完成加载
  • parent:返回一个对象,表示调用该模块的模块
  • children:返回一个数组,表示该模块要用到的其他模块
  • exports:表示模块对外输出的值

module.exports

  • 在 module 中有一个属性 exports,即:module.exports。它是该模块对外的输出值,是一个对象。其它模块在加载该模块时,实际上加载到的数据都是由它提供的。
  • module.exports 输出的可以是一个对象,也可以是一个函数。在引用该模块的文件内,如果接受到的是对象,可以直接访问其中的属性,如果接受到的是一个函数,也可以直接执行。

exports

  • exports 是一个特殊的存在,它是对 module.exports 的指向,可以通过向 exports 对象中添加变量、方法等,但是不能直接将 exports 指向一个值,这样会切断 exports 和 module.exports 之间的联系。
  • export 和 module.exports 的使用有一点需要注意,如果导出的是一个函数,只能使用 module.exports。

export

  • export 用于规定模块的对外接口,可以输出变量、函数或类。export 规定的对外接口必须与模块内部的变量建立一一对应关系。

export default

  • 使用 export 时,输出的内容和接收时用的变量名称必须一致,否则无法加载;使用 export default,则为模块指定默认输出,同时,可以为接收到的变量指定其它名称
  • 使用 export 输出的变量或者函数,在 import 时,需要使用大括号;使用 export default 则不用

import

  • import 和 export 配对使用,用于加载接收 export 输入的内容

require

  • require 用于加载模块文件,默认加载的文件后缀名为.js
  • 如果加载的文件路径以/开头,则表示加载的是一个绝对路径
  • 如果加载的文件路径以./或../开头,则表示加载的是一个相对当前位置的路径
  • 如果加载的文件路径不以以上内容开头,则加载的是一个核心模块,默认会到 node_modules 下寻找

export 与 import 的搭配

  • 模块是独立的文件,该文件内部的所有的变量外部都无法获取。如果希望获取某个变量,必须通过 export 输出。
  • export 命令规定的是对外接口,必须与模块内部变量建立一一对应的关系
  • export 模块可以位于模块中的任何位置,但是必须是在模块顶层,如果在其他作用域内,会报错。因为 es6 模块是静态加载的,所以 import 和 export 不能出现在判断等动态语句中
  • export 定义了模块的对外接口后,其他 JS 文件就可以通过 import 来加载这个模块。
  • import 命令具有提升效果,会提升到整个模块的头部,首先执行

第一种导出方式:默认导出

一个文件模块只能默认导出一个变量,引入默认导入的变量时,import 不需要使用大括号。如果导出的是匿名函数,则 import 时可以为匿名函数指定任意名字。

// name.js
let firstname = "yuan";
export let lastname = "chengcheng";
export function multiply(x, y) {
  return x * y;
}
​
export default firstname;
import firstname, { lastname, multiply } from "name.js";

第二种导出方式:直接导出

既可以直接导出变量,也可以直接导出函数或类

// name.js
export let firstname = "yuan";
export let lastname = "chengcheng";
export function multiply(x, y) {
  return x * y;
}
import { firstname, lastname, multiply } from "name.js";

第三种导出方式:批量导出

// name.js
let firstname = "yuan";
let lastname = "chengcheng";
function multiply(x, y) {
  return x * y;
}
​
// 可以在export中通过as给输出的变量改名
export { firstname as name, lastname, multiply };
// 也可以在import中通过as给输入的变量改名
import { name, lastname as lname, multiply } from "name.js";

第四种导出方式:整体加载

对于导出的所有变量,可以使用(*)指定一个对象,则所有的输出都会加载到这个对象上。

// name.js
export let firstname = "yuan";
export let lastname = "chengcheng";
export function multiply(x, y) {
  return x * y;
}
import * as name from "name.js";

import()函数

由于 import 命令只能静态加载,所以引入 import()函数,用于完成动态加载。

语法:import(specifier)

specifier 用来指定所要加载的模块的位置。import 能接受什么参数,import()可以接受同样的参数。

import()返回一个 Promise 对象。

const main = document.querySelector("main");
​
import(`./section-modules/${someVariable}.js`)
  .then((module) => {
    module.loadPageInto(main);
  })
  .catch((err) => {
    main.textContent = err.message;
  });

使用场景一:按需加载

button.addEventListener("click", (event) => {
  import("./dialogBox.js")
    .then((dialogBox) => {
      dialogBox.open();
    })
    .catch((error) => {
      /* Error handling */
    });
});

使用场景二:条件加载

if (condition) {
  import('moduleA').then(...);
} else {
  import('moduleB').then(...);
}