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

Vue3 里 img src 动态赋值怎么才稳?不会报错、不会白屏还能兼顾性能?

terry 6小时前 阅读数 84 #Vue

要说Vue3开发里最常见又最容易栽跟头的小细节,img标签的src动态赋值绝对能排进前三,我前阵子帮朋友改项目,就遇到过离谱的事儿:他本地dev环境好好的,部署到正式站后所有动态头像全白屏,控制台还飘着一堆404;还有之前做电商后台,上传的产品图有时候缓存死活更新不了,运营部天天追着问,踩过这些坑才明白,Vue3里动态给src赋值根本不是“写个变量挂上去”那么简单,得结合不同的资源类型、不同的构建工具,甚至还要考虑安全和性能,今天就把这些经验整理出来,从基础用法到避坑指南,再到进阶优化,一次性讲透。

静态资源vs网络资源?先搞懂你要加载的是什么图

很多人一开始就搞混了,不管三七二十一直接把路径写成变量往src上塞,结果要么本地找不到要么线上挂,Vue3里的图片资源,本质上分两种:一种是静态资源(也就是存放在项目assets、public这类文件夹里的图),另一种是网络资源(后端接口返回的URL、云存储的链接这类),这两种资源的加载逻辑完全不一样,得先分清场景。

网络资源:最简单但别忽略这两点

网络资源其实是动态赋值里最省心的,只要后端或者业务逻辑给的URL是合法的,直接用v-bind或者简写的:src绑定就行,不用经过Vite或者Webpack的额外处理,比如从接口拿到用户的头像链接avatarUrl,直接写:

<template>
  <div class="user-avatar">
    <!-- 基础写法 -->
    <img :src="avatarUrl" alt="用户头像" width="80" height="80">
  </div>
</template>
<script setup>
import { ref, onMounted } from 'vue'
const avatarUrl = ref('')
onMounted(async () => {
  // 模拟接口请求
  const res = await fetch('/api/user/info')
  const data = await res.json()
  avatarUrl.value = data.avatar
})
</script>

不过省心归省心,有两个细节必须注意,不然照样出问题: 第一个是URL拼接场景的编码问题,比如有时候需要自己加参数(比如加个时间戳防缓存,这个后面性能部分会详细说),或者拼接图片规格(像阿里云OSS的?x-oss-process=image/resize,w_100),这时候如果URL里有中文、空格、特殊符号(amp;符号后面的键值对如果没写对编码),浏览器可能会识别错误,别嫌麻烦,这种时候最好用encodeURIComponent或者直接交给专门的URL构造工具,

// 错误示例:直接拼中文文件名
const productId = '123'
const spec = '高清原图'
const imgUrl = `https://example.com/images/${productId}/${spec}.jpg` // 中文spec没编码,可能404
// 正确示例:用encodeURIComponent处理特殊部分
const imgUrl = `https://example.com/images/${productId}/${encodeURIComponent(spec)}.jpg`
// 或者用URL对象,更规范(适合参数多的情况)
const baseUrl = new URL('https://example.com/images')
baseUrl.pathname += `/${productId}/${encodeURIComponent(spec)}.jpg`
baseUrl.searchParams.set('x-oss-process', 'image/resize,w_100,h_100')
baseUrl.searchParams.set('t', Date.now()) // 防缓存
avatarUrl.value = baseUrl.toString()

第二个是URL为空或者失效的兜底问题,接口可能挂掉,返回的头像URL可能是空字符串或者404链接,这时候总不能让页面空着或者显示浏览器默认的碎图吧?可以用error事件加个兜底图,

<template>
  <img 
    :src="avatarUrl" 
    @error="handleAvatarError" 
    alt="用户头像" 
    width="80" 
    height="80"
  >
</template>
<script setup>
import { ref } from 'vue'
import defaultAvatar from '@/assets/default-avatar.png' // 先把静态兜底图导入进来,后面静态资源部分会讲为什么要导入
const avatarUrl = ref('')
const handleAvatarError = (e) => {
  // 防止兜底图也失效导致无限触发error事件
  if (e.target.src !== defaultAvatar) {
    e.target.src = defaultAvatar
  }
}
</script>

静态资源:别再直接写assets路径了!构建工具会把它“吃掉”

静态资源才是Vue3 img动态赋值的重灾区,很多新手甚至部分老开发者,都习惯像写HTML那样直接在变量里存assets下的相对路径,

