先搞懂,Vue3项目里为啥会出现「require is not defined」
不少刚开始用Vue3做项目的同学,可能会碰到控制台突然蹦出「require is not defined」的报错,明明之前用Vue2或者Node.js的时候require用得很顺,怎么到Vue3就不行了?今天咱们就把这个问题拆碎了讲,从原因到不同场景的解决方法,再到原理层面的理解,帮你彻底搞懂咋处理~
要解决问题,得先明白背后的逻辑,关键在**构建工具**和**模块规范**的差异上。Vue3现在主流的项目脚手架有两种:一种是用Vite创建(Vue官方推荐,更轻更快),另一种是用Vue CLI创建(基于Webpack),这俩构建工具对“模块怎么导入导出”的处理逻辑不一样,而require
属于CommonJS模块规范里的语法,它和浏览器/现代构建工具用的ES Module(import/export
)是两套体系。
- Vite的逻辑:Vite天生基于ES Module工作,开发阶段,它让浏览器直接以
<script type="module">
的方式加载代码,浏览器只认import/export
,不认require
;生产阶段,Vite打包时虽然会兼容CommonJS,但开发阶段直接写require
会被浏览器当场“拒识”,所以报错。 - Webpack的逻辑:Webpack对CommonJS的支持更好(毕竟早期生态以CommonJS为主),默认能处理
require
,但如果你的代码没经过Webpack处理(比如直接在public
文件夹里写脚本,或者在HTML里手写<script>
用require
),浏览器也不认识require
,一样会报错。
不同场景下的解决办法
报错场景不一样,解法也不同,下面分四种常见场景一个个说:
项目是Vite创建的,想用类似require的方式导入资源
Vite项目里不能直接写require
,但可以用ES Module的语法或者Vite提供的特殊方法替代。
-
导入静态资源(图片、样式、JSON等)
比如想在Vue组件里导入一张图片,原来用require
可能写成这样:// 错误示范:Vite里直接写require会报错 const logo = require('./assets/logo.png')
改成ES Module的import语法就行:
import logo from './assets/logo.png' // 然后在模板里用 <img :src="logo" />
要是需要动态拼接资源路径(比如图片名从接口拿),可以用Vite的
new URL()
方法:function getImgUrl(name) { return new URL(`./assets/${name}.png`, import.meta.url).href } // 调用时:getImgUrl('logo') 就能拿到正确的资源路径
原理是
import.meta.url
指向当前模块的路径,new URL()
能帮我们生成带哈希的资源URL(Vite生产环境会给资源加哈希),保证路径正确。 -
导入JS模块(自己写的工具类或第三方库)
如果是自己写的JS文件,把module.exports
改成export
,用import
导入;如果是第三方库(比如lodash),原来用require
的写法:// 错误示范:Vite开发阶段会报错 const _ = require('lodash')
改成ES Module的
import
:import _ from 'lodash'
要是第三方库只有CommonJS版本(看它
package.json
里有没有module
字段,没有的话可能是纯CommonJS),Vite会自动做预构建兼容,但代码里别直接写require
,还是用import
更稳。
项目是Vue CLI(Webpack)创建的,仍报这个错
Vue CLI基于Webpack,理论上默认支持require
,但如果碰到报错,大概率是代码没被Webpack处理到。
-
检查代码位置:比如把JS文件放在
public
文件夹里。public
里的文件会被直接拷贝到输出目录,不经过Webpack编译,所以里面写require
的话,浏览器执行时不认识,就会报错,这时候把文件移到src
文件夹里,让Webpack处理就行。 -
检查自定义配置:如果自己改了Webpack配置,把处理CommonJS的规则删了,也可能报错,这种情况要确保
webpack.config.js
里有处理require
的loader(比如babel-loader
配合@babel/plugin-transform-modules-commonjs
插件),不过Vue CLI默认已经配好了,一般不用动。
举个例子:之前有同学在public/index.html
里直接写<script>require('./utils.js')</script>
,结果报错,因为index.html
里的脚本没经过Webpack,浏览器不认识require
,解决方法是把这段脚本移到src
里的Vue组件或JS文件里,用import
或require
(Webpack处理后就没问题)。
动态加载模块(根据条件加载不同文件)
比如做权限控制,不同角色加载不同的组件/模块,原来用require.ensure
或动态require
,在Vite或Webpack里得换写法。
-
Vite里用
import()
动态导入
import()
是ES Module的动态导入语法,返回Promise,能完美替代动态require
,比如原来的:// 旧写法:动态require(Vite里会报错) const module = require(`./modules/${role}.js`)
改成:
// 新写法:动态import import(`./modules/${role}.js`).then(module => { // 拿到模块后做处理 console.log(module.default) })
注意:Vite对动态路径的解析有“静态分析”要求,路径不能完全动态(比如
import(http://xxx.com/${path}.js)
这种完全外部的路径可能不行),但像./modules/${role}.js
这种相对路径,Vite能分析出可能的模块,预构建时处理。 -
Webpack里用
require.ensure
或动态require
Webpack支持require.ensure
做代码分割,也支持动态require
(比如require('./modules/' + role + '.js')
),但要确保路径是Webpack能解析的,如果报错,检查路径是否正确,或者有没有开启output.publicPath
配置。
Vue3 SSR(服务端渲染)环境下的处理
SSR项目分服务端(Node.js环境,支持require
)和客户端(浏览器环境,支持ES Module),这时候要区分环境导入模块。
可以用条件判断:
let myModule if (typeof window === 'undefined') { // 服务端:用require myModule = require('./server-only-module.js') } else { // 客户端:用import() import('./client-only-module.js').then(mod => { myModule = mod }) }
或者统一用import()
,让构建工具(比如Vite或Webpack)自动处理服务端和客户端的差异,Vite在SSR时会自动处理CommonJS和ES Module的转换,只要代码里别硬写require
,用import
或import()
基本不会踩坑。
从原理层面理解:模块规范与构建工具的适配
为啥require
在Vue3里突然“失效”?本质是JavaScript模块规范的演进和构建工具的设计逻辑导致的。
-
CommonJS vs ES Module
- CommonJS是Node.js默认的模块规范,用
require
导入,module.exports
导出,属于运行时加载(代码执行时才去加载模块),而且模块输出的是“值的拷贝”。 - ES Module是浏览器和现代构建工具主推的规范,用
import/export
,属于编译时加载(代码编译阶段就确定依赖,静态分析),模块输出的是“值的引用”。
浏览器原生只支持ES Module(要加
<script type="module">
),不认识require
;Node.js早年只支持CommonJS,后来也加了对ES Module的支持,但需要配置。 - CommonJS是Node.js默认的模块规范,用
-
构建工具的角色
Webpack诞生早,要兼容当时海量的CommonJS生态,所以对require
支持很好,能把CommonJS和ES Module混着处理;Vite则是“站在巨人肩膀上”,直接基于ES Module设计,开发阶段让浏览器原生加载ES Module,生产阶段再打包,所以对require
的支持是“兼容”而非“原生支持”,开发阶段写require
就会触发浏览器的报错。
避坑小技巧:日常开发怎么避免这类问题?
知道原理和解法后,再给几个实用小技巧,减少踩坑概率:
-
选脚手架时明确构建工具
创建Vue3项目时,命令行里选对构建工具,比如想简单快用Vite,就选npm create vue@latest
(默认Vite);如果团队更熟悉Webpack,选Vue CLI(npm install -g @vue/cli
然后vue create my-project
),选对了,后续思路更清晰。 -
代码规范:优先用ES Module语法
写自己的模块时,尽量用export/import
,少用module.exports/require
,导入第三方库时,先看文档有没有ES Module的用法,有的话优先用import
,比如axios,既可以const axios = require('axios')
,也可以import axios from 'axios'
,选后者更适配现代构建工具。 -
静态资源管理:按构建工具规则来
Vite里处理图片、样式等静态资源,记牢import
和new URL()
这两种方式;Webpack里可以用require
,但别把资源丢到public
文件夹里裸写脚本。 -
调试时看报错上下文
报错时先看是开发环境还是生产环境,是客户端还是服务端,比如开发环境Vite报错,十有八九是require
没用对;生产环境报错,可能是动态导入路径没处理好,缩小范围后,解法更明确。
Vue3里出现「require is not defined」,核心是构建工具(Vite/Webpack)和模块规范(CommonJS/ES Module)的差异导致的,只要分清自己项目用的是Vite还是Webpack,再对应场景选解法(替换成import、处理静态资源路径、动态导入用import()等),这个问题就不难解决,理解了模块规范和构建工具的逻辑后,下次碰到类似的“语法不兼容”问题,也能更快找到方向~
版权声明
本文仅代表作者观点,不代表Code前端网立场。
本文系作者Code前端网发表,如需转载,请注明页面地址。
发表评论:
◎欢迎参与讨论,请在这里发表您的看法、交流您的观点。