提示:看到我 请让我滚去学习
文章目录
前言
浅谈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 导入模块
具体规范如下:
- 如果一个 JS 文件中存在 exports 或者 require,该 js 时一个模块。
- 模块内的所有代码均为隐藏代码,包括全局变量、全局函数,这些全局的内容均不应该对全局变量造成任何污染。
- 如果一个模块需要暴露一些 API 提供给外部使用,需要通过 exports 导出,exports是一个空的对象, 可以为该对象添加需要导出的内容。
- 如果一个模块需要导入其他模块,通过 require 实现,require 是一个函数,传入模块的路径即可返回该模块导出的整个内容。
- 在 nodejs 导入自定义模块,要使用相对路径,以./或者…/开头。
//index.js
module.exports = {
// 导出部分
};
// 或者
exports.xxx;
//导入
const onj = require("./index.js");
node 对 CommonJS 的实现
为了实现 CommonJS 规范,nodejs 对模块做出了以下处理:
- 为了高效的执行,仅加载必要的模块。 node 只有执行到require函数时才会加载并执行模块。
- 为了隐藏模块中的代码,nodejs 执行模块时,会将模块中的代码放置到一个函数中执行,以保证不污染全局变量,
- 为了保证顺利的导出模块内容
○ 在模块开始执行前,初始化一个值module.exports = {}
○ module.exports = {} 即为模块的导出值
○ 为了方便开发者便捷的导出,有声明了一个变量 exports = module.exports
(function (module) {
module.exports = {};
let exports = module.exports;
// 模块中的代码
return module.exports;
});
- 为了避免反复加载同一个模块,nodejs 默认开启了模块缓存,如果加载的模块已经被加载过了,则会自动使用之前的导出结果。
Es6Module
ECMA 组织参考了众多社区模块化标准,终于在 2015 年,随着 ES6 发布了官方的模块化标准
具有以下特点
- 使用依赖 预声明 的方式导入模块
a. 依赖延迟声明
ⅰ. 优点:某些时候可以提高效率 比如在满足某个条件下 才执行某个模块
ⅱ. 缺陷:无法在一开始确定模块依赖关系(比较模糊)
b. 依赖预声明
ⅰ. 优点:在一开始确定模块依赖关系,可读性较好,利于后期维护
ⅱ. 缺陷:某些时候效率较低 - 灵活的多种导入导出方式
- 规范的路径表示法:所有路径必须以 ./ 或 …/ 开头
基本导入导出
模块的引入
在一块非模块标准
目前,浏览器使用以下方式引入一个 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 是更好的选择。