<template>
  <!-- 错误示例!构建后大概率找不到图 -->
  <img :src="errorAvatarPath" alt="错误的头像" width="80" height="80">
</template>
<script setup>
import { ref } from 'vue'
const errorAvatarPath = ref('@/assets/default-avatar.png')
</script>

为什么这个写法会错?得先搞懂Vite和Webpack这类现代构建工具对静态资源的处理逻辑:存放在assets文件夹里的资源,会被构建工具视为源码的一部分,会被打包压缩、加上哈希值(比如default-avatar.abc123.png),然后放在dist/assets或者public的某个子文件夹里,原有的相对路径在构建后就完全失效了,而存放在public文件夹里的资源,构建工具会原样复制到dist的根目录下,不会做哈希处理,路径是固定的。

那静态资源动态赋值到底该怎么写?得分assets和public两种情况,还要结合构建工具,先讲最常用的Vite,再补充Webpack的。

Vite下的静态资源动态赋值

Vite提供了两种处理assets静态资源动态赋值的方法:一种是静态导入后再绑定,另一种是使用new URL()动态构造

静态导入法:适合资源少、路径固定的场景

如果动态的图片路径其实是有限的(比如性别头像只有男、女、未知三种),直接把这几张图都提前导入进来,然后存到一个对象或者数组里,根据变量切换就行,这种方法最稳,构建工具会自动处理哈希值,而且支持Tree Shaking(如果有没用到的图,打包的时候会自动删掉)。

<template>
  <div class="gender-avatar">
    <img :src="genderAvatars[gender]" :alt="`${gender}性头像`" width="80" height="80">
    <button @click="gender = 'male'">切换男</button>
    <button @click="gender = 'female'">切换女</button>
    <button @click="gender = 'unknown'">切换未知</button>
  </div>
</template>
<script setup>
import { ref } from 'vue'
// 提前导入所有可能用到的图
import maleAvatar from '@/assets/gender-male.png'
import femaleAvatar from '@/assets/gender-female.png'
import unknownAvatar from '@/assets/gender-unknown.png'
// 存到对象里方便切换
const genderAvatars = {
  male: maleAvatar,
  female: femaleAvatar,
  unknown: unknownAvatar
}
const gender = ref('male')
</script>

这种方法的缺点也很明显:如果图片非常多(比如有100个图标要动态切换),提前导入会写很多重复代码,而且如果某些图标是根据后端返回的参数动态决定的(比如后端返回一个iconId,对应assets里的icon-1.png到icon-100.png),提前导入根本做不到,这时候就得用new URL()动态构造法了。

new URL()动态构造法:适合资源多、路径可拼接的场景

Vite在ESM环境下支持用new URL()配合import.meta.url来动态加载assets里的图,import.meta.url是当前模块的URL路径(比如src/views/Home.vue),用它作为基准路径拼接assets里的相对路径,构建工具就能识别并处理这些动态资源了,注意,这种方法拼接的路径部分必须有明确的静态前缀,不能是完全动态的字符串,不然Vite会无法识别哪些资源需要打包,直接忽略掉。

<template>
  <div class="icon-list">
    <div v-for="iconId in 5" :key="iconId" class="icon-item">
      <!-- 正确示例:有明确的静态前缀@/assets/icons/和静态后缀.png -->
      <img :src="getIconUrl(iconId)" :alt="`图标${iconId}`" width="40" height="40">
    </div>
    <!-- 错误示例:路径完全动态,Vite识别不了,会被忽略 -->
    <!-- <img :src="getFullDynamicIconUrl(iconId)" alt="完全动态图标"> -->
  </div>
</template>
<script setup>
import { ref } from 'vue'
const getIconUrl = (iconId) => {
  // 必须有明确的静态前缀和后缀
  return new URL(`@/assets/icons/icon-${iconId}.png`, import.meta.url).href
}
// 错误示例的函数
// const getFullDynamicIconUrl = (iconId) => {
//   const prefix = '@/assets/icons/'
//   const suffix = '.png'
//   return new URL(prefix + `icon-${iconId}` + suffix, import.meta.url).href
// }
</script>

为什么完全动态的不行?这是因为Vite的静态资源处理是在构建阶段完成的,它需要扫描代码里的new URL()调用,找到明确的资源模式,把符合模式的所有资源都打包进去,如果路径是完全动态的,Vite扫描的时候根本不知道要打包哪些图,所以就不处理了,浏览器运行的时候就会找不到。

