Geliştiriciler genellikle SVG'yi doğrudan JSX'e ekler. Bunun kullanımı kolaydır ancak JS paket boyutunu artırır. Optimizasyon arayışı içinde, paketi karmaşıklaştırmadan SVG simgelerini kullanmanın başka bir yolunu bulmaya karar verdim. SVG sprite'larının ne olduğu, nasıl kullanılacağı ve onlarla çalışmak için hangi araçların mevcut olduğu hakkında konuşacağız.
Teoriden başlayarak, adım adım bir SVG sprite oluşturan bir komut dosyası yazacağız ve vite ve webpack için eklentileri tartışarak sonlandıracağız.
Bir görüntü grafiği, tek bir görüntüye yerleştirilen görüntülerin koleksiyonudur. SVG Sprite ise <symbol />
içine sarılmış ve <svg />
içine yerleştirilmiş bir SVG içeriği koleksiyonudur.
Örneğin basit bir SVG kalem simgemiz var:
Bir SVG hareketli grafiği elde etmek için <svg />
etiketini <symbol />
ile değiştireceğiz ve onu harici olarak <svg />
ile saracağız:
Artık bu bir SVG hareketli grafiği ve içinde id="icon-pen"
olan bir simgemiz var.
Tamam ama bu simgeyi HTML sayfamıza nasıl yerleştireceğimizi bulmalıyız. Simgenin kimliğini belirten <use />
etiketini href
özelliğiyle birlikte kullanacağız ve bu öğeyi SVG'nin içinde kopyalayacağız.
<use />
nasıl çalıştığına dair bir örneğe bakalım:
Bu örnekte iki daire var. Birincisi mavi bir çerçeveye sahip, ikincisi ise ilkinin kopyası ancak kırmızı dolgulu.
SVG sprite'ımıza geri dönelim. <use />
kullanımıyla birlikte şunu elde edeceğiz:
Burada kalem simgemizin bulunduğu bir düğmemiz var.
Şu ana kadar <button />
da kodu olmayan bir simge kullandık. Sayfada birden fazla düğmemiz varsa, bu durum HTML düzenimizin boyutunu birden fazla etkilemeyecektir çünkü tüm simgeler SVG sprite'ımızdan gelecektir ve yeniden kullanılabilir olacaktır.
index.html
dosyasını karmaşıklaştırmamak için SVG sprite'ımızı ayrı bir dosyaya taşıyalım. Öncelikle bir sprite.svg
dosyası oluşturun ve içine bir SVG sprite yerleştirin. Bir sonraki adım, <use/>
içindeki href
özelliğini kullanarak simgeye erişim sağlamaktır:
Simge kullanımında çok fazla zaman kazanmak için bu işlem için bir otomasyon ayarlayalım. Simgelere kolay erişim sağlamak ve onları istediğimiz gibi yönetmek için, her birinin kendi dosyasında ayrılması gerekir.
Öncelikle tüm simgeleri aynı klasöre koymalıyız, örneğin:
Şimdi bu dosyaları alan ve bunları tek bir SVG grafiğinde birleştiren bir komut dosyası yazalım.
Projenizin kök dizininde generateSvgSprite.ts
dosyasını oluşturun.
Glob kitaplığını yükleyin:
npm i -D glob
globSync
kullanarak her simge için bir dizi tam yol elde edin:
Şimdi, her dosya yolunu yineleyeceğiz ve Node'un yerleşik fs kitaplığını kullanarak dosya içeriğini alacağız:
Harika, her simgenin SVG koduna sahibiz ve artık bunları birleştirebiliriz, ancak her simgenin içindeki svg
etiketini symbol
etiketiyle değiştirmeli ve gereksiz SVG niteliklerini kaldırmalıyız.
DOM gösterimini elde etmek için SVG kodumuzu bazı HTML ayrıştırıcı kütüphaneleriyle ayrıştırmalıyız. node-html-parser'ı kullanacağım:
SVG kodunu ayrıştırdık ve SVG öğesini gerçek bir HTML öğesiymiş gibi elde ettik.
Aynı ayrıştırıcıyı kullanarak, svgElement
öğesinin alt öğelerini symbol
öğesine taşımak için boş bir symbol
öğesi oluşturun:
Çocukları svgElement
çıkardıktan sonra, id
ve viewBox
özelliklerini de almalıyız. id
olarak ikon dosyasının ismini belirleyelim.
Artık bir SVG hareketli grafiğine yerleştirilebilecek bir symbol
öğemiz var. Bu nedenle, dosyaları yinelemeden önce symbols
değişkenini tanımlayın, symbolElement
öğesini bir dizeye dönüştürün ve symbols
aktarın:
Son adım, SVG grafiğinin kendisini oluşturmaktır. “Kök”te svg
ve alt öğeler olarak semboller bulunan bir dizeyi temsil eder:
const svgSprite = `<svg>${symbols.join('')}</svg>`;
Aşağıda bahsedeceğim eklentileri kullanmayı düşünmüyorsanız, oluşturulan hareketli grafiğin bulunduğu dosyayı statik bir klasöre koymanız gerekir. Paketleyicilerin çoğu public
klasör kullanır:
fs.writeFileSync('public/sprite.svg', svgSprite);
Ve işte bu; komut dosyası kullanıma hazır:
// 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);
Bu betiği projenizin kök dizinine yerleştirebilir ve tsx ile çalıştırabilirsiniz:
npx tsx generateSvgSprite.ts
Aslında burada tsx kullanıyorum çünkü eskiden her yerde TypeScript'te kod yazıyordum ve bu kütüphane TypeScript'te yazılmış düğüm komut dosyalarını çalıştırmanıza izin veriyor. Saf JavaScript kullanmak istiyorsanız şunu çalıştırabilirsiniz:
node generateSvgSprite.js
Şimdi betiğin ne yaptığını özetleyelim:
.svg
dosyası için src/icons
klasörüne bakar.
<svg />.
public
klasörde sprite.svg
dosyası oluşturur.Sık karşılaşılan ve önemli bir durumu ele alalım: renkler! Simgenin bir hareketli grafik haline geldiği bir komut dosyası oluşturduk ancak bu simge, proje boyunca farklı renklere sahip olabilir.
Yalnızca <svg/>
öğelerinin dolgu veya kontur niteliklerine sahip olabileceğini değil aynı zamanda path
, circle
, line
ve diğer öğelerin de olabileceğini aklımızda tutmalıyız. Bize yardımcı olacak çok kullanışlı bir CSS özelliği var - currentcolor .
Bu anahtar kelime, bir öğenin renk özelliğinin değerini temsil eder. Örneğin, background: currentcolor
olan bir öğe üzerinde color: red
rengini kullanırsak, bu öğenin arka planı kırmızı olur.
Temel olarak, her kontur veya dolgu öznitelik değerini currentcolor
olarak değiştirmemiz gerekir. Umarım bunun manuel olarak yapıldığını görmüyorsunuzdur, heh. Hatta SVG dizelerini değiştirecek veya ayrıştıracak bazı kodlar yazmak bile çok kullanışlı bir araç olan svgo ile karşılaştırıldığında çok verimli değildir.
Bu, yalnızca renkler konusunda değil aynı zamanda SVG'deki gereksiz bilgilerin kaldırılmasında da yardımcı olabilecek bir SVG optimize edicidir.
Svgo'yu yükleyelim:
npm i -D svgo
svgo
yerleşik eklentileri vardır ve bunlardan biri, currentColor: true
özelliğine sahip olan convertColors
. Bu SVG çıktısını kullanırsak renkleri currentcolor
ile değiştirecektir. convertColors
ile birlikte svgo
kullanımı şöyledir:
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);
Ve çıktı şöyle olacaktır:
<svg viewBox="0 0 24 24"><path fill="currentColor" d="m15 5 4 4"/></svg>
Bir önceki bölümde yazdığımız sihirli scriptimize svgo
ekleyelim:
// 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);
Ve betiği çalıştırın:
npx tsx generateSvgSprite.ts
Sonuç olarak SVG hareketli grafiği, currentColor
özelliğine sahip simgeler içerecektir. Ve bu simgeler projenin her yerinde istediğiniz renkle kullanılabilmektedir.
Elimizde bir script var ve onu istediğimiz zaman çalıştırabiliyoruz ama manuel olarak kullanmamız biraz sakıncalı. Bu nedenle, .svg
dosyalarımızı izleyebilecek ve hareket halindeyken SVG sprite'ları oluşturabilecek birkaç eklenti öneririm:
vite-plugin-svg-spritemap ( vite kullanıcıları için)
Bu, temelde bu makalede az önce oluşturduğumuz betiği içeren eklentim. Eklentinin currentColor
değişimi varsayılan olarak etkindir, böylece eklentiyi oldukça kolay bir şekilde kurabilirsiniz.
// 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-eklentisi ( webpack kullanıcıları için)
Vite'a geçene kadar bu Webpack eklentisini kullandım. Ancak Webpack kullanıyorsanız bu eklenti hala iyi bir çözümdür. Renk dönüşümünü manuel olarak etkinleştirmelisiniz; şu şekilde görünecektir:
// 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'ta bir örnek vereceğim ama çoğunlukla HTML ile ilgili olduğu için istediğiniz yere uygulayabilirsiniz. Dolayısıyla, derleme klasörümüzde sprite.svg
bulunduğundan, hareketli resim dosyasına erişebilir ve temel Icon
bileşenini oluşturabiliriz:
const Icon: FC<{ name: string }> = ({ name }) => ( <svg> <use href={`/sprite.svg#${name}`} /> </svg> ); const App = () => { return <Icon name="pen" />; };
Dolayısıyla, simgelerle yapılan birçok manuel çalışmayı önlemek için her şeyi özetleyerek şunları yapıyoruz:
Geliştirmede verimlilik yalnızca zamandan tasarruf etmekle ilgili değildir; bu, yaratıcı potansiyelimizin kilidini açmakla ilgilidir. Simgeleri yönetmek gibi hassas görevleri otomatikleştirmek yalnızca bir kısayol değildir; daha sorunsuz, daha etkili bir kodlama deneyimine açılan bir kapıdır. Bu tür rutin işlerde zaman kazanarak daha karmaşık görevlere odaklanabilir ve bir geliştirici olarak daha hızlı büyüyebilirsiniz.