How to inject domain from environment variable into vuejs with require - vue.js

I have a special use case where I need the full url to an image to render on the html side. Ex; Facebook Open Graph requires the full image url to work properly, relative image or absolute path won't work.
I'm currently working in with #vue/cli and typescript. I have the following component:
example.vue
<template>
<div id="example">
<img :src="exampleIcon" alt="Example"/>
</div>
</template>
<script lang="ts">
export default {
data() {
return {
exampleIcon: require(`../assets/exampleIcon.png`),
};
},
};
</script>
The above renders fine, it produces an img tag that results in this:
<img src="/img/exampleIcon.8d0b1a90.png" alt="Example">
but let's say my domain is example.com, here's what I would like to have as a result instead:
<img src="https://example.com/img/exampleIcon.8d0b1a90.png" alt="Example">
I don't want to hardcode anything in the template. I would like to use an environment variable so I can inject the domain when building the vue bundle:
export default {
data() {
return {
baseUrl: process.env.BASE_URL,
};
},
};
Some related documentation:
vue-loader asset-url
file-loader

You're pretty close.
Instead of getting a value of baseUrl from an environmental variable, set the value based on the environment you build the current app for:
data() {
return {
baseUrl: process.env.NODE_ENV == 'production'? "https://example.com" : 'http://localhost:8081'
}
I guess you have different npm scripts for production environment and development environment. In webpack 4 you can use use mode.
Read more about environments.

Related

Vue3 Component doesn't render in production when using v-bind:href require()

Everything works fine when developing but once I export for production 1 component doesn't render and instead gets replaced by <!--->
After some debugging, I discovered that this happens because of require()
I have images with dynamic URLs that look like this:
:src="require(`#/assets/path/${variable}`)"
This works in dev but once I build the app for production the component doesn't render.
When I replace the path with
src="#/assets/path/file.png"
The component shows up and works properly
Is it something that I can provide to webpack in vue.config.js?
Is there a way to use variables in path without require()?
The expression inside v-bind is executed at runtime, webpack aliases at compile time.
Move require() from html template to data() and it should work in production.
Simple example:
<template>
<img :src="getImg" />
</template>
<script>
export default {
name: 'Example',
data() {
return {
file: 'image',
}
},
methods: {
getImg() {
return require(`#/assets/images/${this.file}.png`)
},
},
}
</script>

Vue 3 Vite - dynamic image src

I'm using Vue 3 with Vite. And I have a problem with dynamic img src after Vite build for production. For static img src there's no problem.
<img src="/src/assets/images/my-image.png" alt="Image" class="logo"/>
It works well in both cases: when running in dev mode and after vite build as well. But I have some image names stored in database loaded dynamically (Menu icons). In that case I have to compose the path like this:
<img :src="'/src/assets/images/' + menuItem.iconSource" />
(menuItem.iconSource contains the name of the image like "my-image.png").
In this case it works when running the app in development mode, but not after production build. When Vite builds the app for the production the paths are changed (all assests are put into _assets folder). Static image sources are processed by Vite build and the paths are changed accordingly but it's not the case for the composed image sources. It simply takes /src/assets/images/ as a constant and doesn't change it (I can see it in network monitor when app throws 404 not found for image /src/assets/images/my-image.png).
I tried to find the solution, someone suggests using require() but I'm not sure vite can make use of it.
Update 2022: Vite 3.0.9 + Vue 3.2.38
Solutions for dynamic src binding:
1. With static URL
<script setup>
import imageUrl from '#/assets/images/logo.svg' // => or relative path
</script>
<template>
<img :src="imageUrl" alt="img" />
</template>
2. With dynamic URL & relative path
<script setup>
const imageUrl = new URL(`./dir/${name}.png`, import.meta.url).href
</script>
<template>
<img :src="imageUrl" alt="img" />
</template>
3.With dynamic URL & absolute path
Due to Rollup Limitations, all imports must start relative to the importing file and should not start with a variable.
You have to replace the alias #/ with /src
<script setup>
const imageUrl = new URL('/src/assets/images/logo.svg', import.meta.url)
</script>
<template>
<img :src="imageUrl" alt="img" />
</template>
2022 answer: Vite 2.8.6 + Vue 3.2.31
Here is what worked for me for local and production build:
<script setup>
const imageUrl = new URL('./logo.png', import.meta.url).href
</script>
<template>
<img :src="imageUrl" />
</template>
Note that it doesn't work with SSR
Vite docs: new URL
Following the Vite documentation you can use the solution mentioned and explained here:
vite documentation
const imgUrl = new URL('./img.png', import.meta.url)
document.getElementById('hero-img').src = imgUrl
I'm using it in a computed property setting the paths dynamically like:
var imagePath = computed(() => {
switch (condition.value) {
case 1:
const imgUrl = new URL('../assets/1.jpg',
import.meta.url)
return imgUrl
break;
case 2:
const imgUrl2 = new URL('../assets/2.jpg',
import.meta.url)
return imgUrl2
break;
case 3:
const imgUrl3 = new URL('../assets/3.jpg',
import.meta.url)
return imgUrl3
break;
}
});
Works perfectly for me.
The simplest solution I've found for this is to put your images in the public folder located in your directory's root.
You can, for example, create an images folder inside the public folder, and then bind your images dynamically like this:
<template>
<img src:="`/images/${ dynamicImageName }.jpeg`"/>
</template>
Now your images should load correctly in both development and production.
Please try the following methods
const getSrc = (name) => {
const path = `/static/icon/${name}.svg`;
const modules = import.meta.globEager("/static/icon/*.svg");
return modules[path].default;
};
In the context of vite#2.x, you can use new URL(url, import.meta.url) to construct dynamic paths. This pattern also supports dynamic URLs via template literals.
For example:
<img :src="`/src/assets/images/${menuItem.iconSource}`" />
However you need to make sure your build.target support import.meta.url. According to Vite documentation, import.meta is a es2020 feature but vite#2.x use es2019 as default target. You need to set esbuild target in your vite.config.js:
// vite.config.js
export default defineConfig({
// ...other configs
optimizeDeps: {
esbuildOptions: {
target: 'es2020'
}
},
build: {
target: 'es2020'
}
})
All you need is to just create a function which allows you to generate a url.
from vite documentation static asset handling
const getImgUrl = (imageNameWithExtension)=> new URL(`./assets/${imageNameWithExtension}`, import.meta.url).href;
//use
<img :src="getImgUrl(image)" alt="...">
Use Vite's API import.meta.glob works well, I refer to steps from docs of webpack-to-vite. It lists some conversion items and error repair methods. It can even convert an old project to a vite project with one click. It’s great, I recommend it!
create a Model to save the imported modules, use async methods to dynamically import the modules and update them to the Model
// src/store/index.js
import Vue from 'vue'
import Vuex from 'vuex'
const assets = import.meta.glob('../assets/**')
Vue.use(Vuex)
export default new Vuex.Store({
state: {
assets: {}
},
mutations: {
setAssets(state, data) {
state.assets = Object.assign({}, state.assets, data)
}
},
actions: {
async getAssets({ commit }, url) {
const getAsset = assets[url]
if (!getAsset) {
commit('setAssets', { [url]: ''})
} else {
const asset = await getAsset()
commit('setAssets', { [url]: asset.default })
}
}
}
})
use in .vue SFC
// img1.vue
<template>
<img :src="$store.state.assets['../assets/images/' + options.src]" />
</template>
<script>
export default {
name: "img1",
props: {
options: Object
},
watch: {
'options.src': {
handler (val) {
this.$store.dispatch('getAssets', `../assets/images/${val}`)
},
immediate: true,
deep: true
}
}
}
</script>
My enviroment:
vite v2.9.13
vue3 v3.2.37
In vite.config.js, assign #assets to src/assets
'#assets': resolve(__dirname, 'src/assets')
Example codes:
<template>
<div class="hstack gap-3 mx-auto">
<div class="form-check border" v-for="p in options" :key="p">
<div class="vstack gap-1">
<input class="form-check-input" type="radio" name="example" v-model="selected">
<img :src="imgUrl(p)" width="53" height="53" alt="">
</div>
</div>
</div>
</template>
<script>
import s1_0 from "#assets/pic1_sel.png";
import s1_1 from "#assets/pic1_normal.png";
import s2_0 from "#assets/pic2_sel.png";
import s2_1 from "#assets/pic2_normal.png";
import s3_0 from "#assets/pic3_sel.png";
import s3_1 from "#assets/pic3_normal.png";
export default {
props: {
'options': {
type: Object,
default: [1, 2, 3, 4]
}
},
data() {
return {
selected: null
}
},
methods: {
isSelected(val) {
return val === this.selected;
},
imgUrl(val) {
let isSel = this.isSelected(val);
switch(val) {
case 1:
case 2:
return (isSel ? s1_0 : s1_1);
case 3:
case 4:
return (isSel ? s2_0 : s2_1);
default:
return (isSel ? s3_0 : s3_1);
}
}
}
}
</script>
References:
Static Asset Handling of Vue3
Memo:
About require solution.
"Cannot find require variable" error from browser. So the answer with require not working for me.
It seems nodejs >= 14 no longer has require by default. See this thread. I tried the method, but my Vue3 + vite give me errors.
In Nuxt3 I made a composable that is able to be called upon to import dynamic images across my app. I expect you can use this code within a Vue component and get the desired effect.
const pngFiles = import.meta.glob('~/assets/**/*.png', {
//#ts-ignore
eager: true,
import: 'default',
})
export const usePNG = (path: string): string => {
// #ts-expect-error: wrong type info
return pngFiles['/assets/' + path + '.png']
}
sources
If you have a limited number of images to use, you could import all of them like this into your component. You could then switch them based on a prop to the component.

How to link to a html page in vue.js?

How do i link/redirect to a html page that has it own cdn in vue.js
These html page are some old project that i made in the past that i want to link to.
I have only install the webpack-simple and vue-router.
<div id="navMenu">
<ul>
<li class="project">
<a :href="publicPath + 'project/projectOne/drone.html'">Drone</a>
</li>
</ul>
</div>
The script
<script>
export default {
data () {
return {
publicPath: process.env.BASE_URL
}
},
methods:{
}
}
</script>
Assuming these are static files, separate from your Vue project, that you don't need to run through webpack, and that you're using vue-cli for scaffolding:
Put the static files inside the public directory (at the root level of your project). Anything you put in there will be copied directly into the dist folder at build -- so public/foo.html would wind up at the root level inside dist; /public/project/projectOne/drone.html would wind up in /dist/project/projectOne/drone.html, etc.
Link to those files from within your Vue project as you would any normal external site or file (using the project BASE_URL if necessary):
<!-- assuming the source file is in /public/project/projectOne/drone.html -->
<a :href="publicPath + 'project/projectOne/drone.html'">Drone</a>
export default {
data () {
return {
publicPath: process.env.BASE_URL
}
}
// ...

Can't load images from relative path with Vue V-Lazy-Image Plugin

I'm using this plugin to create a portfolio gallery on a single page components application but I'm stuck loading the images from a relative path, it works fine if I use a full URL path.
When I use the full URL the image Loads and loose the blur as expected, instead when I use a relative path the image load but it keeps blurred and it doesn't load the class .v-lazy-image-loaded into the image tag. Any Ideas? here is the code.
LazyImage.vue
<template>
<v-lazy-image :src="src" />
</template>
<script>
import VLazyImage from 'v-lazy-image'
export default {
props: {
// HERE I TRIED TO USE A FUNCTION ALSO WITH NO LUCK
// src: Function
src: String
},
components: {
VLazyImage
},
// UPDATE: I added a method to test here.
methods: {
consoleSuccess (msg) {
console.log(msg)
}
}
}
</script>
Portfolio.vue
<template>
<section id="portfolio">
// I'M TRYING THIS TWO OPTIONS
// UPDATED: I Added here the events calls and its just calling the intersect but the load.
<v-lazy-image src="../assets/img/innovation.jpg" alt="alternate text" #load="consoleSuccess('LOADED!')" #intersect="consoleSuccess('INTERSECS!')"></v-lazy-image> <-- this just work with full url ex. http://cdn...
<v-lazy-image :src="require('../assets/img/innovation.jpg')" alt="alternate text"></v-lazy-image> <-- this loads the image but remains blured
</section>
</template>
<script>
import VLazyImage from '#/components/lazyImage'
export default {
components: {
VLazyImage
}
}
</script>
UPDATE
I added a method to test and realized that just the #intersect event it's called and the #load won't fire at all with relative paths.
I noticed this was caused by bug in the component code:
if (this.$el.src === this.src) {
should be replaced by
if (this.$el.getAttribute('src') === this.src) {
I've sent pull request for this.

How to get environment variable from Quasar Framework

The environment variables are defined in config/ directory prod.env.js and dev.env.js, how to get those variable on .vue file?
I've tried using process.env.MY_VAR assuming it's nodejs built-in library, it gives an error:
[======= ] 34% (building modules){ SyntaxError: Unexpected token (1:5)
The minimal code in .vue file:
<template>
<q-layout>
<div class="layout-view">
<button class="primary" #click="foo">
<i class="on-left">lock</i> Login
</button>
</div>
</q-layout>
</template>
<script>
export default {
data() {
return { url: '' }
}
methods: {
foo: function() {
this.url = process.env.MY_VAR // no error if commented
}
}
}
</script>
What's the correct way to get those environment variable?
In dev.env.js and prod.env.js you write something like:
module.exports = merge(prodEnv, {
NODE_ENV: '"development"',
MY_VAR: '"something"'
})
Then you can use process.env.MY_VAR in your Quasar app code.
Notice the quote + double quote. The build process in Quasar uses Webpack's DefinePlugin (https://webpack.js.org/plugins/define-plugin/) which requires a JSON encoded value. Use JSON.stringify() for more complex values like JS Objects.