Reputation: 1979
I switched from the Vue CLI to Vite CLI, and from the Composition API of Vue 3 to SFC Script setup API.
When I was using the official Vue CLI, I could import an image source by passing the filename of the path by the props :
<template>
<img :src="require(`@/assets/${imagePath}`)"/>
<template/>
<script>
export default {
props: {
imagePath: { type: String },
},
setup() {
// ...
}
}
<script/>
And then call it like this :
<template>
<Image imagePath="icon.png" />
</template>
But since I migrated to the Vite CLI, I have an error "Uncaught ReferenceError: require is not defined". My file now use the script setup syntax and looks like this :
<script setup>
const props = defineProps({
imagePath: { type: String },
})
</script>
<template>
<img :src="require(`@/assets/${props.imagePath}`)"/>
</template>
I already tried to import the file directly from the assets folder with a relative path, and it worked. But I cannot specify the path from props with the import statement.
<script setup>
// Works but do not use the props, so the component is not reusable
import logo from "./../assets/logo.png"
</script>
<template>
<img :src="logo"/>
</template>
<script setup>
// Component is reusable but the import statement has illegal argument I guess
const props = defineProps({
imagePath: { type: String },
})
import logo from `./../assets/${props.imagePath}`
</script>
<template>
<img :src="logo"/>
</template>
I also tried the import statement in the template but it cannot even compile the code :
<script setup>
const props = defineProps({
imagePath: { type: String },
})
</script>
<template>
<img :src="import `./../assets/${props.iconPath}`" />
</template>
Am I missing something ? Maybe a plugin exists and can help me achieve this ?
Upvotes: 50
Views: 79790
Reputation: 2044
Was able to make vite/webpack-agnostic solution with vite-plugin-transform by Silksofthesoul
vite.config.ts
:
import { defineConfig } from 'vite';
import vue from '@vitejs/plugin-vue';
import transformPlugin from 'vite-plugin-transform';
export default defineConfig({
plugins: [
vue(),
transformPlugin({
callbackArray: [str => str.replace(/__VITE_ONLY_ASSIGN__/gim, '')],
}),
],
define: {
'process.env': {},
},
});
In file with non-working require()
:
const __VITE_ONLY_ASSIGN__require = (relativePath: string) => {
return new URL(relativePath, import.meta.url).href;
};
const myImageUrl = require('./my-image.svg');
Upvotes: 0
Reputation: 81
I am currently struggeling with the same thing, with the added complication that the app will be deployed to k8s, where the ingress routing adds another level of absurdity for file paths.
I have tried all suggestions here, nothing worked well for both development and prod.
In the end, I simply kept the pictures I load dynamically in the public folder. Per Vue's own website, this folder is copied but does not go through webpack, so paths remain intact.
It's not super elegant, but it works incredibly well. This finally solved the headache I have been having for weeks over multiple web apps.
Upvotes: 1
Reputation: 11
Just had the same issue. I have a global function that gets the image path depending on product name. That's how I made it work.
Many thanks to @leipzy
export const getProductLogo = (cart) => {
if (cart) {
let im
try {
im = `../assets/img/cart/${cart}.png`
} catch (err) {
im = '../assets/img/default-logo.svg'
}
return new URL(`${im}`, import.meta.url).href
}
}
Upvotes: 1
Reputation: 59
for those who will use static assets with vue + vite, they can use this method.
import imgUrl from '../../img/bgimg.jpg'
export default {
data() {
return {
bgImage: imgUrl
};
},
};
then you can use it like this
<div class="ms-auto h-100 z-index-0 ms-n6"
:style="{
backgroundImage:
'url(' +
bgImage +
')',
}"
></div>
Importing a static asset will return the resolved public URL when it is served:
import imgUrl from './img.png'
document.getElementById('hero-img').src = imgUrl
For example, imgUrl will be /img.png
during development, and become /assets/img.2d8efhg.png
in the production build.
see also: https://vitejs.dev/guide/assets.html#importing-asset-as-url
Upvotes: 4
Reputation: 1
If none of provided answers is not help (as in my case) - try to use <slot/>
instead, as shown below:
//card component (child)
<template>
<div class="card">
<slot />
</div>
</template>
//services component (parent)
<template>
<ServiceCard>
<img src="@/assets/icons/service-1.svg" alt="svg image" />
</ServiceCard>
<ServiceCard>
<img src="@/assets/icons/service-2.svg" alt="svg image" />
</ServiceCard>
</template>
Upvotes: 0
Reputation: 5771
In case you're using require.context
, the new way seems to be glob import. Change your old statement from:
const locales = require.context("../../lang", true, /[A-Za-z0-9-_,\s]+\.json$/i)
to:
const locales = import.meta.glob('../../lang/*.json')
Edit:
This also seems to replace require
.
Upvotes: 14
Reputation: 12754
I also encountered this problem. I searched about this and found according to this github issue comment,
There should never be
require
in source code when using Vite. It's ESM only.
More about this on Features | Vite - Static Assets
A bit of searching lead me to this Vue 3 code sample link that worked for me
<CarouselItem v-for="(item,index) of carouselData" :key="index">
<img :src="getImageUrl(item.img_name)" />
</CarouselItem>
setup() {
const getImageUrl = (name) => {
return new URL(`../../lib/Carousel/assets/${name}`, import.meta.url).href
}
return { carouselData, getImageUrl }
}
Upvotes: 61
Reputation: 138226
Use a watcher on the imagePath
prop that dynamically imports the image, and updates a ref
(bound to the <img>.src
) with the result:
<script setup>
import { ref, watchEffect } from 'vue'
const props = defineProps({
imagePath: { type: String },
})
const logo = ref()
watchEffect(async () => {
logo.value = (await import(/* @vite-ignore */ `../assets/${props.imagePath}`)).default
})
</script>
<template>
<img :src="logo">
</template>
Upvotes: 1