require和import(模块导入/导出)

提示:看到我 请让我滚去学习


前言

浅谈require和import

  • require 是 CommonJS 的模块加载方法,主要用于 Node.js 环境。它是在运行时同步加载模块的,这意味着只有当 require 函数执行完,才能继续执行后面的代码
  • import 是 ES6 的模块加载方法,主要用于现代浏览器环境,也被 Node.js v14 及以上版本支持(需要在 package.json 中设置 “type”: “module”)。它是在编译时就确定模块依赖关系的,也就是说 import 语句是静态的,不能在运行时改变。

注意:在 Node.js 中使用 import 时,文件扩展名必须是 .mjs,或者在 package.json 中设置 “type”: “module”,并且导入的模块也必须是 ES6 模块。


ES6 vs CommonJS

ES6 vs CommonJS:import 是 ES6(ECMAScript 2015)的语法,而 require 是 CommonJS 的语法。ES6 模块是 JavaScript 语言标准的一部分,而 CommonJS 模块是 Node.js 的模块系统。

1.静态 vs 动态:import 是静态的,意味着你不能在运行时改变你要导入的模块。另一方面,require 是动态的,你可以在运行时决定要导入哪个模块。

  • Tree shaking 是一个术语,通常用于描述移除 JavaScript 上下文中的未引用代码的过程。它依赖于 ES6 的模块系统的静态结构特性。
    ES6:ES6 模块是静态的,这意味着你不能动态地导入或导出模块。这使得编译器(如 Babel)和打包工具(如 Webpack、Rollup)可以在编译阶段确定哪些模块和模块的哪些部分被使用了。这样,它们就可以删除未使用的代码,从而减少最终的打包大小。这就是所谓的 “tree shaking”。
    CommonJS:相比之下,CommonJS 模块是动态的,这意味着你可以动态地导入或导出模块。这使得在编译阶段很难确定哪些模块和模块的哪些部分被使用了。因此,CommonJS 模块不支持 tree shaking。

2.顶级作用域:import 只能在顶级作用域中使用,而 require 可以在任何地方使用。

3.异步 vs 同步:import 可以异步加载模块(使用 import() 语法),而 require 是同步加载模块。

4.默认导出和命名导出:import 可以同时导入默认导出和命名导出,而 require 需要分别处理默认导出和命名导出。

  • 当使用module.exports命名导出时require会加载整个文件生成对象,在读取对象上命名导出方法。
  • 当使用exports命名导出时import会加载命名导出的方法,其他方法不加载。

CommonJS

CommonJS 使用 exports 导出模块, require 导入模块
具体规范如下:

  1. 如果一个 JS 文件中存在 exports 或者 require,该 js 时一个模块。
  2. 模块内的所有代码均为隐藏代码,包括全局变量、全局函数,这些全局的内容均不应该对全局变量造成任何污染。
  3. 如果一个模块需要暴露一些 API 提供给外部使用,需要通过 exports 导出,exports是一个空的对象, 可以为该对象添加需要导出的内容。
  4. 如果一个模块需要导入其他模块,通过 require 实现,require 是一个函数,传入模块的路径即可返回该模块导出的整个内容。
  5. 在 nodejs 导入自定义模块,要使用相对路径,以./或者…/开头。
//index.js
module.exports = {
  // 导出部分
};
// 或者
exports.xxx;

//导入
const onj = require("./index.js");

node 对 CommonJS 的实现

为了实现 CommonJS 规范,nodejs 对模块做出了以下处理:

  1. 为了高效的执行,仅加载必要的模块。 node 只有执行到require函数时才会加载并执行模块。
  2. 为了隐藏模块中的代码,nodejs 执行模块时,会将模块中的代码放置到一个函数中执行,以保证不污染全局变量,
  3. 为了保证顺利的导出模块内容
    ○ 在模块开始执行前,初始化一个值module.exports = {}
    ○ module.exports = {} 即为模块的导出值
    ○ 为了方便开发者便捷的导出,有声明了一个变量 exports = module.exports
(function (module) {
 module.exports = {};
 let exports = module.exports;

 // 模块中的代码

 return module.exports;
});
  1. 为了避免反复加载同一个模块,nodejs 默认开启了模块缓存,如果加载的模块已经被加载过了,则会自动使用之前的导出结果。

Es6Module

