Module(import/require/export)的学习笔记-ES6系列4

本文详细对比了ES6模块化与CommonJS模块化的语法及特性,包括export、import、module.exports和require的使用,以及它们在加载机制、提升效果、条件加载和动态绑定方面的差异。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

module的学习参考了以下大大们的博客~仅为自己的学习笔记。
http://es6.ruanyifeng.com/#docs/module

1. 模块化的需求

为什么要使用模块化,前端模块化的发展历程,在我的上一篇学习笔记《前端模块化的学习笔记》中有写
https://blog.youkuaiyun.com/hongtaochi0464/article/details/92572860

在学习这一章的时候,应当将ES6标准的模块化与CommonJS规范的模块化进行区分。下面先学习一下ES6模块化的语法~

2. ES6模块化的语法

在ES6中,我们使用export导出接口,使用import引入模块。

2.1 export

在模块化中,我们希望一个模块是独立的文件,外部不能随意访问和获取我们定义的变量。只能通过我们给出的接口来进行操作。ES6模块中,使用export来导出接口,具体的写法如下:

//module1.js
export var userName = "cjx"
export var age = "18"
export function speak(){
	console.log(userName+"is speaking");
}

也可以export一个对象(使用{}进行export),如:

//module1.js
var userName = "cjx"
var age = "18"
function speak(){
	console.log(userName+"is speaking");
}
export { userName, age, speak }

一般情况下,export输出的变量就是本来的名字。可以使用as关键字重命名。

//module1.js
var userName = "cjx"
var age = "18"
function speak(){
	console.log(userName+"is speaking");
}
export { userName as name, age, speak }

export命令必须处于模块顶层,不能处于块级作用域内。

2.2 import

在ES6中,使用import命令来加载其他模块export出来的对外接口。
如我们想要在main.js中引入上面module.js暴露出来的接口,就可以用下面的写法

//main.js
import { userName, age, speak } from "./module.js";
speak();
console.log("我叫"+userName+",今年"+age+"岁")

import 也可以使用as关键字来对引入的变量进行重命名,写成如下:

//main.js
import {userName as name} from "./module.js"

2.3 *

除了指定加载某个(些)接口,还可以使用*指定一个对象,将所有的接口都加载在这个对象上。如下面的例子:

//module1.js
var userName = "cjx"
var age = "18"
function speak(){
	console.log(userName+"is speaking");
}
export { userName, age, speak }

现在我们使用*来加载这个模块

//main.js
import * as m1 from "./module.js"

m1.speak();
console.log("我叫"+m1.userName+",今年"+m1.age+"岁")

2.4 export default

使用export default,可以为模块指定默认输出。用其他模块加载该模块时,import命令可以为该接口指定任意名字。如以下写法:

//module.js
export var userName = "cjx"
export var age = "18"
export default function speak(){ 
	console.log(userName+"is speaking");
}

(export default命令也可用在非匿名函数之前,加载的时候会视同匿名函数来加载)

//main.js
import f1 from "./module.js"
f1()

本质上,export default就是输出一个叫做default的变量或方法,然后系统允许你为它取任意名字。因此,一个模块只能有一个默认输出。export default命令在一个模块中只能使用一次。且import命令后面不用加大括号就能唯一获取到default命令。

ES6模块中还支持同时import默认方法和其他接口,如:

//main.js
import f1, {userName,age} from "./module.js"

3. commonJS模块化的语法

在commonJS中,我们使用module.exports导出接口,使用require来引入模块。

3.1 module.exports

将上面的module.js用commonJS模块化的语法写成如下

//module.js
module.exports = {
	userName : "cjx",
	age : "18",
	speak : function(){
		console.log(this.userName+" is speaking");
	}

}

3.2 require

将上面的main.js用commonJS模块化的语法写成如下:

//main.js
var o1 = require("./module.js")
o1.speak();
console.log(o1)

我们使用node来执行一下main.js,输出结果如下:
在这里插入图片描述

4. 比较ES6模块化和commonJS模块化

(1)ES6模块的加载是“编译时加载”,commonJS模块的加载是"运行时加载"。
对于commonJS模块来说

let { userName, age, speak } = require("./module.js")

等同于

let m1 = require("./module.js")
let userName = m1.userName
let age = m1.age
let speak = m1.speak

即相当于 加载了整个module模块再去从加载出的对象中分别读取方法
而对于ES6模块来说

let { userName, age, speak } from "./module.js"

就是单纯的从module模块中加载三个方法,其他方法是不加载的。
(2)import命令具有提升效果,会提升到整个模块的头部执行。如以下的代码不会报错:

speak();
console.log("我叫"+userName+",今年"+age+"岁")
import { userName, age, speak } from "./module.js";

(3)require可以实现条件加载