那如果路径真的完全动态怎么办?比如iconId的范围不固定,或者文件名是后端返回的一个随机字符串?这时候就别把图放在assets里了,放在public文件夹里吧。

public文件夹下的静态资源动态赋值

public文件夹下的资源,Vite会原样复制到dist的根目录,所以路径是固定的,就是相对于项目根目录的绝对路径(比如public/default.png对应的路径就是/default.png),动态赋值的时候直接拼接绝对路径就行,不用导入,也不用new URL()。

<template>
  <div class="public-resource">
    <!-- 正确示例:public下的图直接用绝对路径 -->
    <img :src="`/avatars/${publicAvatarId}.jpg`" alt="public下的头像" width="80" height="80">
    <!-- 绝对路径也可以写成./,不过更推荐/,因为不管路由是history还是hash模式,都能正常访问 -->
    <!-- <img :src="`./avatars/${publicAvatarId}.jpg`" alt="相对根目录的public图"> -->
  </div>
</template>
<script setup>
import { ref } from 'vue'
const publicAvatarId = ref('user-123')
</script>

public文件夹的优点是路径固定、不用额外处理、适合完全动态的资源;缺点是不会被构建工具压缩、不会加哈希值(所以更新缓存会麻烦一点,后面性能部分会讲)、不支持Tree Shaking(不管用不用得到,所有public下的资源都会被复制到dist里,增加打包体积),所以public文件夹更适合放不需要压缩的大文件、完全动态的资源、或者必须保持原始文件名的资源(比如favicon.ico、robots.txt这类),日常开发的小图标、头像、产品缩略图还是尽量放在assets里。

Webpack下的静态资源动态赋值

虽然现在Vue3官方推荐用Vite,但还是有很多老项目在用Webpack(比如Vue CLI创建的Vue3项目),所以也得提一下Webpack的处理方法,Webpack下的静态资源动态赋值和Vite有点像,但语法不一样。

Webpack下处理assets静态资源动态赋值,主要有两种方法:一种是require()静态导入或者部分动态导入,另一种是配置publicPath后用public文件夹

require()导入法:Webpack的老办法

如果是有限的静态资源,直接require()导入后存到对象/数组里就行,和Vite的静态导入法类似:

<template>
  <img :src="genderAvatars[gender]" :alt="`${gender}性头像`" width="80" height="80">
</template>
<script setup>
import { ref } from 'vue'
// Webpack下用require()导入
const genderAvatars = {
  male: require('@/assets/gender-male.png'),
  female: require('@/assets/gender-female.png'),
  unknown: require('@/assets/gender-unknown.png')
}
const gender = ref('male')
</script>

如果是路径可拼接的资源,和Vite一样,必须有明确的静态前缀和后缀,Webpack才能识别:

<template>
  <img :src="getIconUrl(iconId)" :alt="`图标${iconId}`" width="40" height="40">
</template>
<script setup>
import { ref } from 'vue'
const getIconUrl = (iconId) => {
  // 明确的静态前缀和后缀
  return require(`@/assets/icons/icon-${iconId}.png`)
}
</script>

如果完全动态,同样放在public文件夹里,Webpack下public文件夹的路径配置和Vite差不多,不过如果项目配置了自定义的publicPath(比如部署到子目录下),public下的资源路径可能需要加上process.env.BASE_URL:

<template>
  <!-- 加上process.env.BASE_URL,兼容部署到子目录的情况 -->
  <img :src="`${process.env.BASE_URL}avatars/${publicAvatarId}.jpg`" alt="public下的头像">
</template>

避坑指南:这些坑我踩过三次以上,你别再踩了

刚才讲了基础用法,但实际开发中还有很多隐藏的坑,一不小心就会踩进去,白屏、报错、缓存问题都可能出现,现在把我踩过最多的坑列出来,帮你避坑。

坑1:assets路径用了字符串拼接但没有静态前缀

不管是Vite还是Webpack,动态构造assets下的资源路径时,必须有明确的静态前缀和后缀,刚才已经提过,但这个坑实在是太多人踩了,所以再强调一遍,举个具体的例子,比如后端返回的是iconId和iconType,iconType是png或者jpg,你可能会这样写:

// 错误!Vite和Webpack都识别不了
const getIconUrl = (iconId, iconType) => {
  const path = '@/assets/icons/' + iconId + '.' + iconType
  // Vite
  return new URL(path, import.meta.url).href
  // Webpack
  // return require(path)
}

