Our goal is to do this:
<icon src=”plus.svg” />
So it rendered in DOM as
<span class=”icon”><svg>….</svg></span>
First we create a
icon.vue
component:
<template>
<span class="icon"></span>
</template>
<script>
export default {
props: {
src: { type: String, required: true }
}
};
</script>
We will load the svg at
mounted
, where will can just fetch the icon. With the
async/await
syntax it is quite simple.
async mounted(){
let svg = await fetch(this.src).then(r => r.text());
this.$el.innerHTML = svg;
}
But when the component is used multiple times, it will generate multiple request to the same file.
<div v-for="item in list">
{{item.desp}}
<button @click="add(item)">
<icon src="plus.svg"/>
</button>
</div>
<!-- it will make as many requests as
the list length to plus.svg! -->
So we need to implement some cache. We can do it through
Map
let cache = new Map();
async mounted(){
if(!cache.has(this.src)){
cache.set(this.src, await fetch(this.src).then(r => t.text()))
}
this.$el.innerHTML = cache.get(this.src);
}
Are we good? No. We found that at the first load, it still make multiple requests. The problem is that the
cache
is not yet populated so subsequent request will still be made. We should store the
Promise
in cache and let all requests of same
src
refer to it.
async mounted(){
if(!cache.has(this.src)){
cache.set(this.src, fetch(this.src).then(r => t.text()))
}
this.$el.innerHTML = await cache.get(this.src);
}
We add some error handling:
async mounted(){
if(!cache.has(this.src)){
try{
cache.set(this.src, fetch(this.src).then(r => t.text()));
} catch (e) {
cache.delete(this.src);
}
}
if(cache.has(this.src)){
this.$el.innerHTML = await cache.get(this.src);
}
}
The full component code:
<template>
<span class="icon"></span>
</template>
<script>
let cache = new Map();
export default {
props: {
src: { type: String, required: true }
},
async mounted() {
if (!cache.has(this.src)) {
try {
cache.set(this.src, fetch(this.src).then(r => r.text()));
} catch (e) {
cache.delete(this.src);
}
}
if (cache.has(this.src)) {
this.$el.innerHTML = await cache.get(this.src);
}
}
};
</script>
Remember to also pack your svg files in your webpack config.