Code前端首页关于Code前端联系我们

先搞懂,Vue3项目里为啥会出现「require is not defined」

terry 2周前 (10-02) 阅读数 48 #Vue
文章标签 Vue3;require错误

不少刚开始用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文件里,用importrequire(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,用importimport()基本不会踩坑。

从原理层面理解:模块规范与构建工具的适配

为啥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的支持,但需要配置。

  • 构建工具的角色
    Webpack诞生早,要兼容当时海量的CommonJS生态,所以对require支持很好,能把CommonJS和ES Module混着处理;Vite则是“站在巨人肩膀上”,直接基于ES Module设计,开发阶段让浏览器原生加载ES Module,生产阶段再打包,所以对require的支持是“兼容”而非“原生支持”,开发阶段写require就会触发浏览器的报错。

避坑小技巧:日常开发怎么避免这类问题?

知道原理和解法后,再给几个实用小技巧,减少踩坑概率:

  1. 选脚手架时明确构建工具
    创建Vue3项目时,命令行里选对构建工具,比如想简单快用Vite,就选npm create vue@latest(默认Vite);如果团队更熟悉Webpack,选Vue CLI(npm install -g @vue/cli然后vue create my-project),选对了,后续思路更清晰。

  2. 代码规范:优先用ES Module语法
    写自己的模块时,尽量用export/import,少用module.exports/require,导入第三方库时,先看文档有没有ES Module的用法,有的话优先用import,比如axios,既可以const axios = require('axios'),也可以import axios from 'axios',选后者更适配现代构建工具。

  3. 静态资源管理:按构建工具规则来
    Vite里处理图片、样式等静态资源,记牢importnew URL()这两种方式;Webpack里可以用require,但别把资源丢到public文件夹里裸写脚本。

  4. 调试时看报错上下文
    报错时先看是开发环境还是生产环境,是客户端还是服务端,比如开发环境Vite报错,十有八九是require没用对;生产环境报错,可能是动态导入路径没处理好,缩小范围后,解法更明确。

Vue3里出现「require is not defined」,核心是构建工具(Vite/Webpack)和模块规范(CommonJS/ES Module)的差异导致的,只要分清自己项目用的是Vite还是Webpack,再对应场景选解法(替换成import、处理静态资源路径、动态导入用import()等),这个问题就不难解决,理解了模块规范和构建工具的逻辑后,下次碰到类似的“语法不兼容”问题,也能更快找到方向~

版权声明

本文仅代表作者观点,不代表Code前端网立场。
本文系作者Code前端网发表,如需转载,请注明页面地址。

发表评论:

◎欢迎参与讨论,请在这里发表您的看法、交流您的观点。

热门