preface

The original purpose of this article was to introduce SVG sprites to the team members for managing project ICONS, which is a pain point because many projects still use font classes.

When the first phase of the project is completed, the second phase of the project will be entered after a period of time. New development requirements will inevitably lead to new ICONS, and font classes will require a fully packaged font file for ICONS.

Even if only one icon is added in the new requirement, the front end student has to merge the old icon with the new icon and repackage it to generate a font file, which is unacceptable.

SVG Sprites solve this problem perfectly. The whole idea is to generate an SVG file corresponding to each icon in the project, so that the number of SVG files corresponds to the number of ICONS.

If you want to add an icon later, you can simply add a new SVG file. Existing ICONS and SVG files do not need to be involved.

The next part of this paper will take VuE3 as the basic framework and IconFont as the icon library to practice the whole process of icon introduction, use and management step by step. In the second half of this article, we’ll look at how SVG ICONS are handled in multi-theme color change mode.

Generate SVG

Introduction of SVG sprites

SVG Sprites is a technology that has been around for a long time. For more details, you can check out Zhang Xinxu’s future Hot: An Introduction to SVG Sprites in 2014.

SVG Sprites are based on two tag elements :

and

.


groups elements that are not displayed on the interface and act as a template.

elements are used to reference and render ICONS.

For example, there is one of the following SVG ICONS (code below), which is shaped like a heart.

<svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" x="0px" y="0px" width="24px" height="24px" viewBox="0 0 24 24"> <path fill="#E86C60" D = "M17, 0-1.9 c, 0-3.7, 0.8 5,2.1 C10.7, 0.8, 8.9, 0,7,0 C3.1,,0,3.1 0, 0, 7 c0, 6.4, 10.9, 15.4, 11.4, 15.8 C0.2, 0.2, 0.4, 0.2, 0.6, 0.2 s0.4-0.1, 0.6 0.2 C13.1, 22.4, 24,13.4, 24, 7 C24, 3.1, 20.9, 0,17,0 z "> < / path > < / SVG >Copy the code

Now wrap the above path with the symbol tag as follows:

<svg> <symbol viewBox="0 0 24 24" id="heart"> <path fill="#E86C60" D = "M17, 0-1.9 c, 0-3.7, 0.8 5,2.1 C10.7, 0.8, 8.9, 0,7,0 C3.1,,0,3.1 0, 0, 7 c0, 6.4, 10.9, 15.4, 11.4, 15.8 C0.2, 0.2, 0.4, 0.2, 0.6, 0.2 s0.4-0.1, 0.6 0.2 C13.1, 22.4, 24,13.4, 24, 7 C24, 3.1, 20.9, 0,17,0 z "> < / path > < / symbol > < / SVG >Copy the code

Next, put the symbol wrapped code on the page (as shown below) and hide it by adding display: None. This is the equivalent of registering an icon with the ID of heart on the page.

The rest of the page can then refer to the icon by placing a use tag inside the SVG tag. The xlink:href of the use tag is set to the icon ID and the interface will render the heart shape.

<body> <svg style="display: none;" > <symbol viewBox="0 0 24 24" id="heart"> <path fill="#E86C60" D = "M17, 0-1.9 c, 0-3.7, 0.8 5,2.1 C10.7, 0.8, 8.9, 0,7,0 C3.1,,0,3.1 0, 0, 7 c0, 6.4, 10.9, 15.4, 11.4, 15.8 C0.2, 0.2, 0.4, 0.2, 0.6, 0.2 s0.4-0.1, 0.6 0.2 C13.1, 22.4, 24,13.4, 24, 7 C24, 3.1, 20.9, 0,17,0 z "> < / path > < / symbol > < / SVG > < SVG > < the use  xlink:href="#heart"/> <! </ SVG > </body>Copy the code

Get the project icon

When the front end students get the design drawing, they usually go through all the ICONS needed for the whole project.

Iconfont is a vector icon library built by Alibaba experience team, which contains a large number of ICONS for front-end engineers to choose and use.

