在 Vue3 + Vite2 按需使用 SVG 图标的另一种方式

需要最后达到的使用目的








按钮

VIcon 组件

假设我们首先封装一个 v-icon 组件在 src/components/VIcon.vue



使用 unplugin-icons 作为图标按需加载

在目前的解决方案中,可能会使用 unplugin-icons 插件在 SFC 中静态按需加载图标。



配合 unplugin-vue-components 自动导入省去 scriptimport 部分。

这在单纯使用 v-icon 组件的时候没什么问题,但是如果基于 v-icon 封装其他组件,再使用就会有点繁琐。

例如我们封装一个 v-buttonsrc/components/VButton.vue ,并添加一个前置图标的插槽,代码如:

我们的使用方式如下:

如果 v-button 在嵌套在别的父组件中,使用这个父组件时传递一个按钮的前置图标将会非常繁琐,所以我可能更想这样去使用:

如何转化成这样去用呢,最简单的处理方法是将 v-button 属性 prepend-icon 的静态值 "carbon-accessibility" 字符串替换成 组件导入。

如何做模板组件属性值的静态替换

我们可以在 @vue/compiler-sfc 模板编译后的代码中,通过正则替换文本值,例如模板代码为:



模板会编译成

function _sfc_render(_ctx, _cache, $props, $setup, $data, $options) {
  return (_openBlock(),
    _createBlock($setup["VButton"], {
      "prepend-icon": "mdi-delete"
    }, {
      default: _withCtx(()=>[_hoisted_1]),
      _: 1 /* STABLE */
    }))
}

通过正则

/_create(?:VNode|Block)\((?:_component_v_button|\$setup\["VButton"\]), .*?{.*?("prepend-icon"): (.+?)([,|}].*?\))/gs

匹配出静态部分,再转化为

import __icons_0 from '~icons/mdi/delete'

function _sfc_render(_ctx, _cache, $props, $setup, $data, $options) {
  return (_openBlock(),
    _createBlock($setup["VButton"], {
      "prepend-icon": __icons_0
    }, {
      default: _withCtx(()=>[_hoisted_1]),
      _: 1 /* STABLE */
    }))
}

就完成了组件属性值从字符串转化为传入一个组件。

使用 vite-plugin-iconify 插件

这一过程我封装了一个 vite-plugin-iconify 插件,如上述只需要配置 replaceableProps

// vite.config.ts
import { defineConfig } from 'vite'
import vue from '@vitejs/plugin-vue'
import Iconify from 'vite-plugin-iconify'

// https://vitejs.dev/config/
export default defineConfig({
  plugins: [
    vue(),
    Iconify({
      replaceableProps: [
        { component: 'VButton', props: ['prependIcon'] },
        { component: 'VIcon', props: ['icon'] },
      ]
    }),
  ]
})

改造组件 v-icon 以支持属性值传入组件





改造组件 v-button 以支持属性值传入组件





就可以直接在模板中使用



根据文件目录导入自定义图标

在以往的习惯用法中,我们通常喜欢下载 svg 文件到某个目录作为自定义图标载入,vite-plugin-iconify 也支持了,默认会载入 src/icons 目录中 .svg 后缀的文件,通过 ~icons 导入组件集。

那如何将自定义图标结合 v-icon 使用呢,新增文件如下:

// src/icons/dashboard.svg

我们想通过简单指定 icon="dashboard" 就能使用

这里提供一个使用方式

首先我们创建一个 icons 的组合式 API

// src/compositions/icons.ts

// Utils
import { inject } from 'vue'

// Types
import type { InjectionKey, DefineComponent } from 'vue'

export interface IconsInstance
{
  /**
   * @zh 所有图标的别名
   */
  aliases: Record
}

export const IconsKey: InjectionKey = Symbol.for('app:icons')

/**
 * @zh 创建图标集
 *
 * @param options
 */
export function createIcons (options: IconsInstance) {
  return options
}

/**
 * @zh 使用图标集
 */
export function useIcons () {
  const icons = inject(IconsKey)
  if (!icons) throw new Error('Could not find icons instance')
  return icons
}

main.ts 中全局注册提供者

// Utils
import { createApp } from 'vue'
import App from './App.vue'

// Icons
import icons from '~icons'

// Compositions
import { createIcons, IconsKey } from './compositions/icons'

const app = createApp(App)

app.provide(IconsKey, createIcons({
  aliases: icons
}))

app.mount('#app')

修改 v-icon 组件如下





你就可以愉快的这样使用了

示例代码

示例完整代码查看 examples/vite-vue3 ,详细配置和用法查看 vite-plugin-iconify

你可能感兴趣的:(vitevue3svg)