// import报错
if (x === 2) {
  import module from "./module.js"
}
//require可运行
if (x === 2) {
  var module = require("./module.js")
}

[注]目前已有提案使用import()来进行条件加载。import()返回一个Promise对象。更多的可看阮一峰的博客~
(4)export语句输出的接口,与其对应的值是动态绑定关系,即通过该接口,可以取到模块内部实时的值。

//module.js
export let age = 18;
export oneYearAfter(){
	age++;
}
//main.js
import {age,oneYearAfter} from "./module.js"
console.log(age); //18
oneYearAfter();
console.log(age); //19

CommonJS 模块输出的是值的拷贝,也就是说,一旦输出一个值,模块内部的变化就影响不到这个值。

//module.js
module.exports = {
	age : 18,
	oneYearAfter : function(){
		this.age++
	}
}
let {age,oneYearAfter} = require("./module.js") 
console.log("age1:"+age);
oneYearAfter();
console.log("age2:"+age);

我们使用node运行这个main.js,结果如下:

在这里插入图片描述

前端起项目报错 ERROR Error loading /Users/barbelwe/Desktop/中农在/ydwf-east-manages/vue.config.js: ERROR Error [ERR_REQUIRE_ESM]: require() of ES Module /Users/barbelwe/Desktop/中农在/ydwf-east-manages/vue.config.js from /Users/barbelwe/Desktop/中农在/ydwf-east-manages/node_modules/@vue/cli-shared-utils/lib/module.js not supported. Instead change the require of vue.config.js in /Users/barbelwe/Desktop/中农在/ydwf-east-manages/node_modules/@vue/cli-shared-utils/lib/module.js to a dynamic import() which is available in all CommonJS modules. Error [ERR_REQUIRE_ESM]: require() of ES Module /Users/barbelwe/Desktop/中农在/ydwf-east-manages/vue.config.js from /Users/barbelwe/Desktop/中农在/ydwf-east-manages/node_modules/@vue/cli-shared-utils/lib/module.js not supported. Instead change the require of vue.config.js in /Users/barbelwe/Desktop/中农在/ydwf-east-manages/node_modules/@vue/cli-shared-utils/lib/module.js to a dynamic import() which is available in all CommonJS modules. at exports.loadModule (/Users/barbelwe/Desktop/中农在/ydwf-east-manages/node_modules/@vue/cli-shared-utils/lib/module.js:79:14) at Service.loadUserOptions (/Users/barbelwe/Desktop/中农在/ydwf-east-manages/node_modules/@vue/cli-service/lib/Service.js:330:22) at Service.init (/Users/barbelwe/Desktop/中农在/ydwf-east-manages/node_modules/@vue/cli-service/lib/Service.js:70:30) at Service.run (/Users/barbelwe/Desktop/中农在/ydwf-east-manages/node_modules/@vue/cli-service/lib/Service.js:215:10) at Object.<anonymous> (/Users/barbelwe/Desktop/中农在/ydwf-east-manages/node_modules/@vue/cli-service/bin/vue-cli-service.js:36:9)
最新发布
06-05
<think>我们正在解决一个前端项目启动时因vue.config.js文件导致的ERR_REQUIRE_ESM错误。根据提供的引用信息,ERR_REQUIRE_ESM错误通常是因为在Node.js环境中尝试使用require来加载一个ES模块(ESM)。在Node.js中,require()是CommonJS的导入方式,不支持直接导入ESM模块。因为vue.config.js是一个CommonJS模块,所以当我们在其中使用require导入ES模块时就会报错。引用中给出了几种解决方法:[^1]中直接遇到了同样的错误,但是没有说明具体解决过程,只是展示了配置代码。[^2]的解决方法是清理缓存并重新构建:删除node_modules和package-lock.json,然后重新安装依赖。[^3]的解决方法是降低包的版本,因为该包的较高版本可能是纯ESM模块(即只支持import语法),而旧版本支持CommonJS(用require)。[^4]同样是通过降低包版本到支持CommonJS的版本来解决。但我们的问题是在vue.config.js文件中遇到了这个错误。vue.config.js是VueCLI的配置文件,它运行在Node.js环境下,并且默认使用CommonJS模块系统(即使用requiremodule.exports)。因此,解决办法有几种思路:1.检查vue.config.js中引入的包是否有支持CommonJS的版本,如果有则降低版本(如引用[^3]和[^4]的方法)。2.将vue.config.js改为使用ES模块(ESM)语法(即使用importexport)并确保Node.js支持。但需要注意,从VueCLIv5开始,支持使用ES模块格式的配置文件,需要将文件重命名为vue.config.mjs并在package.json中设置"type":"module"。或者,可以在vue.config.js中通过添加特定注释("type":"module")来启用ESM,但Node.js的版本需要支持。3.如果某个包只支持ESM,而我们又无法降低版本,那么可以尝试在require语句之前动态加载ES模块(通过import()动态导入)。因为require无法加载ESM,但import()可以(它返回一个Promise)。由于动态导入是异步的,而vue.config.js的配置需要同步导出,因此我们不能直接在配置对象中使用异步的import。但是,我们可以将整个vue.config.js改写为一个异步函数,然后导出Promise。注意:VueCLI从4.5.0开始支持异步的vue.config.js。因此,我们可以使用动态导入来解决。具体步骤:方法一:降低包的版本(若可行)检查项目中是否引入了只支持ESM的包,将其降到支持CommonJS的版本。方法二:将vue.config.js改为ESM格式1.将vue.config.js重命名为vue.config.mjs2.在package.json中设置"type":"module"(但这样会影响整个项目,如果项目中的其他文件还是CommonJS可能会出问题)3.或者,不设置package.json的type属性,而是将配置文件的扩展名改为.mjs后,Node.js会将其视为ESM模块。方法三:使用动态导入(import)代替require(适用于只支持ESM的包)例如,原来我们这样引入:constSomeModule=require('some-esm-package');我们可以改为:constSomeModule=awaitimport('some-esm-package').then(m=>m.default||m);但是,因为import()返回的是Promise,我们需要使用async/await,所以整个配置文件需要改为异步函数。示例:原来的vue.config.js(CommonJS):module.exports={//配置}修改后:module.exports=defineConfig(async(phase)=>{//在这里可以动态导入constSomeModule=awaitimport('some-esm-package').then(m=>m.default||m);return{//配置对象};});或者,如果我们不需要根据环境变量动态改变配置,也可以直接使用Promise:module.exports=(asyncfunction(){constSomeModule=awaitimport('some-esm-package').then(m=>m.default||m);return{//配置};})();但注意:在VueCLI中,我们通常使用defineConfig来获得类型提示,而defineConfig也可以接受一个函数(但官方文档未明确说明支持异步函数,但实际上VueCLI服务会等待Promise解析)。然而,根据VueCLI的文档,配置文件可以导出为一个函数,这个函数可以返回配置对象或者一个Promise(在VueCLIv4.5.0以上支持异步)[参考官方文档]。因此,我们可以这样写:const{defineConfig}=require('@vue/cli-service');//这个还是可以用require的,因为@vue/cli-service是CommonJS模块module.exports=defineConfig(async(phase)=>{//动态导入ESM模块constUnocss=awaitimport('unocss/vite').then(m=>m.default||m);//注意:其他需要ESM包的地方同理return{//配置plugins:[Unocss(),//...其他插件]}});但是,注意:如果defineConfig需要从ESM模块中导入,那么就不能用require来导入它。这种情况下,我们可能需要改为动态导入defineConfig。另一种更通用的写法是:不使用defineConfig,而是直接导出配置对象或函数。module.exports=async()=>{constUnocss=awaitimport('unocss/vite').then(m=>m.default||m);return{plugins:[Unocss()]};};根据引用[^1]中的vite.config.ts示例,他们使用了ES模块(import/export)并且使用了defineConfig,那是Vite的配置文件。但我们现在是VueCLI(基于webpack)的配置文件。然而,如果我们使用的是VueCLIv5,它已经支持使用Vite作为构建工具(通过插件),但问题中提到的vue.config.js还是传统的webpack配置。因此,我们按照VueCLI的标准来。具体到你的情况:根据你之前提供的引用信息,你可能在vue.config.js中使用了unocss/vite,而unocss的vite插件可能只支持ESM模块。那么,在vue.config.js中直接require它就会报错。所以,我们可以采用动态导入的方式://vue.config.jsmodule.exports=async()=>{const{defineConfig}=awaitimport('@vue/cli-service');constUnocss=awaitimport('unocss/vite').then(m=>m.default||m);returndefineConfig({plugins:[Unocss()]});};但是要注意,如果@vue/cli-service本身不支持ESM,那么使用动态导入可能也有问题。实际上,@vue/cli-service是一个CommonJS模块,所以最好还是使用require来导入它。所以我们可以混合使用:const{defineConfig}=require('@vue/cli-service');//这个包是CommonJS的,所以require没问题module.exports=defineConfig(async()=>{constUnocss=awaitimport('unocss/vite').then(m=>m.default||m);return{plugins:[Unocss()]};});然而,defineConfig本身并不接受异步函数作为参数。所以我们需要将配置对象放在Promise内。正确写法是:const{defineConfig}=require('@vue/cli-service');module.exports=defineConfig({//但是配置中需要用到Unocss插件,它必须动态导入,这就导致我们必须异步生成配置});但是这样不行,因为Unocss的导入是异步的,不能直接放在同步的配置对象里。因此,我们可以不使用defineConfig,而是直接导出一个异步函数:module.exports=async(phase)=>{constUnocss=awaitimport('unocss/vite').then(m=>m.default||m);return{plugins:[Unocss()]};};这样,VueCLI服务会等待这个异步函数执行完毕并返回配置。如果还需要使用defineConfig来获得类型提示(在支持的环境中),可以这样做://异步加载配置,返回一个Promise<object>asyncfunctionloadConfig(){constUnocss=awaitimport('unocss/vite').then(m=>m.default||m);return{plugins:[Unocss()]};}module.exports=loadConfig();但这样就没有defineConfig的提示了。另外,注意:在VueCLI的配置中,plugins选项是用来配置VueCLI插件的,而unocss/vite是一个Vite插件,它不能直接用在VueCLI(webpack)中。所以这个例子可能不恰当,实际需要安装对应的webpack版本,比如unocss的webpack插件。因此,在解决这个错误时,还要确保你使用的包和VueCLI(webpack)兼容。结论:1.首先,确认你使用的包在VueCLI中是否可用。例如unocss的webpack版本是@unocss/webpack,应该使用这个,而不是unocss/vite(用于Vite)。2.如果包同时支持webpack和vite,那么确认你安装的是支持CommonJS的版本。如果没有,尝试降级。3.如果以上都不行,才采用动态导入的方式。步骤总结:-检查报错中提到的包,看是否有支持CommonJS的版本(比如查看其package.json中的exports字段,或者看其是否有commonjs构建产物)。如果没有,尝试降级到支持CommonJS的版本(如引用[^3][^4]的做法)。-尝试清理node_modules和lock文件,重新安装依赖(引用[^2])。-如果包只支持ESM,或者无法降级,则使用动态导入(import())并确保整个配置文件导出Promise(异步配置)。另外,如果你使用的是VueCLIv5或更新版本,并且想完全迁移到ESM,那么可以将vue.config.js改为vue.config.mjs,并使用ES模块语法。但需要注意,这需要你的Node.js版本在14以上,并且可能影响到其他依赖。最后,根据错误信息,确定是哪个模块引起的。在vue.config.js中找到导致错误的require语句,然后针对性地处理。</think> ### 解决 `vue.config.js` 文件导致的 `ERR_REQUIRE_ESM` 错误 #### 错误原因分析 该错误发生在 Node.js 尝试用 `require()` 加载 ES 模块(ESM)时[^1]。在 Vue CLI 项目中,`vue.config.js` 默认使用 CommonJS 语法,但当引入的依赖(如插件)为纯 ESM 格式时(例如 Unocss/Vite),会导致此错误[^2][^4]。 #### 解决方案(三选一) --- ##### ✅ **方案一:降级依赖版本(推荐)** 1. **查找问题依赖** 检查 `vue.config.js` 中通过 `require()` 导入的依赖(如 `Unocss`)。 2. **安装兼容版本** 将 ESM-only 的依赖降级到支持 CommonJS 的版本: ```bash npm uninstall unocss vite-plugin-uni # 卸载高版本ESM模块 npm i -D unocss@0.45.6 @dcloudio/vite-plugin-uni@2.0.0-307202006000 # 安装支持CJS的版本 [^3][^4] ``` 3. **清理缓存** ```bash rm -rf node_modules package-lock.json npm install ``` --- ##### ✅ **方案二:改用异步动态导入** 修改 `vue.config.js`,用 `import()` 替代 `require()`: ```javascript // vue.config.js module.exports = defineConfig(async () => { const Unocss = await import('unocss/vite').then(m => m.default); const uni = await import('@dcloudio/vite-plugin-uni').then(m => m.default); return { plugins: [ uni(), Unocss() ] }; }); ``` > **注意**:需要 Vue CLI v4.5+ 支持异步配置[^1] --- ##### ✅ **方案三:迁移至 ESM 格式** 1. 重命名配置文件: `vue.config.js` → `vue.config.mjs` 2. 改用 ESM 语法: ```javascript // vue.config.mjs import { defineConfig } from 'vite'; import uni from '@dcloudio/vite-plugin-uni'; import Unocss from 'unocss/vite'; export default defineConfig({ plugins: [uni(), Unocss()] }); ``` 3. 在 `package.json` 添加: ```json { "type": "module" } ``` #### 🔧 验证步骤 1. 执行清理命令: ```bash npm cache clean --force rm -rf node_modules .cache ``` 2. 重新安装依赖: ```bash npm install ``` 3. 重启开发服务器: ```bash npm run serve ``` #### 📌 核心修复逻辑 $$ \text{ERR_REQUIRE_ESM} \propto \frac{\text{ESM 模块}}{\text{CommonJS 环境}} $$ 解决方案本质是通过版本降级实现语法兼容(方案一)、动态转换模块类型(方案二)或迁移至匹配环境(方案三)[^2][^4]。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值