• vue3探索——vue3+vite2动态绑定图片优雅解决方案


    优雅解决方案在最下面,小伙伴们儿可以直接前往 😊

    背景

    在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>

    the End……

  • 相关阅读:
    Eye Tracking for Everyone
    (附源码)springboot 中小学餐饮配送系统 毕业设计645661
    什么是拦截器?拦截器如何配置?
    Web 前端程序员通常具备哪些技能和经验?
    使用余弦算法计算向量相似性
    C语言之文件的使用(上)
    Java高并发编程实战3,Java内存模型与Java对象结构
    干货技巧|关于Redis的16个使用技巧
    【力扣每日一题】2023.9.5 从两个数字数组里生成最小数字
    Vue_el挂载点
  • 原文地址:https://www.cnblogs.com/cry0-0/p/17647359.html