Click to open the iconFont official website in the browser, select the icon to be used in your project, move the mouse to the icon and click add to the library.

After the icon is collected, click the shopping cart in the right navigation bar of the head, and the popup box appears. Click “Add to item”. After all ICONS are added to the project, the page will automatically jump to the navigation bar Resource Management – my project page (as shown below).

Our goal is to generate an SVG file for the icon, so here are some Settings. Open the project Settings option in the picture above, and then the popup opens as shown below.

In the box that displays the font format, leave only the SVG check box, and click the save button.

The page will be refreshed at this time, and then click the download to local button on the page to download the SVG files of all ICONS and decompress them. The structure of the decompressed files is as shown below.

Looking at the file structure in the figure above, we can see that all the code for SVG ICONS is written in a single file, iconfont. SVG, which is not as expected. The result we want is one SVG file for each icon, not one file for each icon as it is now.

Although IconFont does not currently provide a file separation mechanism, we can use other platforms to help us split the converged SVG files into individual files, such as iconmoon, a similar icon library site.

Click to open the official website of IconMoon in the browser, select IcoMoon App on the right of the top navigation bar to go to the icon selection page, and click Imports Icons on the left of the navigation bar in the head of the page to import the iconfont. SVG file downloaded from IconFont.

In the iconFont column, you can see the icon we led to appear on the page. Then click the icon to mark it as selected, and click the Generate SVG & More button in the lower left corner of the page (as shown below).

Click the button to jump to the page. At this time, click the Download button in the lower left corner (as shown below) to Download the icon.

After the download is complete, decompress the directory. The SVG folder appears in the decompressed directory. Open the folder and you will find that all the ICONS are separated into a single file (as shown below).

Project Settings

Now, in the SRC -> assets folder of the vuE3 project, create a new folder, fonts and subfolder, fonts/ SVG, and throw all the single SVG files generated above under fonts/ SVG.

With the files set up, it’s time to start configuring the project so that vue3 can manage and use the ICONS smoothly.

  • Step 1 Open the command line in the project root directory and run NPM I SVG-sprite-loader -d. We installed a dependency on SVG-Sprite-Loader because it automatically plugs the code of an SVG file into each symbol tag.

  • Step 2 Create a new file vue.config.js in the root directory of the project. Those familiar with VUE should know that vue.config.js is used to configure the build environment.

The detailed configuration parameters of vue.config.js can be queried in vue-CLI official website. We only need to know how to configure SVG-sprite-loader here.

As we all know, vue-CLI’s build environment is based on WebPack. We add various configuration parameters to the vue.config.js file, which vue-CLI will eventually incorporate into webPack’s configuration.

This allows us to configure the development environment using vue.config.js instead of directly manipulating the WebPack configuration file.

Now that you have installed a dependency SVG-sprite-loader, you need to load this loader into the webpack configuration by filling in the following code in vue.config.js.

