5

需求简介

如题,iconfont的symbol引用 结合Quasar组件Svg-icons 实现动态加载字体库。
详细的说,就是做左侧菜单的图标选择,图标库使用iconfont,采用symbol引用,而且要做到动态的读取阿里字体库文件的图标,加载到项目内。动态读取图标库的好处是用户可以自己做一套他自己的图标,配置在项目内的字典项中,然后代码无需修改,灵活性更高,也更方便用户实现个性化的网站。
代码实现大致思路就是:阿里图标会生成symbol在线链接,有了这个生成的symbol链接,能看到这是一个js文件,通过接口请求的方式读取js文件内容(会跨域,在项目做由运维帮忙配置即可),再匹配内容中的symbol id="来获取每个图标的类名,从而获取到所有文件的图标的类名集合,然后动态加载项目内即可。

具体的代码实现接下来一步一步讲解。

具体实现

iconfont的symbol引用

iconfont的symbol引用
参考上面的symbol引用的使用步骤,应用到项目中,这里因为项目是使用Quasar框架的,所以结合Quasar组件Svg-icons 来实现。
因为我们是要做动态加载库,所以不用拷贝链接写死在项目内。直接从第二步开始,二三步骤的内容可以放置在一个组件内:
代码如下:

<template>
  <q-icon>
    <svg
      :class="svgClass"
      aria-hidden="true"
      v-on="$listeners"
    >
      <use :xlink:href="iconName" />
    </svg>
  </q-icon>
</template>

<script>
// doc: https://quasar.dev/vue-components/icon#Inlined-svg
export default {
  name: 'IconSvg',
  props: {
    iconClass: {
      type: String,
      default: ''
    }
  },
  computed: {
    iconName () {
      return `#${this.iconClass}`
    },
    svgClass () {
      return 'icon-svg'
    }
  }
}
</script>

<style scoped>
.icon-svg {
  width: 1em;
  height: 1em;
  vertical-align: -0.15em;
  fill: currentColor;
  overflow: hidden;
}

</style>

配置字典项

image
现在图标使用的阿里云图标库是这个,这里上传或者修改图标后,会重新生成图标库的在线链接。每次图标发生变动,需要在项目中更改新的字体图标库的链接,在字典iconfont里修改数据值。字典项可添加一个或者多个阿里图标库的链接。
image

重头戏:代码

先准备接口请求api:

// 菜单iconfont字典项
export function getMenuIcon () {
  return request({
    url: '/xxx/dict/type/iconfont', // 字典项接口
    method: 'get'
  })
}

// 读取阿里云图标库文件
export function getIconSymbol (url) {
  return request({
    baseURL: process.env.ICON_URL, // 运维帮忙配置的代理服务器
    url: url, // 只需要js文件的名字
    method: 'get'
  })
}

然后,在菜单管理页面,加载选择图标下拉框数据:


    getIconList () {
      getMenuIcon().then(async ({ data }) => {
        if (data.code === 0) {
          const iconfontList = data.data.map(_ => _.value)

          let allSymbol = []
          // 读取 阿里云字体库 文件
          for (let index = 0; index < iconfontList.length; index++) {
            const ele = iconfontList[index]
            loadScript(ele)
            const url = ele.replace('//at.alicdn.com/t', '') // 只需要获取js文件名字即可
            const response = await getIconSymbol(url)
            // eslint-disable-next-line no-useless-escape
            const symbol = [...response.data.matchAll(/symbol id=\"((\w|\-)+)/g)].map(_ => _[1])
            allSymbol = allSymbol.concat(symbol)
          }
          // console.log('所有文件的图标集合:' + allSymbol)
          this.stringOptions = allSymbol
          this.iconOptions = extend(true, this.stringOptions, this.iconOptions)
        }
      })
    },

先获取到文件的路径,然后通过接口请求的方式读取js文件内容,再匹配内容中的symbol id="来获取每个图标的类名,最终获取到所有文件的图标的类名集合,同时,动态加载js文件。

其中,动态插入script如下:

// 动态插入script
export function loadScript (src) {
  const s = document.createElement('script')
  s.type = 'text/javascript'
  s.src = src
  document.body.appendChild(s)
}

下拉框是Quasar select组件实现:

                <q-select
                  class="xxx-select"
                  outlined
                  use-input
                  input-debounce="0"
                  v-model="form.icon"
                  :options="iconOptions"
                  @filter="filterFn"
                  clearable
                >
                  <template v-slot:option="scope">
                    <q-item
                      v-bind="scope.itemProps"
                      v-on="scope.itemEvents"
                    >
                      <q-item-section avatar>
                        <icon-svg :icon-class="scope.opt"></icon-svg>
                      </q-item-section>
                      <q-item-section>
                        <q-item-label v-html="scope.opt" />
                      </q-item-section>
                    </q-item>
                  </template>
                </q-select>

    filterFn (val, update) {
      if (val === '') {
        update(() => {
          this.iconOptions = this.stringOptions
        })
        return
      }

      update(() => {
        const needle = val.toString().toLowerCase()
        this.iconOptions = this.stringOptions.filter(v => v.toLowerCase().indexOf(needle) > -1)
      })
    }
   

效果如下:
image

菜单管理中配置好的图标,会相应的显示在左侧菜单中,这里需要全局的一个加载图标库:
actions:

  // 获取字体库
  GetFontList ({ commit }) {
    return new Promise((resolve, reject) => {
      getMenuIcon().then(({ data }) => {
        if (data.code === 0) {
          const iconfontList = data.data.map(_ => _.value)

          // 动态加载阿里云字体库
          iconfontList.forEach(ele => {
            loadScript(ele)
          })
        }
        resolve()
      }).catch(e => {
        reject(e)
      })
    })
  }

左侧菜单组件内同样使用IconSvg组件显示图标:

  created () {
    this.$store.dispatch('app/GetFontList')
  },

如果这里获取不到图标库,那么,图标可能显示不出来。

问题

最大的问题就是可能会出现很多重复加载的字体库js文件,那这也没办法了,因为图标库一旦改变,字典项就要重设,每次都重新加载,才能保证字体文件是最新的正确图标文件。


北堂棣
6.6k 声望116 粉丝

2016年应届毕业生。