When UI gives multiple SVG ICONS, the front end needs to render different ICONS according to different conditions, and the color of ICONS should be consistent with the color of the surrounding text. For example, in hover state, the color of ICONS and fonts is changed from black to Red. How to achieve this requirement?

About encapsulating SVG icon base components

Typically, you can copy the contents of an SVG file directly into the template and then change the color of its fill.

But here we need to encapsulate the SVG and write an iconbase. vue file as follows (abstract the component according to the SVG format, keep the < SVG > tag, abstract the path part, encapsulate the component, and introduce it dynamically).

encapsulationIconBasecomponent

The encapsulated IconBase component supports passing in:

  • The name of theiconName
  • The width of thewidth
  • highlyheight
  • coloriconColor
<template>
  <svg
    xmlns="http://www.w3.org/2000/svg"
    :width="width"
    :height="height"
    viewBox="0 0 1024 1024"
    :aria-labelledby="iconName"
    role="presentation"
  >
    <title :id="iconName" lang="en">{{iconName}} icon</title>
    <g :fill="iconColor"> <! --> <component v-bind:is="currentIcon" />
    </g>
  </svg>
</template>

<script>
import Vue from 'vue'
export default {
  props: {
    iconName: {
      type: String,
      default: 'box'
    },
    width: {
      type: [Number, String],
      default: 18
    },
    height: {
      type: [Number, String],
      default: 18
    },
    iconColor: {
      type: String,
      default: 'currentColor'}},data () {
    return {
      currentIcon: ' '}},created () {
    this.getSvgIcon()
  },
  
  methods: {
    getSvgIcon() { try { this.registerComponent(this.iconName).then(component => { this.currentIcon = component }) } catch (error) { // console.log(error)}}, /** *@desc registerComponent (name) {const files = require.context(); // console.log(error)}}, /** *@desc'@/components/icons/'.false, /\.vue$/)
      if (files.keys().includes(`./${name}.vue`)) {
        return import('@/components/icons/' + name).then(component => { //eslint-disable-line
          return Vue.extend(component.default)
        })
      } else {
        console.log('Icon component not found')
      }
    }
  }
}
</script>

<style scoped>
svg {
  display: inline-block;
  vertical-align: baseline;
  margin-bottom: -2px; /* yes, I'm that particular about formatting */
}
</style>

Copy the code

The method of dynamically loading component resources is explained here in Part 2. Let’s begin by explaining what a component is when it’s abstracted as a dynamic component.

What exactly are components introduced and abstracted dynamically?

Since the iconbase. vue file is introduced as a dynamic component, the SVG file needs to be processed and referenced as a vue component. Each SVG file needs to be encapsulated in the following structure:

<template>
  <path
    d="M484.266667 272.021333l6.634666 6.72c5.973333 5.973333 13.013333 12.842667 21.098667 20.629334l9.194667-8.917334c7.253333-7.04 13.44-13.184 18.56-18.432a193.28 193.28 0 0 1 277.44 0c75.904 77.525333 76.629333 202.794667 2.133333 281.194667L512 853.333333 204.672 553.237333c-74.474667-78.421333-73.770667-203.690667 2.133333-281.216a193.28 193.28 0 0 1 277.44 0z"
  / >
</template>

Copy the code

It is also important to note that it is best to keep all the SVG encapsulated vue files in one directory for require.context() to use.

How to useIconBaseComponents?

After the package is complete, use the following method, where IconLike is an icon file name.

<template>
  <div id="app">
    <icon-base icon-name="IconLike"></icon-base>111
  </div>
</template>
Copy the code

When the hover style is added, the icon color changes with the text color:

<style lang="stylus" scoped>
#app
  &:hover
    color red
</style>
Copy the code

A description of dynamically loading component resources

In the example above, we used dynamic loading of components. Here’s how to load components dynamically:

Here we use another demo, which was used in the previous visualization large screen configuration project. When you select title, a configuration title component of Echarts, the form component that encapsulates all the configuration items contained in title is dynamically displayed below.

Let’s start with the reasons for using dynamically loaded components

How to do the function shown in the figure? The first thing that comes to mind might be v-if, V-else-if, v-else, but when you have too many conditions you get too redundant, and the page looks complicated. So, in order to simplify the code and reduce the number of steps to manually import components, we use dynamic loading components.

Use dynamic loading components

How to display the corresponding component information according to the selected configuration item? Vue provides the vue.extend() method and the use of is.

  • isExpected to receiveString | Object (component Object of options). whencurrentViewAs you change, the components change. Click on theIs website introductionLook at it.
<template>
    <component v-bind:is="currentView"></component>
</template>
Copy the code
  • At the same time we needVue.extend(component.default)To implement load registered component resources. In order to optimize the function, there is also an error handling mechanism for not found files to prevent failureimportProcessing on success:
/ / handleChangeApi (value) {try {this.registerComponent(value).then(Component => {this.currentView = component})} catch (error) {// Not found the corresponding file}}Copy the code

userequire.context()To reduce the manualimport

Here, require.context() is used, so you can see how to use it, which is useful if you need to import a large number of files.

/** *@desc */ registerComponent (name) {const files = require.context(); /** *@desc'./form'.false, /\.vue$/) 
  if (files.keys().includes(`./${name}.vue`)) {
    return import('./form/' + name).then(component => { 
      return Vue.extend(component.default)
    })
  } else {
    this.$message.error('Configuration item not currently supported')}}Copy the code

That’s how dynamic components are used in VUE.

The above steps illustrate the dynamic use of SVG ICONS in Vue. Finally, insert a little lesson about how a vUE parent passes a picture variable to a child.

Other alternatives

Other tools to help you manage SVG ICONS are:

vue-svg-icon

svg-sprite-loader

svgo-loader

How does a vUE parent pass an image variable to a child?

Usually we use the following code to dynamically import images in vUE:

<img :src="require('.. /.. /assets/happy.png')" />
Copy the code

So if we want to pass variables in require? The answer is no.

Because require’s argument values must be strings.

requiremethods

The parent component requires the image transfer directly. Note: the path must not be @/assets, otherwise it will also report an error.

// Parent component <template> <child :icon="require('.. /.. /assets/happy.png')" />
</template>

<script>
import child from './child'

export default {
  components: {
    child
  }
}
</script>
Copy the code

importmethods

The parent component needs to import the image first, and then pass it as an argument to the child component. The advantage of this method is that it can be written as @/assets/happy.png:

// Parent component <template> <child :icon="happy" />
</template>

<script>
import child from './child'
import happy from '@/assets/happy.png'

export default {
  components: {
    child
  },
  data () {
    return {
      happy
    }
  }
}
</script>
Copy the code

In both scenarios, the child component is written in the same way. The SRC attribute of img is written directly to the variable:

// Child component <template> <img: SRC ="icon" alt />
</template>

<script>
export default {
  props: {
    icon: {
      type: String,
      default: ' '
    }
  },
}
</script>

Copy the code

element-uiUser stamp here: combinediconimgCompatibility writing method:

If you are a user of elementUI, you may be worried that if an icon is displayed as either el-icon-Edit or img, do we pass the two arguments separately?

Of course not. To make the child components more compatible, it is recommended that the parent pass only one parameter.

Img <child :icon="happy"/ > <! -- import happy from'@/assets/happy.png'--> icon <child :icon="el-icon-eleme"/ > <! -- const el-icon-eleme ='el-icon-eleme' -->
Copy the code

Because through a parameter passing, so need to use a regular in the child components/(. *) \. (PNG | JPG | jpeg | | GIF BMP) $/ and / ^ data: image \ / [a-z] +; Base64 / Determine the type:

// Subcomponent <template> <div> < I v-if="isIcon" :class="icon"></i>
    <img v-else :src="icon"/> </div> </template> <script> const IMG_REG = /(.*)\.(png|jpg|jpeg|gif|bmp)$/ const SIMPLE_IMG_BASE_64_REG = /^data:image\/[a-z]+; base64/export default {
  props: {
    icon: {
      type: String,
      default: ' '
    }
  },
  computed: {
    isIcon () {
      return! IMG_REG.test(this.icon) && ! SIMPLE_IMG_BASE_64_REG.test(this.icon) } } } </script>Copy the code