为什么识别不了?因为iconType也是动态的,整个路径的静态部分不够明确,这时候怎么办?可以把iconType限制在几个固定的值里,比如只允许png和jpg,然后分别处理,或者直接把所有图标都转成同一种格式(比如png),或者放在public文件夹里。

坑2:部署到子目录下public资源找不到

这个坑在history模式路由的项目里特别常见,比如你的项目部署在https://example.com/my-vue-app/下,public文件夹里有个default.png,如果你直接写src="/default.png",浏览器会去请求https://example.com/default.png,而不是https://example.com/my-vue-app/default.png,自然就404了。

怎么解决?Vite和Webpack都有对应的配置和变量,Vite下可以用import.meta.env.BASE_URL,这个变量是在vite.config.js里配置的base选项决定的,默认是/,如果部署到子目录,就改成/my-vue-app/(注意前后都要有斜杠):

// vite.config.js
export default {
  base: '/my-vue-app/' // 部署到子目录的配置
}

然后在代码里拼接:

<img :src="`${import.meta.env.BASE_URL}avatars/${publicAvatarId}.jpg`" alt="public下的头像">

Webpack下(Vue CLI)可以用process.env.BASE_URL,这个变量是在vue.config.js里配置的publicPath决定的,默认也是/,部署到子目录改成对应的路径:

// vue.config.js
module.exports = {
  publicPath: '/my-vue-app/'
}

然后拼接:

<img :src="`${process.env.BASE_URL}avatars/${publicAvatarId}.jpg`" alt="public下的头像">

不过有个例外:如果你的路由是hash模式,public下的资源直接用相对路径./或者/一般都没问题,因为hash不会影响URL的路径部分,但为了兼容性,还是建议加上BASE_URL或者publicPath。

坑3:没有处理图片加载失败的情况,显示浏览器默认碎图

刚才讲网络资源的时候提过error事件加兜底图,但很多人会忘记处理静态资源加载失败的情况,或者兜底图也失效了导致无限触发error事件,这里再给一个更完善的处理方案,不管是网络资源还是静态资源都能用:

<template>
  <img 
    :src="imgSrc" 
    :alt="imgAlt"
    @load="handleImgLoad"
    @error="handleImgError"
    class="dynamic-img"
    ref="imgRef"
  >
</template>
<script setup>
import { ref, computed } from 'vue'
// 导入兜底图的静态资源,不管用Vite还是Webpack,这样最稳
import fallbackImg from '@/assets/fallback.png'
// props接收外部传入的图片URL和alt
const props = defineProps({
  src: {
    type: String,
    default: ''
  },
  alt: {
    type: String,
    default: '图片'
  }
})
// 内部维护的imgSrc,优先用props.src,失败后用fallbackImg
const internalImgSrc = ref(props.src)
// 用computed处理props.src的变化,每次外部传入新的src,都重置internalImgSrc
const imgSrc = computed(() => {
  internalImgSrc.value = props.src
  return internalImgSrc.value
})
const imgAlt = computed(() => props.alt)
const imgRef = ref(null)
// 图片加载成功的回调,可以加一些动画或者统计
const handleImgLoad = () => {
  console.log('图片加载成功')
  imgRef.value.classList.add('loaded')
}
// 图片加载失败的回调,双重保险防止无限触发
const handleImgError = () => {
  if (internalImgSrc.value !== fallbackImg) {
    console.log('图片加载失败,使用兜底图')
    internalImgSrc.value = fallbackImg
    // 再检查一次兜底图是否成功加载,如果失败,可以再加一个备用的,或者隐藏图片
    setTimeout(() => {
      if (imgRef.value?.naturalWidth === 0 && imgRef.value?.naturalHeight === 0) {
        console.log('兜底图也加载失败,隐藏图片')
        imgRef.value.style.display = 'none'
      }
    }, 100)
  }
}
</script>
<style scoped>
.dynamic-img {
  opacity: 0;
  transition: opacity 0.3s ease;
}
.loaded {
  opacity: 1;
}
</style>

这个方案封装成一个通用的DynamicImg组件会更好,以后所有动态加载的图片都用这个组件,不用再重复写error事件了。

坑4:assets路径用了绝对路径(比如D:/project/src/assets/xxx.png)

有些开发者可能会直接复制本地文件的绝对路径写到代码里,这是绝对不行的!本地开发可能没问题(如果刚好复制的是当前项目的路径),但部署到服务器后,服务器根本没有D:/project这个路径,肯定会404,不管什么时候,都要用相对路径或者别名(),Vite和Webpack都能正确解析别名。

