跳至主要内容

【狀況】圖片連結錯誤,自訂指令

環境為Vue3

在實作image連結時, 由於不確定傳入src的路徑是否正確,

所以會想加入預設圖片來防止圖片讀不到產生空白的情形。

直覺作法問題

最直覺的作法是在src加上判斷, 如:

最直覺的做法
<img :src="data.image || '/xxx.png'" alt:"image">

此做法的確可以防止路徑錯誤導致圖片空白問題, 但如果連結本身沒問題呢?

可以輸入 "www.google.com" 取代 data.image 試試,

會得到一個空白畫面, 因為src判斷他是一個有效的連結,

如此一來, 直接用 || 作為判斷的方法就會失效。

HTML onError

MDN: https://developer.mozilla.org/zh-CN/docs/conflicting/Web/API/Window/error_event

原始的 HTML 就有處理錯誤的方式, 也就是加上 onError 來處理當圖片連結錯誤時的狀況,

但由於目前環境是屬於 Vue 故直接使用 Vue 的 @error 來做舉例。

<template>
<img src="www.google.com" @error="imgtest" alt="image">
</template>

<script setup>
import defaultImg from '~/public/xxx.png'

function imgtest (e) {
e.target.src = defaultImg
}
</scritp>

目前就能看到圖片做到完整的判斷, 達到我們的需求。

延伸優化

但如果專案裡有一百個地方需要用到 img 並且都需要這個方式來做錯誤處理,

一行行加就有點太沒效率,

這時就能使用 Vue 的 directive 來做一個全域註冊指令。

Vue directive 自訂指令

Vue 官方說明:https://vuejs.org/guide/reusability/custom-directives.html

經由官方說明我們知道

  1. 需要使用在 createApp 下 ((也就是 main.js 內
  2. directive("取名字", el, binding, vnode, preNode) 幾個Hook可以使用
xxx.vue
<script setup>
// enables v-focus in templates
const vFocus = {
mounted: (el) => el.focus()
}
</script>

<template>
<input v-focus />
</template>
main.ts
const app = createApp({})

// make v-focus usable in all components
app.directive('focus', {
/* ... */
})

使用 directive 製作自己的指令

首先為了main.ts 乾淨, 我們用外部引入的方式建立 directive 的 method

  1. app.directive("指令名子", method)
/main.ts
import { defaultImg } from './plugins/directive'

const app = createApp(App)
app.directive('src', defaultImg)

再來在 directive 這個檔案實作指令

  1. 引入 binding type (非ts則不需引入)
  2. 使用 new Image() 判斷圖檔連結是否可讀取
  3. onload 代表讀取正確, el.src 等於傳入連結
  4. onerror 代表讀取失敗, el.src 等於預設圖片
/plugins/directive.ts
import { DirectiveBinding } from 'vue'

export function defaultImg (el:HTMLImageElement, binding:DirectiveBinding) {
const img = new Image()
img.src = binding.value
img.onload = () => {
el.src = binding.value
}
img.onerror = () => {
el.src = '/defaultImg.png'
}
}

到這就完成了指令的註冊, 回到使用地方用剛剛註冊的 v-src 測試。

<template>
<img v-src="www.google.com" alt="image">
</template>

到此就能完美的使用指令避免我們圖檔失效的問題。