vue3+vite2实现动态绑定图片的优雅解决方案
作者:前端cry
背景
在vue3+vite2项目中,我们有时候想要动态绑定资源,比如像下面的代码这样:
<template> <div> <!-- 动态绑定图片资源 --> <img :src="img_src"> </div> </template> <script setup> import { ref } from 'vue'; // 静态图片资源 const img_src = ref('./1.jpg'); </script>
实际效果是这样:
原因分析
我们注意到,控制台的报错信息GET http://127.0.0.1:5173/1.jpg 404 (Not Found)
GET
:表示向服务器请求资源的方式。http://127.0.0.1:5173
:表示主机为项目开启的服务器地址以及端口号http://127.0.0.1:5173/1.jpg
:表示存放在服务器中的图片资源地址。404 (Not Found)
:状态码,404表示找不到资源。
问题就出在http://127.0.0.1:5173/1.jpg
这里,项目文件的路径是src/App.vue
,图片的路径是src/1.jpg
,因此,图片在服务器上的存放路径实际应该是http://127.0.0.1:5173/src/1.jpg
,我们直接在浏览器中访问这个地址。
可以看到,成功获取了图片资源。
由于vite打包的机制,造成了路径错误的问题(类似于vue2 + vue-cli项目的动态绑定图片问题)。
解决
目前网上的解决方案有很多,这里列出其中一种受众的,以及笔者在此基础上进一步加强的解决方案。
普遍的解决方案
话不多说,直接列出代码:
<template> <div> <!-- 动态绑定图片资源 --> <img :src="img_src"> </div> </template> <script setup> import { ref } from 'vue'; // 静态图片资源 const img_src = ref('./1.jpg'); // 主要代码,利用 new URL().href 进行相对路径的拼接 function getAssetImage(imgSrc) { return new URL(imgSrc, import.meta.url).href; } // 当然你也可以这样简写,这里用到es6箭头函数 // const getAssetImage = imgSrc => new URL(imgSrc, import.meta.url).href; </script>
这段代码的重点是new URL().href
和 es6的 import.meta.url
。
new URL(url, baseUrl).href
:路径拼接。比如url是./1.jpg
,baseUrl是http://127.0.0.1:5173/src/App.vue
,那么拼接出来就是http://127.0.0.1:5173/src/1.jpg
import.meta.url
:获取当前模块的路径,比如在src/App.vue
中,就是http://127.0.0.1:5173/src/App.vue
。
所以最后的new URL().href
就是真正的图片资源地址,自己打印一下new URL(url, baseUrl)
和import.meta.url
就容易明白了。
这里给大伙儿画张图,便于理解。
优雅的解决方案
上面的方案可行,但不够优雅。试想,如果有很多文件都需要动态绑定静态图片资源,那岂不是每个.vue文件都要封装一次getAssetImage()
函数?所以下面介绍一种优雅的封装方案。
封装的主要问题是如何自动获取.vue文件的import.meta.url
,就可以不必每次调用都携带import.meta.url
。
核心思路是通过抛出错误获取函数调用栈,从而获得函数调用者文件(或者说模块)的路径,再通过正则表达式提取出路径信息,把import.meta.url
替换掉,就能实现,只传图片相对路径这一个参数,得到图片的完整路径的效果。
直接上代码:
JavaScript版本
// src/utils/common.js export default { getAssetImage(imgSrc, baseUrl) { // console.log('baseUrl', baseUrl); // console.log('new URL(imgSrc, baseUrl).href', new URL(imgSrc, baseUrl).href); // console.log('import.meta.url', import.meta.url); // console.log('new URL(imgSrc, import.meta.url).href', new URL(imgSrc, import.meta.url).href); // 正则匹配函数调用者文件的路径 const regExp1 = /at Proxy.getAssetImage \((.+)\)/g; // 正则命中目标 let target; try { // 抛出错误,获取函数调用栈信息 throw new Error(); } catch (err) { // 匹配函数调用者文件的路径 target = regExp1.exec(err?.stack); // console.log('err.stack', err?.stack); // console.log(target?.[1]); } if (target?.[1]) { // 用户没有传入第二个参数,就使用自动获取的路径 baseUrl = baseUrl || target?.[1]; } if (!baseUrl) { // 用户没有传入第二个参数,且获取函数调用者文件的路径失败 throw new Error('请传入第二个参数 import.meta.url'); } // 返回处理后的资源路径 return new URL(imgSrc, baseUrl).href; } }
TypeScript版本
// src/utils/common.ts export default { getAssetImage(imgSrc: string, baseUrl: string) { // console.log('baseUrl', baseUrl); // console.log('new URL(imgSrc, baseUrl).href', new URL(imgSrc, baseUrl).href); // console.log('import.meta.url', import.meta.url); // console.log('new URL(imgSrc, import.meta.url).href', new URL(imgSrc, import.meta.url).href); // 正则匹配函数调用者文件的路径 const regExp1 = /at Proxy.getAssetImage \((.+)\)/g; // 正则命中目标 let target: RegExpExecArray | null; try { // 抛出错误,获取函数调用栈信息 throw new Error(); } catch (err) { // 匹配函数调用者文件的路径 target = regExp1.exec(err?.stack); // console.log('err.stack', err?.stack); // console.log(target?.[1]); } if (target?.[1]) { // 用户没有传入第二个参数,就使用自动获取的路径 baseUrl = baseUrl || target?.[1]; } if (!baseUrl) { // 用户没有传入第二个参数,且获取函数调用者文件的路径失败 throw new Error('请传入第二个参数 import.meta.url'); } // 返回处理后的资源路径 return new URL(imgSrc, baseUrl).href; } }
在.vue文件中使用
// src/App.vue <template> <div> <!-- 测试 --> <img :src="getAssetImage(img_src)"> <!-- 可以试试在嵌套组件中使用^_^ --> </div> </template> <script lang="ts" setup> import { ref } from 'vue'; import common from '@/utils/common.ts'; const img_src = ref('./1.jpg'); // const getAssetImage = img_src => common.getAssetImage(img_src, import.meta.url); // 可以省略第二个参数import.meta.url,函数内部会自动获取函数的调用路径。 const getAssetImage = img_src => common.getAssetImage(img_src); // 常规写法 // function getAssetImage(img_src) { // return common.getAssetImage(img_src); // } </script>
到此这篇关于vue3+vite2实现动态绑定图片的优雅解决方案的文章就介绍到这了,更多相关vue3 vite2动态绑定图片内容请搜索脚本之家以前的文章或继续浏览下面的相关文章希望大家以后多多支持脚本之家!