Vue.js + vue-svg-loaderでSVGファイルを表示したところ、SVGファイルのidが消えたので対応した

Vue.jsでSVGファイルをVueコンポーネントとして扱う方法がないかを探したところ、 vue-svg-loader がありました。
visualfanatic/vue-svg-loader: 🔨 webpack loader that lets you use SVG files as Vue components

 
試しに

<svg width="144" height="72" viewBox="0 0 144 72" xmlns="http://www.w3.org/2000/svg">
 <g id="layer">
  <title>rectangle</title>
  <path d="M0,0 L144,0 144,72 0,72 0,0" stroke-width="1.5" stroke="#000" fill="#fff" id="myRect" class="foo" />
 </g>
</svg>

という四角のSVGファイルを用意してみたところ、Vueコンポーネントとして利用できました。

ただ、ブラウザの表示をよく見てみたところ、SVGファイルのid myRect が消えているようでした。

HTML

f:id:thinkAmi:20200411222052p:plain:w450

 
そこで、SVGファイルのidを残したままブラウザで表示する方法を調べたときのメモを残します。

 
目次

 

環境

  • @vue/cli 4.3.1
  • vue 2.6.11
  • vue-svg-loader 0.16.0
  • SVGO 1.3.2

 

vue-svg-loaderでSVGファイルを読み込むまでの環境構築方法

vue-cliでBabelを含めてプロジェクト作成

vue-svg-loader はBabelが必要なため、最初に追加しておきます。

RouterやVuexも含めてありますが、こちらは今回のものには関係ありません。

$ vue create svgvue

Vue CLI v4.3.1
? Please pick a preset: Manually select features
? Check the features needed for your project: Babel, Router, Vuex
? Use history mode for router? (Requires proper server setup for index fallback in production) Yes
? Where do you prefer placing config for Babel, ESLint, etc.? In dedicated config files
? Save this as a preset for future projects? No

 

vue-svg-loaderのインストールと設定ファイル作成
$ cd svgvue

$ npm i -D vue-svg-loader

 
プロジェクトのルートに vue-svg-loader の設定を行う vue.config.js を作成します。

内容はGitHubのREADMEに書かれている Vue CLI の内容そのままです。

module.exports = {
  chainWebpack: (config) => {
    const svgRule = config.module.rule('svg');

    svgRule.uses.clear();

    svgRule
      .use('babel-loader')
      .loader('babel-loader')
      .end()
      .use('vue-svg-loader')
      .loader('vue-svg-loader');
  },
};

 

アプリの作成
SVGファイルの保管先

src/assets/rectangle.svg として保管します。

 

SVGファイルを読み込むコンポーネントの作成

src/components/SvgFileComponent.vue として作成します。

SVGファイルを読み込み、 rectangle タグとして使います。

また、確認用として、SVG画像をクリックした時にSVGファイルの属性を出力するようメソッドを作成しています。

<template>
  <rectangle id="rectangle" @click="handleClick" />
</template>

<script>
  import Rectangle from '../assets/rectangle.svg';

  export default {
    name: "SvgFileComponent",

    components: {
      Rectangle,
    },

    methods: {
      handleClick: function() {
        const svgDoc = document.getElementById('rectangle');
        const paths = svgDoc.getElementsByTagName('path');
        console.log(paths);
        console.log(paths[0]);
        console.log(paths[0].id);
        console.log(paths[0].className);
        console.log(paths[0].classList);
        console.log(paths[0]._prevClass);
      }
    }
  }
</script>

 

viewの作成

上記で作成したコンポーネントを読み込むViewを作成します。

<template>
  <div>
    <svg-file-component />
  </div>
</template>

<script>
  import SvgFileComponent from "../components/SvgFileComponent";
  export default {
    name: "SvgFileView",

    components: {
      SvgFileComponent,
    }
  }
</script>

 

routerやApp.vueの修正

上記のViewを使うよう、routerやApp.vueを修正します。

 

動作確認

npm run serve で起動して動作を確認すると、冒頭のような結果になります。

 

調査・対応

stackoverflowに似た事例がありました。
javascript - vue-svg-loader removes some tags while loading SVGs - Stack Overflow

このことから、SVGO を使った設定へと変えれば良さそうでした。ただ、例として示されていたものが vue.config.js 向けではありませんでした。

 
vue-svg-loaderの公式ドキュメントを見たところ、SVGOを使った書き方がありました。
How to prefix id attributes? - Frequently Asked Questions | Documentation

公式ドキュメントでは prefixIds の設定方法のみ記載されていました。

そこで、 cleanupIDs pluginを無効化するよう設定しました。

const { basename } = require('path');

module.exports = {
  chainWebpack: (config) => {
    const svgRule = config.module.rule('svg');

    svgRule.uses.clear();

    svgRule
      .use('babel-loader')
      .loader('babel-loader')
      .end()
      .use('vue-svg-loader')
      .loader('vue-svg-loader')
      
      // プラグインを追加
      .options({
        svgo: {
          plugins: [
            {
              // デフォルトではSVGファイル中のidを消してしまうため、idをそのままにする
              cleanupIDs: false,
            },
          ],
        },
      });
  },
};

 

動作確認

グローバルにSVGOをインストール
$ npm install -g svgo

 

起動

再度 npm run serve して確認したところ、SVGファイルのidがHTMLに含まれていました。

HTML

f:id:thinkAmi:20200411222123p:plain:w450

コンソール

f:id:thinkAmi:20200411222205p:plain:w450

 

参考:prefixIds pluginについて

READMEには

prefix IDs and classes with the SVG filename or an arbitrary string

と書かれていたため、prefixIds pluginでも良さそうでしたが、

.options({
  svgo: {
    plugins: [
      {
        // 下のは、classNameにprefixが付与される
        prefixIds: true,
      },
    ],
  },
});

と設定したものの、クラスのみprefixが付与されただけでした*1

f:id:thinkAmi:20200411222224p:plain:w450

 

ソースコード

GitHubに上げました。
https://github.com/thinkAmi-sandbox/SVG_Vuejs-sample

*1:使い方が違うだけかもしれませんが...