# vite 导入导出报 require is not defined
当前使用 Vite 做为开发构建工具,而 Vite 默认不支持使用 require 方式进行模块导入导出
可以使用 vite-plugin-require-transform 插件来处理
- 安装 npm install vite-plugin-require-transform
- 在 vite.config.js中添加
| import requireTransform from "vite-plugin-require-transform"; | |
| plugins: [ | |
| requireTransform({ | |
| fileRegex: /.js$|.vue$/, | |
| }), | |
| ], | 
# vite 引入图片资源报 require is not defined
# 方案一
既然如此,那么直接改为 new URL () 的写法。
原本写法是:
| url: require("../assets/images/banner@2x.png"), | 
- 那么使用 new URL()的写法:
| url: new URL(`../assets/images/banner@2x.png`, import.meta.url).href; | 
- 或者是 import的写法 :
| import banner from "../assets/images/banner@2x.png"; | |
| url: banner, | 
# 方案二
由于上面的方法都要一个个修改过于麻烦了,而且项目图片都位于 src/assets/images/ 下,那么直接封装成一个函数,像 hooks 那样调用应该方便很多。
- 在 utils 文件下下新建 useImgUrl.js 文件,简简单单,只需要传入图片文件名及类型即可
| const getImgUrl = file => { | |
| return new URL(`../assets/images/${file}`, import.meta.url).href; | |
| }; | |
| export default getImgUrl; | 
- 在文件中引入并使用,文件内修改只需使用全局替换功能即可
| import getImgUrl from "../utils/useImgUrl"; | |
| url: getImgUrl("banner@2x.png"), | 
# 方案三
上面的方案虽然不用一个个改了,但是 Vue3 没有 mixins 这样可以全局引入的方法,还是需要在不同的文件去引入 hook,这个时候我想能不能像 webpack 的 loader 那样去全局处理 .vue 文件,这样全局替换的操作交给构建工具去自动执行得了,然后发现 Vite 里面并没有 loader 配置,不过好在这时候一个 Vite 插件给了我灵感,可以用 Vite 插件 API 去实现类似的功能,于是转换下思路,写了这么一个插件
- 代码:
| // requireToUrlPlugin.js | |
| export default function requirePlugin() { | |
| return { | |
|     // 插件名称 | |
| name: "vite-plugin-vue-requireToUrlPlugin", | |
|     // 默认值 post:在 Vite 核心插件之后调用该插件,pre:在 Vite 核心插件之前调用该插件 | |
|     // enforce: "post", | |
|     // 代码转译,这个函数的功能类似于 "webpack" 的 "loader" | |
| transform(code, id, opt) { | |
| const vueRE = /\.js$|\.vue$/; | |
| const require = /require/g; | |
|       // 过滤掉非目标文件 | |
| if (!vueRE.test(id) || !require.test(code)) return code; | |
|       // 匹配 require () 内的内容 | |
| const requireRegex = /require\((.*?)\)/g; | |
|       // 将 require () 内的内容替换为 new URL 的写法 | |
| const finalCode = code.replace(requireRegex, "new URL($1,import.meta.url).href"); | |
|       // 将转换后的代码返回 | |
| return finalCode; | |
| }, | |
| }; | |
| } | 
- 在 Vite 配置中引入此插件:
| // vite.config.js | |
| import requireToUrlPlugin from './src/requireToUrlPlugin'; | |
| export default defineConfig(({ command, mode }) => { | |
| plugins: [ | |
| vue(), | |
| requireToUrlPlugin(), | |
|     ] | |
| }); | 
引入后项目中的 require 方法都会被插件自动转换为 new URL () 的语法,真的是终极大招 [doge],这样就不用再一个个文件去改了,非常省事,而且自己写的插件也完全可以自由定制,写法仅供参考。
# 原因及相关原理
# 1. 为什么需要 require 方法
静态资源就是直接存放在项目中的资源,这些资源不需要我们发送专门的请求进行获取。比如 assets 目录下面的图片,视频,音频,字体文件,css 样式表等。
动态资源就是需要发送请求获取到的资源
答:因为项目引入的动态资源被当做静态资源处理了。被打包过后,被打包在新的文件夹下的图片资源会生成新的文件名,在原来的文件名后会加入一串数字,此即为资源哈希化,是为了做服务器缓存用的。那么静态的路径并不能匹配到新的文件名,导致无法正确的引入资源,所以需要加上 require。require 是一个 node 方法,webpack 会将图片当成一个模块,并根据配置文件中的规则进行打包,通过 require 方法拿到的文件地址,就是资源文件编译过后的文件地址。
# 2. 为什么 require 方法失效了
答:因为原来的项目是 VueCli 搭建的,其是构建于 webpack 和 webpack-dev-server 之上的,所以 require 方法会经过 webpack 处理,而 Vite 开发环境是基于原生 ES Module 的,生产环境则是通过 Rollup 进行打包的,Rollup 默认也是不支持 CommonJS 模块的,所以无法识别 require 方法。
# 3. new URL () 为什么就可以
| const imgUrl = new URL('./img.png', import.meta.url).href | 
- **new URL (url,base)** 用来创建一个新 URL 对象: - url —— 完整的 URL,或者仅路径(如果设置了 base)
- base —— 可选的 base URL:如果设置了此参数,且参数 url 只有路径,则会根据这个 base 生成 URL
 - 其中有一个属性是 href,正好是函数的返回值! 
- import.metaimport.meta 对象包含关于当前模块的信息。 
 它的内容取决于其所在的环境。在浏览器环境中,它包含当前脚本的 URL,或者如果它是在 HTML 中的话,则包含当前页面的 URL。
# 4. Vite 插件介绍
Vite 插件扩展了设计出色的 Rollup 接口,带有一些 Vite 独有的配置项。因此,只需要编写一个 Vite 插件,就可以同时为开发环境和生产环境工作。
# 扩展阅读
- Vite 官方中文文档 | 静态资源处理
- Vite 官方中文文档 | 插件 API
- Vue CLI | 处理静态资源
