Skip to content

vue3 自定义全局指令

vue3 项目结构如下
frontend
 ┣ src
 ┃ ┣ assets
 ┃ ┣ components
 ┃ ┃ ┣ Loading
 ┃ ┃ ┃ ┣ index.ts
 ┃ ┃ ┃ ┗ index.vue
 ┃ ┃ ┗ index.ts
 ┃ ┣ directives
 ┃ ┃ ┣ lazy
 ┃ ┃ ┃ ┗ index.ts
 ┃ ┃ ┗ index.ts
 ┃ ┣ layout
 ┃ ┃ ┗ index.vue
 ┃ ┣ pages
 ┃ ┃ ┣ home
 ┃ ┃ ┃ ┗ index.vue
 ┃ ┣ router
 ┃ ┃ ┗ index.ts
 ┃ ┣ App.vue
 ┃ ┣ global.d.ts
 ┃ ┣ main.ts
 ┃ ┣ shims.d.ts
 ┃ ┗ vite-env.d.ts
 ┣ types
 ┃ ┣ auto-imports.d.ts
 ┃ ┗ components.d.ts
 ┣ .dockerignore
 ┣ .editorconfig
 ┣ .eslintrc
 ┣ .gitignore
 ┣ .npmrc
 ┣ README.md
 ┣ commitlint.config.js
 ┣ commitlint.config.ts
 ┣ index.html
 ┣ package.json
 ┣ pnpm-lock.yaml
 ┣ tsconfig.json
 ┣ tsconfig.node.json
 ┣ unocss.config.ts
 ┗ vite.config.ts

主要工作目录在 directives 文件夹下

创建 lazy 指令

directives 目录下创建 lazy 文件夹,然后在 lazy 文件夹下创建 index.ts 文件。

文件内容如下:

ts
import errorImage from '~/assets/ai_compass.png'

const imageIsExist = (src: string) => {
  return new Promise((resolve) => {
    const img = new Image()
    img.onload = () => {
      resolve(true)
    }
    img.onerror = () => {
      resolve(false)
    }
    img.src = src
  })
}

export const lazy = {
  mounted(el: Element) {
    const src = el.getAttribute('custom-src')!
    const io = new IntersectionObserver(([{ isIntersecting }]) => {
      if (isIntersecting) {
        imageIsExist(src).then((exist) => {
          if (exist)
            el.setAttribute('src', src)
          else
            el.setAttribute('src', errorImage)

          io.disconnect()
        })
      }
    })
    io.observe(el)
  },
}

export default {
  name: 'lazy',
  directive: lazy,
}

上述代码中,imageIsExist 函数用于判断图片是否存在,lazy 函数用于判断图片是否在可视区域内,如果在可视区域内,则加载图片,否则不加载。

注册 lazy 指令

directives 目录下创建 index.ts 文件,文件内容如下:

ts
import type { App } from 'vue'

interface Directive {
  name: string
  directive: Record<string, () => {}>
}

const directives = import.meta.glob<Record<string, Directive>>('./**/*.ts', { eager: true })

export function setDirectives(app: App) {
  Object.keys(directives).forEach((key) => {
    app.directive(directives[key].default.name, directives[key].default.directive)
  })
}

上述代码中,directives 用于获取 directives 目录下所有的指令,然后遍历注册。

引入指令

main.ts 文件中引入指令,文件内容如下:

ts
import { createApp } from 'vue'
import App from './App.vue'
import { setComponents } from './components'
import { setDirectives } from './directives'


async function bootstrap() {
  const app = createApp(App)
  setComponents(app)
  setDirectives(app)
  app.mount('#app')
}

bootstrap()

使用指令

home 文件夹下创建 index.vue 文件,文件内容如下:

html
<img v-lazy class="w-100% h-100% rounded-full box-border" :src="errorImage" :custom-src="image" />

挂载指令后,图片就会在可视区域内时才加载,否则不加载,并且加载失败时会显示默认图片。