进阶优化:不仅能加载,还要加载得快、加载得安全

刚才讲的都是基础用法和避坑指南,能保证图片正常显示,但作为一个合格的前端开发者,还得考虑性能和安全问题,让用户体验更好。

性能优化1:图片懒加载

图片懒加载是最基础也最有效的性能优化手段之一,原理是只加载用户可视区域内的图片,可视区域外的图片等用户滚动到附近的时候再加载,这样可以减少首屏的HTTP请求数和带宽消耗,加快首屏加载速度。

Vue3官方没有内置懒加载指令,但可以用Intersection Observer API自己实现一个,非常简单,不用依赖第三方库(当然也可以用vue-lazyload这类成熟的第三方库,不过自己实现的更轻量,更可控)。

先自己实现一个v-lazy指令:

// src/directives/lazy.js
export default {
  mounted(el, binding) {
    // 先把真实的src存到data-src里,用一张占位图(比如透明的1x1像素png)替换
    el.setAttribute('data-src', binding.value)
    el.setAttribute('src', 'data:image/gif;base64,R0lGODlhAQABAAAAACH5BAEKAAEALAAAAAABAAEAAAICTAEAOw==')
    // 创建Intersection Observer实例
    const observer = new IntersectionObserver((entries, observer) => {
      entries.forEach(entry => {
        if (entry.isIntersecting) {
          // 当图片进入可视区域时,把data-src赋值给src
          const img = entry.target
          img.setAttribute('src', img.getAttribute('data-src'))
          // 加载成功或者失败后,停止观察
          img.onload = () => observer.unobserve(img)
          img.onerror = () => observer.unobserve(img)
        }
      })
    }, {
      rootMargin: '100px 0px', // 预加载可视区域上下100px的图片
      threshold: 0.01 // 图片进入可视区域1%就开始加载
    })
    // 开始观察图片
    observer.observe(el)
    // 把observer实例存到el上,方便unmounted的时候清理
    el._observer = observer
  },
  unmounted(el) {
    // 组件卸载时,停止观察,防止内存泄漏
    el._observer?.unobserve(el)
  }
}

然后在main.js里全局注册这个指令:

// main.js
import { createApp } from 'vue'
import App from './App.vue'
import lazyDirective from './directives/lazy'
const app = createApp(App)
app.directive('lazy', lazyDirective)
app.mount('#app')

最后在组件里使用:

<template>
  <div class="img-list">
    <div v-for="i in 100" :key="i" class="img-item">
      <!-- 用v-lazy代替:src -->
      <img v-lazy="`https://picsum.photos/200/300?random=${i}`" :alt="`图片${i}`">
    </div>
  </div>
</template>

如果不想自己实现,也可以用vue-lazyload@3.x版本(专门适配Vue3的),安装和使用都很简单:

npm install vue-lazyload@3.x --save
// main.js
import { createApp } from 'vue'
import App from './App.vue'
import VueLazyload from 'vue-lazyload'
import errorImg from '@/assets/error.png'
import loadingImg from '@/assets/loading.gif'
const app = createApp(App)
app.use(VueLazyload, {
  preLoad: 1.3, // 预加载的倍数
  error: errorImg, // 加载失败的兜底图
  loading: loadingImg, // 加载中的占位图
  attempt: 3, // 加载失败后的重试次数
  listenEvents: ['scroll', 'wheel', 'mousewheel', 'resize', 'animationend', 'transitionend', 'touchmove'] // 触发懒加载的事件
})
app.mount('#app')
<template>
  <img v-lazy="imgSrc" alt="图片">
</template>

性能优化2:给图片加哈希值或者时间戳防缓存

有时候图片更新了,但用户浏览器里还是显示旧的图片,这就是缓存的问题,解决缓存问题的方法有两种:一种是给图片URL加哈希值,另一种是加时间戳或者随机数。

assets下的图片,Vite和Webpack会自动加哈希值(比如default-avatar.abc123.png),所以更新assets里的图片后,重新打包部署,哈希值会变,浏览器就会自动加载新的图片,不用我们手动处理。

public下的图片和网络资源,不会自动加哈希值,所以需要手动处理,public下的图片不建议加时间戳,因为每次用户访问都会加载新的图片,浪费带宽,更好的方法是在更新图片的时候,直接改文件名(比如default-avatar-v2.png),然后更新代码里的路径,如果文件名不能改,才考虑加时间戳或者随机数。

