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
そこで、SVGファイルのidを残したままブラウザで表示する方法を調べたときのメモを残します。
目次
環境
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
このことから、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
コンソール
参考: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。
ソースコード
GitHubに上げました。
https://github.com/thinkAmi-sandbox/SVG_Vuejs-sample
*1:使い方が違うだけかもしれませんが...