ECMA 组织参考了众多社区模块化标准,终于在 2015 年,随着 ES6 发布了官方的模块化标准
具有以下特点

  1. 使用依赖 预声明 的方式导入模块
    a. 依赖延迟声明
    ⅰ. 优点:某些时候可以提高效率 比如在满足某个条件下 才执行某个模块
    ⅱ. 缺陷:无法在一开始确定模块依赖关系(比较模糊)
    b. 依赖预声明
    ⅰ. 优点:在一开始确定模块依赖关系,可读性较好,利于后期维护
    ⅱ. 缺陷:某些时候效率较低
  2. 灵活的多种导入导出方式
  3. 规范的路径表示法:所有路径必须以 ./ 或 …/ 开头

基本导入导出

模块的引入

在一块非模块标准
目前,浏览器使用以下方式引入一个 ES6 模块文件

<script src="入口文件" type="module">
基本导出

可以有多个, 每个必须有名称

基本语法如下

export 声明表达式
// 或
export {具名符号}
export var a = 1 // 导出a 值为1
export function test(){} // 导出函数test
var age = 18
var sex = 1
export {age,sex}   // 不是对象
// 等同于 export var age = 18
// 等同于 export var sex = 1
export class Per{}

// 重名后面覆盖前面

// 不能
var a = 3
export a
export 3
基本导入

由于使用的是 依赖预加载, 因此,导入任何其他模块。导入代码必须放置所有代码之前。
虽然不会报错,那是应为浏览器处理了,提升到了最前。

// 实际上不是解构, 但可以这样去理解
import { 导入的符号列表 } from "模块路径";

注意细节

● 导入时可以通过关键字 as 对导入符号进行重命名
● 导入时使用的符号时常量,不可修改
● 可以使用 * 号导出所有的基本导出,形成一个对象

import { sex, age } from "./a.js";
// console.log(sex, age); ===> 1 18

import { sex as bfff } from "./a.js";
// console.log(bfff); ===> 1

import * as obj from "./a.js";
// console.log(obj); ===> {sex, age, ...}

import  "./a.js";  // 只运行模块
默认导入导出

每个模块,除了允许有多个基本导出之外,还允许有一个默认导出

因为只能导出一个,因此无需具名

具体语法

export default  默认导出的数据
或
export { 默认导出的数据 as defaut}

export default 1

var a = 1
export default a

var a = 1
export {a as abc}

export default {
    a:'xx', 
    fun(){}
}
默认导入

具体语法

import 接收变量名 from '模块路径'

类似于 CommonJS 中的

var 接收变量名 = require("模块路径")

由于默认导入没有名称

同时有 基本导出和 默认导出
a.js

//a.js
export var a = 0
export default {
    a: 2
}
//index.js
import {a, default as b} from './a.js'
// console.log(a, b) ===> 0 {a: 2}
import * as obj  from './a.js'
// console.log(obj) ===> { a:0, default: {a: 2} }
import defa, {a}  from './a.js'
// console.log(a, defa) ===> 0 {a: 2}

细节

尽量导出不可变值

当导出一个内容时,尽量应该保证内容是不可变的(大部分情况都是如此)。

因为,虽然导入后,无法更改导入内容,但是可以利用导出的方法对内部的内容进行修改,这将会导致一些无法遇到的事情发生。

export const name = "导出常量"
export var age = "导出常量"
export default function(){
    age = "修改内容"
}
可以使用无绑定的导入用于执行一些初始代码

如果只是想执行模块中的一些代码,而不需要导入它的任何内容,可以使用无绑定的导入

import "模块路径"
可以使用绑定在导出,来重新导出另一个模块的内容

有的时候,我们可能需要使用一个模块封装多个模块,任何有选择的将多个模块的内容分别导出,可以使用以下语法

import {a, b} from './a.js'
import defa, {k, j} from './b.js'

export {a, b, defa as default, k, j}

直接导出

export * from './c.js' // 导出 c 模块的所有内容
export {a, b} from './a.js'
export {default as 1, k, j} from './b.js'

总结

在项目中,通常推荐使用 ES6 的 import 语法来导入模块,原因如下:

  • 静态导入:import 是静态导入,这意味着所有的导入都在编译时被处理。这使得工具(如 Babel 和 Webpack)可以进行优化,例如 tree shaking(移除未使用的代码)和代码分割。
  • 默认和命名导出:import 可以同时处理默认导出和命名导出,这使得代码更易读和易维护。
  • ES6 标准:import 是 ES6 的一部分,是 JavaScript 的未来。使用 import 可以使你的代码更现代化。
  • Vue CLI:Vue CLI 创建的项目默认支持 import 语法。
  • 虽然 require 在某些情况下(例如,需要动态导入或条件导入时)也有其用处,但在大多数 Vue 项目中,import 是更好的选择。
  • 39
    点赞
  • 20
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值