网络资源加时间戳或者随机数比较常见,

// 加时间戳,每次请求都是新的URL
avatarUrl.value = `https://example.com/avatar.jpg?t=${Date.now()}`
// 加随机数
// avatarUrl.value = `https://example.com/avatar.jpg?r=${Math.random()}`

不过加时间戳或者随机数也会有问题:每次用户刷新页面都会加载新的图片,浪费带宽和服务器资源,更好的方法是和后端配合,后端在更新图片的时候,返回一个新的版本号或者ETag,前端根据版本号或者ETag来决定是否更新图片。

性能优化3:使用WebP、AVIF等现代图片格式

WebP、AVIF等现代图片格式比传统的JPG、PNG格式小很多(WebP比JPG小25%-35%,AVIF比WebP小20%-50%),可以大大减少带宽消耗,加快加载速度。

不过要注意浏览器兼容性,比如IE完全不支持WebP,旧版本的Chrome、Firefox也不支持AVIF,所以最好的方法是使用picture标签,提供多种格式的图片,让浏览器自动选择支持的最优格式:

<template>
  <picture>
    <!-- 优先用AVIF格式 -->
    <source :srcset="getImgUrl('avif')" type="image/avif">
    <!-- 其次用WebP格式 -->
    <source :srcset="getImgUrl('webp')" type="image/webp">
    <!-- 最后用传统的JPG格式 -->
    <img 
      :src="getImgUrl('jpg')" 
      :alt="imgAlt"
      v-lazy="getImgUrl('webp')"
      @error="handleImgError"
    >
  </picture>
</template>

如果不想自己处理picture标签,也可以用云存储的自动格式转换功能,比如阿里云OSS、七牛云、腾讯云COS都支持根据浏览器的Accept头自动返回WebP或者AVIF格式的图片,非常方便。

安全优化:防止XSS攻击

虽然img标签的src属性一般不会执行JavaScript(现代浏览器已经禁止了javascript:协议的src),但还是有可能被用来做XSS攻击的跳板,比如加载一个恶意的图片,收集用户的信息,或者配合其他漏洞进行攻击。

所以在动态绑定src的时候,一定要对URL进行校验,只允许http://、https://、data:(比如base64格式的占位图)、blob:(比如本地预览的图片)这些安全的协议,禁止javascript:、vbscript:、data:text/html等危险的协议,可以写一个校验函数:

const isValidImgUrl = (url) => {
  if (!url) return false
  try {
    const parsedUrl = new URL(url, window.location.origin)
    const allowedProtocols = ['http:', 'https:', 'data:', 'blob:']
    return allowedProtocols.includes(parsedUrl.protocol)
  } catch (e) {
    // 如果是相对路径,也允许,但要注意避免路径遍历攻击
    // 简单的路径遍历防护:禁止包含../
    return !url.includes('../')
  }
}

然后在绑定src之前先校验:

<template>
  <img :src="validImgSrc" :alt="imgAlt" @error="handleImgError">
</template>
<script setup>
import { ref, computed } from 'vue'
import fallbackImg from '@/assets/fallback.png'
const props = defineProps({
  src: String,
  alt: String
})
const isValidImgUrl = (url) => {
  // 上面的校验函数
}
const validImgSrc = computed(() => {
  return isValidImgUrl(props.src) ? props.src : fallbackImg
})
</script>

还要注意避免加载来自不可信来源的图片,尽量只加载自己服务器或者可信云存储上的图片。

Vue3 img src动态赋值的“稳字诀”

Vue3里动态给img src赋值,要遵循以下几个“稳字诀”:

  1. 先分资源类型:网络资源直接绑定合法URL,assets资源用静态导入或new URL()(Vite)/require()(Webpack),完全动态的资源放public文件夹。
  2. 处理好路径问题:assets路径要有明确的静态前缀和后缀,部署到子目录下要加BASE_URL或publicPath,禁止用本地绝对路径。
  3. 做好兜底和失败处理:用error事件加兜底图,防止无限触发error。
  4. 兼顾性能和安全:用懒加载减少首屏请求,用现代图片格式减少带宽,用URL校验防止XSS攻击。

只要掌握了这些,Vue3里的img src动态赋值就不会再出问题了,既能正常显示,又能加载得快,还很安全。

版权声明

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

热门