美文网首页前端构建
在vue项目中配置使用svg

在vue项目中配置使用svg

作者: z_hboot | 来源:发表于2021-09-16 19:59 被阅读0次

    日常使用svg作为图标使用已经很常见了,以下为几种使用方式 .

    file-loader 解析资源文件

    最原始的无非就是直接导入使用了 , webpack使用了file-loader 对所有静态资源进行路径解析,包括图片、字体、文件等.

    简单的配置对.svg 的解析.

    module.exports = {
      module: {
        rules: [
          {
            test: /\.(svg)(\?.*)?$/,
            use: [
              {
                loader: 'file-loader',
                options: {
                  name: 'img/[name].[hash:8].[ext]',
                },
              },
            ],
          },
        ],
      },
    };
    

    filer-loader 可以解析import/require() 文件路径,并把文件输出到构建目录中.

    然后在组件文件中使用导入的资源

    <template>
      <div>
        <img :src="icon404" />
        <!-- <object :data="icon404"></object>
        <iframe :src="icon404"></iframe> -->
      </div>
    </template>
    <script lang="ts">
    import { Component, Vue } from "vue-property-decorator";
    // import icon404 from "@/assets/icons/404.svg";
    const icon404 = require("@/assets/images/icon-404.svg");
    
    interface IState {
      description: string;
    }
    @Component({
      name: "work-bench",
    })
    export default class extends Vue implements IState {
      description = "展示当前项目拥有的组件,数据皆为模拟测试数据.";
      // 实例中定义引用
      icon404 = icon404;
    }
    </script>
    

    通过img \ object \ iframe 作为资源载体加载资源 . 这样引入的方式对于我们常用修改颜色、字号等产生很大不便. 组件中资源过多就存在大量的import .

    可以通过打印查看icon404 的值是什么? 是对引用的当前.svg资源的路径地址.

    svg-sprite-loader 声明式调用

    为什么说是声明式调用 , 因为它将我们需要的svg资源作为一个模版变量,进而进行指向引用即可 .

    首先我们按照npm包文档指引进行配置.

    安装

    # 或 yarn
    npm install svg-sprite-loader -D
    

    webpack 简单配置

    变更了loader 为svg-sprite-loader

    module.exports = {
      module: {
        rules: [
          {
            test: /\.(svg)(\?.*)?$/,
            use: [
              {
                loader: 'svg-sprite-loader',
                options: {
                  symbolId: "icon-[name]",
                },
              },
            ],
          },
        ],
      },
    };
    

    现在在看组件内的.svg 引入 . 已经加载不出来了,这里要注意的是require 导入,

    // ... 访问导入的值
    this.icon404 = icon404.default;
    
    通过svg标签use 呈现

    通过打印查看导入的内容icon404

    <symbol xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" viewBox="0 0 400 400" id="icon-icon-404">
      // ... svg 内容
    </sysmbol>
    

    提供了id 作为引用键值 . 再使用img \ object 不行了, 了解过svg基础知识的同学知道, symbol 用来定义一个图形模版,然后使用use 呈现 .

    svg-sprite-loader 会将加载的svg定义成symbol 汇聚到一个<svg> 元素中添加到body中.

    这样的话 , 我们局部定义svg引用对应的symbol , 通过use标签引用资源.

    导入的icon404 是一个SpriteSymbol<id,viewBox,content>实例, 通过id 进行访问 .

    <template>
        <div>
        <svg>
           <use :xlink:href="`#${icon404.id}`" />
        </svg>
      </div>
    </template>
    

    当我们知道了svg的名称之后,加上webpack的配置symbolId: "icon-[name]" , 就不需要使用icon404 对象了,只需要导入

    <template>
      <div>
        <svg>
           <use :xlink:href="#icon-icon-404" />
        </svg>
      </div>
    </template>
    <script lang="ts">
      import { Component, Vue } from "vue-property-decorator";
      // import icon404 from "@/assets/icons/404.svg";
      // const icon404 = require("@/assets/images/icon-404.svg");
      // 直接导入资源
      import "@/assets/icons/404.svg";
    
      interface IState {
        description: string;
      }
    
      @Component({
        name: "work-bench",
      })
      export default class extends Vue implements IState {
        description = "展示当前项目拥有的组件,数据皆为模拟测试数据.";
        // 此处已不再需要定义实例中的变量
        // icon404 = icon404.default;
      }
    </script>
    

    就可以愉快的使用啦. 这样还存在一个问题就是,每个需要使用的组件都需要导入 . 而且这明显可以作为一个模版抽离出来进行组件化使用.

    批量处理svg

    按照上面的思路,我们可以定一个index.ts . 导入所有的svg图片,然后导出 ,

    // 引入一遍所有的svg图标
    import '@/assets/images/icon-404.svg'
    import '@/assets/images/icon-404.svg'
    import '@/assets/images/icon-404.svg'
    import '@/assets/images/icon-404.svg'
    // ... 更多
    

    记得要在主入口文件main.ts 引入.

    // icon-svg
    import "@/components/svgIcon/index.ts";
    

    这样在所有vue组件中使用,不需要每次都导入了svg了.

    定义svg-icon 公共组件

    当我们在多个组件中使用某一代码段时,我们就想把它抽离成组件.定一个svgIcon.vue . 这样的

    <template>
      <svg :class="svgClass" v-on="$listeners">
        <use :xlink:href="svgName" />
      </svg>
    </template>
    <script lang="ts">
    import { Component, Vue, Prop } from "vue-property-decorator";
    
    @Component({
      name: "svg-icon",
    })
    export default class extends Vue {
      @Prop() /* 图标名称 */ readonly iconName!: string;
      @Prop({ default: "" }) /* 图标类名 */ readonly iconClass?: string;
        
      // 我们配置的loader 选项中,定义了 icon-[name]
      get svgName() {
        return `#icon-${this.iconName}`;
      }
      get svgClass() {
        return `svg-icon ${this.iconClass}`;
      }
    }
    </script>
    <style lang="less" scoped>
    .svg-icon {
      width: 1em;
      height: 1em;
      fill: currentColor;
      overflow: hidden;
    }
    </style>
    
    

    包含了svg名称 , 内部处理引入的sysmbolId . 使用的人只需要知道svg名称是什么就行了. 不关注webpack配置. 还可以加上自定义class名.以及原生事件等等.

    注册为全局组件,在index.ts 调整,

    import Vue from "vue";
    
    import SvgIcon from "./index.vue";
    // 引入一遍所有的svg图标
    import '@/assets/images/icon-404.svg'
    import '@/assets/images/icon-404.svg'
    import '@/assets/images/icon-404.svg'
    import '@/assets/images/icon-404.svg'
    // ... 更多
    
    // SVG 图标
    Vue.component("svg-icon", SvgIcon);
    

    然后就可以在所所有的组件中愉快的使用公共组件svg-icon 了,不需要每次都导入.

    怎么使用呢, 还是上面的组件.

    <template>
      <div>
        <!--<svg>
           <use :xlink:href="#icon-icon-404" />
        </svg>-->
        <!-- 正常使用一个vue组件 -->
        <svg-icon iconName="icon-404"></svg-icon>
      </div>
    </template>
    <script lang="ts">
    import { Component, Vue } from "vue-property-decorator";
    // 导入不需要
    // import icon404 from "@/assets/icons/404.svg";
    // const icon404 = require("@/assets/images/icon-404.svg");
    
    interface IState {
      description: string;
    }
    
    @Component({
      name: "work-bench",
    })
    export default class extends Vue implements IState {
      description = "展示当前项目拥有的组件,数据皆为模拟测试数据.";
      // 不需要
      // icon404 = icon404.default;
    }
    </script>
    
    

    是不是很完美 . 还需要优化的点就是加载svg资源的,我们只不过是换了个地方import

    // 引入一遍所有的svg图标
    import '@/assets/images/icon-404.svg'
    // 成百上千个 ....
    

    想办法批量加载完成 . 幸好webpack提供了一个API 用于做批量加载的事情. 传送门

    require.context 引入指定文件夹下的所有文件

    require.context(dir,useSubDir,regExp,mode)

    1. 需要加载的目录路径
    2. 是否需要搜索其他子目录.
    3. 加载匹配文件的正则表达式.
    4. 加载方式mode=sync

    返回一个require函数 ,可以接受一个request参数 ;

    require函数三个静态属性resolve,keys,id . 通过编译属性kyes 执行每一个加载资源的请求.

    // 指定目录加载所有的.svg资源
    const req = require.context("@/assets/images", false, /\.svg$/);
    // 编译req属性的keys , 对每一个资源路径执行加载函数
    req.keys().forEach(req)
    

    然后我们的index.ts 文件就修改为

    import Vue from "vue";
    import SvgIcon from "./index.vue";
    // import "@/assets/images/icon-404.svg";
    
    // SVG 图标
    Vue.component("svg-icon", SvgIcon);
    
    const req = require.context("@/assets/images", false, /\.svg$/);
    req.keys().forEach(req)
    

    看起来就简洁多了, 你只需要往这个目录添加你想要使用的svg资源就行了, 然后在项目中使用它.

    vue.config.js 配置

    上面简单的自定义webpack配置时,如何配置webpack. 通常我们都使用了vue-cli 脚手架搭建vue项目.

    那就要在vue.config.js 中调整loader配置了.

    // vue.config.js
    // eslint-disable-next-line
    const path = require("path");
    
    module.exports ={
      chainWebpack: (config) => {
        // svg-sprite-loader
        config.module.rules.delete("svg");
        config.module
          .rule("svg-sprite-loader")
          .test(/\.svg$/)
          .include.add(path.join(__dirname, "./src/assets/icons"))
          .end()
          .use("svg-sprite-loader")
          .loader("svg-sprite-loader")
          .options({ symbolId: "icon-[name]" });
        }
    }
    
    

    首先移除了.svg 的默认loader 规则配置. 开始添加针对.svg 文件的loader规则配置.

    关于vue.config.js 配置详解,还会有另一篇文章来说明 . 解析chainWebpack是如何运转的.

    svgo-loader 优化svg

    svgo-loader依赖安装svgo , 用来优化svg资源 , 清理掉多余、不必要的信息,比如:元数据信息、批注信息、隐藏的元素等.

    还有用来操作svg,svgo 可以转换svgSVG-as-XML SVG-as-JS AST 树从而手动新增、更新、删减元素 .

    {
        content: [
          {
            doctype:'',
          },
          {
            comment:'',
          },
          {
            elem: 'svg',
            local: 'svg',
            attrs: {
              // ...
            },
            content:[
              {
                // ... 嵌套子元素
              }
            ]
          }
        ]
    }
    

    需要更详细操作svg的可以自行去查看这个库, 没怎么具体使用过. 如果有这方面的需求,肯定还有有更细致的文章.

    了解过之后,再来看svgo-loader

    安装

     npm install svgo-loader --save-dev
    

    webpack 配置

    简单的配置,需要明确的是它不是file-loader \ svg-sprite-loader 的替代. 作用不一样.

    起一个中间优化的作用.配置中添加svgo-loader 即可.

    module.exports = {
      module: {
        rules: [
          {
            test: /\.(svg)(\?.*)?$/,
            use: [
              {
                loader: 'file-loader',
                options: {
                  name: 'img/[name].[hash:8].[ext]',
                },
              },
                        {
                loader:"svgo-loader"
              }
            ],
          },
        ],
      },
    };
    

    vue.config.js 配置

    在之前配置的svg-sprite-loader 追加loader配置即可.

    // vue.config.js
    // eslint-disable-next-line
    const path = require("path");
    
    module.exports ={
      chainWebpack: (config) => {
        // svg-sprite-loader
        config.module.rules.delete("svg");
        config.module
          .rule("svg-sprite-loader")
          .test(/\.svg$/)
          .include.add(path.join(__dirname, "./src/assets/icons"))
          .end()
          .use("svg-sprite-loader")
          .loader("svg-sprite-loader")
          .options({ symbolId: "icon-[name]" })
            .end()
          .use("svgo-loader")
          .loader("svgo-loader");
        }
    }
    

    链接资源

    webpack依赖管理-require语句 -

    svg基础知识学习

    相关文章

      网友评论

        本文标题:在vue项目中配置使用svg

        本文链接:https://www.haomeiwen.com/subject/khldgltx.html