const resolve = require("path").resolve; Module. exports = {chainWebpack(config){// import icon config.module.rule("svg").exclude.add(resolve("./src/assets/fonts/svg")); config.module.rule("icon").test(/\.svg$/) .include.add(resolve("./src/assets/fonts/svg")).end() .use("svg-sprite-loader") .loader("svg-sprite-loader") .options({ symbolId:'icon-[name]' }); }}Copy the code

If you have studied webpack configuration, it will be easy to see what the above code means. The above code first excludes the SVG rules set in the rule directory “./ SRC /assets/fonts/ SVG “.

Then add a new rule icon to include the “./ SRC /assets/fonts/ SVG “directory, which is where we will store all SVG files.

The code then uses.use and.loader to configure SVG-sprite-Loader into the project environment and sets the symbolId to icon-[name].

Here the symbolId relates to the ID name generated by the

tag. If you set the symbolId to icon-[name], then the

tag on the last page will refer to the icon using icon- plus the file name.

  • The third step inassets/fontsLet’s create a new fileindex.js(The file structure is shown below), and fill in the following two lines of code.

These two lines of code mainly use the webPack function require.context, which helps us automatically import the file module.

Context the first argument represents the destination file directory, the second argument applies to subfolders, and the third argument matches the file format.

const load = require.context("./svg",false,/\.svg$/);
load.keys().map(load);
Copy the code

The return value load is itself a function imported from the module. In addition, it contains a key attribute. Executing the load.

 ["./arrow.svg", "./arrowon.svg", "./downarrow.svg", "./jiantou.svg", "./trash.svg", "./yiwenicon.svg"]
Copy the code

We can see here that load.keys() will return the relative paths of all ICONS in the fonts/ SVG folder, and then use the loda function to load the files in these paths, thus dynamically importing all files ending in.svg in the fonts/ SVG folder.

If you need to add a new icon in the future, you can download a single SVG file from the IconFont website and drop it directly into the Fonts/SVG folder for automatic introduction.

  • The final step is in the project’s entry filemain.jsCall the one created in step 3index.js, execute allsvgAutomatic import of files (code below).
import { createApp } from 'vue'; import App from './App.vue'; // import "@/assets/fonts/index"; // Import router from '@/router/index'; / / routing createApp (App). Use (router). The mount (' # App);Copy the code

Through the above four steps, the configuration of the project is basically completed, and the whole running process can be simply combed.

After the entry file main.js is started, execute Assets /fonts/index.js to start the automatic import of all SVG files.

Once the SVG-sprite-Loader listens to the introduction of.svG-ended files into the project, it wraps the SVG code contents into a

tag (as shown below) and inserts them into the page document.

This will be equivalent to the SVG-Sprite-Loader helping us register all the SVG ICONS on the page, and all we have left to do is refer to the ICONS on the page.

Icon reference

Xlink :href: a string of # icon-prefixed with the name of a font/SVG file will render the corresponding icon of the file.

<template> <div class="home"> <p class="title">Hello world</p> <svg> <use xlink:href="#icon-trash"/> <! </div> </template>Copy the code

A component reference

The use of SVG and the use tag to refer to the icon on the page is not very elegant, and we can transform it into a component.

Create a new file Icon/index.vue under the global components folder Components. This component takes two parameters, name and color(the code below).

The name parameter corresponds to the name of the icon to render, and the color is the color to render. It is important to note here that SVG color changes can only be made through the fill property, not when the color property is assigned.

<template> <svg :style="{fill:color? color:''}"> <use :xlink:href="'#icon-'+name"/> </svg> </template> <script> export default { props:{ name:String, Deafult :null}}} </script>Copy the code

Now refer to the Icon component on the Home page (code below).

The rendered icon is named Trash and the color is blue (as shown below).

<template> <div class="home"> <p class="title">Hello world</p> <Icon name="trash" color="blue"/><! </div> </template> <script> import Icon from "@/components/Icon/index"; export default { components:{ Icon } } </script>Copy the code

Multi-topic support

As explained above, when we give the < SVG > tag the style attribute fill, the final icon color changes as well, which makes it possible for us to achieve multi-theme development requirements.

Let’s set up a scene where we can click a button to switch the theme online, allowing the SVG icon to change as the theme changes.

Configure multi-theme styles

It is preferred to create a new file SCSS/variables. SCSS under the project folder SRC /assets. The code content is as follows:

The code defines three themes, the default theme, Theme 1, and theme 2. Each theme defines its own icon color and background color under the theme.

The second half of the code defines three mixins for setting the fill, color, and background-color properties. Within each mixin, the colors of the different theme Settings are set according to the color Settings of the theme.

// Default theme $icon-color:red; $background-color:#fff; $icon-color1:gray; $background-color1:#eee; $icon-color2:blue; $background-color2:#999; @mixin fill {fill:$icon-color; [theme1 = "theme1"] & {// fill:$icon-color1; } [data-theme = "theme2"] &{// fill:$icon-color2; }} @mixin color {color:$icon-color; $icon-color1; $icon-color1; $icon-color1; $icon-color1; } [data-theme = "theme2"] & {$icon-color2; $icon-color2; }} @mixin backgroudColor {background-color:$background-color; $theme-color :$theme-color; $theme-color :$theme-color; $theme-color :$theme-color; } [data-theme = "theme2"] & {background-color:$background-color; }}Copy the code

Variable. SCSS is a global multi-theme configuration file, which can not only configure the colors that should be rendered under each theme, but also the font size, common width and height.

With the configuration file written, it is now time to reference it to your project. The editor opens the vue.config.js project configuration file in the root directory and adds the following code.

const resolve = require("path").resolve; Module. exports = {chainWebpack(config){// import icon config.module.rule("svg").exclude.add(resolve("./src/assets/fonts/svg")); config.module.rule("icon").test(/\.svg$/) .include.add(resolve("./src/assets/fonts/svg")).end() .use("svg-sprite-loader") .loader("svg-sprite-loader") .options({ symbolId:'icon-[name]' }); }, css: { loaderOptions: { scss: { prependData: `@import "@/assets/scss/variable.scss"; '},}}}Copy the code

Add the configuration property CSS in module.exports and fill in the prependData value with the path where we wrote the multi-theme configuration file.

To avoid file import failure due to different sASS versions, unify the versions of sass and Sass-Loader.

"Sass" : "1.26.5", "sass - loader" : "8.0.2,"Copy the code

After vue.config.js is configured, restart the application. The variable. Next, we don’t need to use @import to import the theme configuration file in the page component. The variables and mixins defined in vari.scss can be used directly.

Icon modified

To make the Icon respond to the theme change, the global Icon component makes the following code changes. If no color is passed, the factor that determines the color of the icon becomes the class name icon.

<template> <svg class="icon" :style="{fill:color? color:''}"> <use :xlink:href="'#icon-'+name"/> </svg> </template> <script> export default { props:{ name:String, color:{ type:String, deafult:null } } } </script> <style lang="scss" scoped> .icon{ @include fill; } </style>Copy the code

The class name icon calls the mixin corresponding to fill. The variable. SCSS file defines fill as follows.

It finally returns a property fill:color. By default fill has a color value of $icon-color.

When the attribute data-theme value on the page document HTML tag is changed to theme1, the color of fill rendering becomes the color defined by theme1. Similarly switching to theme2, the color of fill rendering becomes the color defined by theme2.

@mixin fill {fill:$icon-color; [theme1 = "theme1"] & {// fill:$icon-color1; } [data-theme = "theme2"] &{// fill:$icon-color2; }}Copy the code

To understand the purpose of the above configuration, take a look at the resulting DOM structure of the page. @mixin eventually generates a copy of the styles under each theme (as shown below), so that the stylesheet for each theme takes effect as long as the data-theme of the < HTML > tag is equal to the theme.

Check the page

Fill in the following code for the Home page component. Three button default themes, theme 1 and theme 2 are added to the original page.

Click the button to trigger the updateTheme function, which will modify the value of the data-theme attribute on the < HTML > tag, thus realizing the function of theme switching (the effect picture is as follows).

<template> <div class="home"> <p class="title">Hello world</p> <Icon name="trash"/><! < theme1 > < theme1 > < theme > < theme > < theme > < theme > < theme > <button > <button </div> </template> <script> import Icon from "@/components/Icon/index"; export default { components:{ Icon }, methods: {{updateTheme (name) if (name = = null) {/ / the default theme document. DocumentElement. RemoveAttribute (" data - the theme "); }else{ document.documentElement.setAttribute("data-theme",name); } } }, } </script> <style scoped lang="scss"> .home{ height: 100%; @include backgroudColor; } .title{ @include color; } </style>Copy the code

Final effect:

Stern said

The multi-topic implementation described above is simple to use, but is not suitable for large, complex projects.

Imagine a large project with more than a dozen themes, and the CSS code for each topic is so large that it would not be appropriate to inject all the style code for each theme into the application at once.

The best practice is to load the theme style as needed when the user clicks to switch to a theme and inject it into the application for rendering, which can greatly improve overall performance. For best practices, see the community articles on multi-topic switching.

The source code