開発者は多くの場合、SVG を JSX に直接挿入します。これは便利ですが、JS バンドルのサイズが大きくなります。最適化を追求する中で、バンドルを乱雑にせずに SVG アイコンを使用する別の方法を見つけることにしました。 SVG スプライトとは何か、その使用方法、およびそれらを操作するために使用できるツールについて説明します。
理論から始めて、SVG スプライトを段階的に生成するスクリプトを作成し、最後にviteとwebpackのプラグインについて説明します。
SVGスプライトとは何ですか?
イメージ スプライトは、単一のイメージに配置されたイメージのコレクションです。 SVG スプライトは SVG コンテンツのコレクションであり、 <symbol />
にラップされ、 <svg />
に配置されます。
たとえば、単純な SVG ペン アイコンがあります。
SVG スプライトを取得するには、 <svg />
タグを<symbol />
に置き換え、外部で<svg />
でラップします。
これは SVG スプライトになり、その中にid="icon-pen"
アイコンが含まれています。
わかりましたが、このアイコンを HTML ページに配置する方法を考えなければなりません。 <use />
タグをhref
属性とともに使用し、アイコンの ID を指定します。これにより、SVG 内でこの要素が複製されます。
<use />
どのように機能するかの例を見てみましょう。
この例では、円が 2 つあります。最初のものは青い輪郭を持ち、2 つ目は最初のものの複製ですが、赤い塗りつぶしが施されています。
SVG スプライトに戻りましょう。 <use />
を使用すると、次のようになります。
ここには、ペンのアイコンが付いたボタンがあります。
これまでは、 <button />
内のコードなしでアイコンを使用してきました。ページ上に複数のボタンがある場合、すべてのアイコンは SVG スプライトから取得され、再利用できるため、HTML レイアウトのサイズに複数回影響することはありません。
SVGスプライトファイルの作成
SVG スプライトを別のファイルに移動して、 index.html
ファイルを乱雑にしないようにしましょう。まず、 sprite.svg
ファイルを作成し、その中に SVG スプライトを配置します。次のステップでは、 <use/>
のhref
属性を使用してアイコンへのアクセスを提供します。
SVG スプライト作成の自動化
アイコンの使用時間を大幅に節約するために、このプロセスの自動化を設定しましょう。アイコンに簡単にアクセスして必要に応じて管理するには、アイコンをそれぞれ独自のファイルに分離する必要があります。
まず、すべてのアイコンを同じフォルダーに配置する必要があります。例:
次に、これらのファイルを取得して単一の SVG スプライトに結合するスクリプトを作成しましょう。
プロジェクトのルート ディレクトリに
generateSvgSprite.ts
ファイルを作成します。
グロブライブラリをインストールします。
npm i -D glob
globSync
を使用して、各アイコンの絶対パスの配列を取得します。ここで、各ファイル パスを反復処理し、Node の組み込みライブラリfsを使用してファイルのコンテンツを取得します。
各アイコンの SVG コードがあり、それらを結合できるようになりました。ただし、各アイコン内の
svg
タグをsymbol
タグに置き換え、不要な SVG 属性を削除する必要があります。
SVG コードを HTML パーサー ライブラリで解析して、DOM 表現を取得する必要があります。 node-html-parser を使用します。
SVG コードを解析し、実際の HTML 要素であるかのように SVG 要素を取得しました。
同じパーサーを使用して、空の
symbol
要素を作成し、svgElement
の子をsymbol
に移動します。svgElement
から子を抽出した後、そこからid
とviewBox
属性も取得する必要があります。id
にはアイコンファイルの名前を設定しましょう。これで、SVG スプライトに配置できる
symbol
要素ができました。したがって、ファイルを反復処理する前にsymbols
変数を定義し、symbolElement
文字列に変換し、それをsymbols
にプッシュするだけです。最後のステップは、SVG スプライト自体を作成することです。これは、「ルート」に
svg
があり、子としてシンボルを持つ文字列を表します。const svgSprite = `<svg>${symbols.join('')}</svg>`;
また、以下で説明するプラグインの使用を検討していない場合は、作成したスプライトを含むファイルを静的フォルダーに置く必要があります。ほとんどのバンドラーは
public
フォルダーを使用します。fs.writeFileSync('public/sprite.svg', svgSprite);
これで終わりです。スクリプトを使用する準備ができました。
// generateSvgSprite.ts import { globSync } from 'glob'; import fs from 'fs'; import { HTMLElement, parse } from 'node-html-parser'; import path from 'path'; const svgFiles = globSync('src/icons/*.svg'); const symbols: string[] = []; svgFiles.forEach(file => { const code = fs.readFileSync(file, 'utf-8'); const svgElement = parse(code).querySelector('svg') as HTMLElement; const symbolElement = parse('<symbol/>').querySelector('symbol') as HTMLElement; const fileName = path.basename(file, '.svg'); svgElement.childNodes.forEach(child => symbolElement.appendChild(child)); symbolElement.setAttribute('id', fileName); if (svgElement.attributes.viewBox) { symbolElement.setAttribute('viewBox', svgElement.attributes.viewBox); } symbols.push(symbolElement.toString()); }); const svgSprite = `<svg>${symbols.join('')}</svg>`; fs.writeFileSync('public/sprite.svg', svgSprite);
このスクリプトをプロジェクトのルートに置き、 tsxで実行できます。
npx tsx generateSvgSprite.ts
実際、ここでtsxを使用しているのは、以前はどこでも TypeScript でコードを記述していたためであり、このライブラリを使用すると、TypeScript で記述されたノード スクリプトを実行できるようになります。純粋な JavaScript を使用したい場合は、次のように実行できます。
node generateSvgSprite.js
それでは、スクリプトが何をしているのかをまとめてみましょう。
-
src/icons
フォルダーで.svg
ファイルがないか調べます。
- すべてのアイコンの内容を抽出し、そこからシンボル要素を作成します。
- すべてのシンボルを単一の
<svg />.
-
public
フォルダーにsprite.svg
ファイルが作成されます。
アイコンの色を変更する方法
よくある重要なケースの 1 つである色について説明しましょう。アイコンがスプライトに入るスクリプトを作成しましたが、このアイコンはプロジェクト全体で異なる色を持つことができます。
<svg/>
要素だけでなく、 path
、 circle
、 line
なども fill 属性やストローク属性を持つことができることに留意する必要があります。 currentcolorという非常に便利な CSS 機能が役に立ちます。
このキーワードは、要素の color プロパティの値を表します。たとえば、 background: currentcolor
である要素にcolor: red
を使用すると、この要素の背景は赤になります。
基本的に、すべてのストロークまたは塗りつぶしの属性値をcurrentcolor
に変更する必要があります。それが手動で行われているのを見ないことを祈ります、へー。また、SVG 文字列を置換または解析するコードを作成することさえ、非常に便利なツールsvgoに比べてあまり効率的ではありません。
これは、色だけでなく、SVG からの冗長な情報の削除にも役立つ SVG オプティマイザーです。
svgoをインストールしましょう。
npm i -D svgo
svgo
には組み込みプラグインがあり、そのうちの 1 つは、 currentColor: true
プロパティを持つconvertColors
です。この SVG 出力を使用すると、色がcurrentcolor
に置き換えられます。以下は、 svgo
とconvertColors
の使用法です。
import { optimize } from 'svgo'; const output = optimize( '<svg viewBox="0 0 24 24"><path fill="#000" d="m15 5 4 4" /></svg>', { plugins: [ { name: 'convertColors', params: { currentColor: true, }, } ], } ) console.log(output);
そして出力は次のようになります:
<svg viewBox="0 0 24 24"><path fill="currentColor" d="m15 5 4 4"/></svg>
前のパートで書いた魔法のスクリプトにsvgo
を追加しましょう。
// generateSvgSprite.ts import { globSync } from 'glob'; import fs from 'fs'; import { HTMLElement, parse } from 'node-html-parser'; import path from 'path'; import { Config as SVGOConfig, optimize } from 'svgo'; // import `optimize` function const svgoConfig: SVGOConfig = { plugins: [ { name: 'convertColors', params: { currentColor: true, }, } ], }; const svgFiles = globSync('src/icons/*.svg'); const symbols: string[] = []; svgFiles.forEach(file => { const code = fs.readFileSync(file, 'utf-8'); const result = optimize(code, svgoConfig).data; // here goes `svgo` magic with optimization const svgElement = parse(result).querySelector('svg') as HTMLElement; const symbolElement = parse('<symbol/>').querySelector('symbol') as HTMLElement; const fileName = path.basename(file, '.svg'); svgElement.childNodes.forEach(child => symbolElement.appendChild(child)); symbolElement.setAttribute('id', fileName); if (svgElement.attributes.viewBox) { symbolElement.setAttribute('viewBox', svgElement.attributes.viewBox); } symbols.push(symbolElement.toString()); }); const svgSprite = `<svg xmlns="http://www.w3.org/2000/svg">${symbols.join('')}</svg>`; fs.writeFileSync('public/sprite.svg', svgSprite);
そして、スクリプトを実行します。
npx tsx generateSvgSprite.ts
その結果、SVG スプライトにはcurrentColor
のアイコンが含まれることになります。これらのアイコンは、プロジェクト内のどこでも好きな色で使用できます。
プラグイン
スクリプトがあるので、いつでも実行できますが、手動で使用しなければならないのは少し不便です。したがって、外出先で.svg
ファイルを監視し、SVG スプライトを生成できるいくつかのプラグインをお勧めします。
vite-plugin-svg-spritemap ( viteユーザー向け)
これは、この記事で作成したばかりのスクリプトを基本的に含むプラグインです。プラグインでは
currentColor
置換がデフォルトで有効になっているため、プラグインを非常に簡単にセットアップできます。// vite.config.ts import svgSpritemap from 'vite-plugin-svg-spritemap'; export default defineConfig({ plugins: [ svgSpritemap({ pattern: 'src/icons/*.svg', filename: 'sprite.svg', }), ], });
svg-spritemap-webpack-plugin ( Webpackユーザー向け)
Vite に切り替えるまでは、この Webpack プラグインを使用していました。ただし、Webpack を使用している場合には、このプラグインは依然として優れたソリューションです。手動で色変換を有効にする必要があります。色変換は次のようになります。
// webpack.config.js const SVGSpritemapPlugin = require('svg-spritemap-webpack-plugin'); module.exports = { plugins: [ new SVGSpritemapPlugin('src/icons/*.svg', { output: { svgo: { plugins: [ { name: 'convertColors', params: { currentColor: true, }, }, ], }, filename: 'sprite.svg', }, }), ], }
レイアウトでの使用法
Reactの例を提供しますが、主に HTML に関するものであるため、必要な場所に実装できます。したがって、ビルド フォルダーにsprite.svg
があるので、スプライト ファイルにアクセスして、基本的なIcon
コンポーネントを作成できます。
const Icon: FC<{ name: string }> = ({ name }) => ( <svg> <use href={`/sprite.svg#${name}`} /> </svg> ); const App = () => { return <Icon name="pen" />; };
最終結果
すべてをまとめると、アイコンを使用した多くの手動作業を避けるために、次のようになります。
- プロジェクト内のアイコンを適切な名前で簡単に保存し、整理しておくことができます
- すべてのアイコンを別のファイル内の単一のスプライトに結合するスクリプトがあり、これによりバンドル サイズが削減され、プロジェクト内のどこでもこれらのアイコンを使用できるようになります
- アイコンを不要な属性から整理し、使用場所の色を変更するのに役立つ便利なツールがあります
- アイコン ファイルを監視し、ビルド プロセスの一部として外出先でスプライトを生成できるプラグインがある
- 一番上の追加要素である Icon コンポーネントがある
結論
開発の効率化とは、単に時間を節約することだけではありません。それは私たちの創造的な可能性を解き放つことなのです。アイコンの管理などの核心的なタスクを自動化することは、単なる近道ではありません。これは、よりスムーズでインパクトのあるコーディング エクスペリエンスへの入り口となります。そして、そのような日常的な作業にかかる時間を節約することで、より複雑なタスクに集中し、開発者としてより早く成長することができます。