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
}
}
// ...
Related
I created a blog in Nuxt.js which uses Markdown for my articles. When writing my first article, I realized I can't include images in my markdown article from my assets folder. It only works if it's a link like the example below:
Markdown Image:
How can insert an image in Nuxt.js Markdown from this location? assets/images/blog/trees.png
In your vue files, you can access images in assets folder with:
<template>
<img src="~/assets/your_image.png" />
</template>
In markdown file, you can do the same with Markdown syntax:
![image alt text](~/assets/your_image.png)
But as your files in content folder is independent of webpack, you have to run nuxt generate each time you add a file in assets folder.
More info here:
https://nuxtjs.org/docs/2.x/directory-structure/assets/
https://github.com/nuxt/content/issues/106
place your images in static folder with this path :
static/images/img1.png
In markdown file, use this syntax:
![image alt text](/images/img1.png)
In vue files, use like this :
<template>
<img src="/images/img1.png" />
</template>
or for use with content :
1- in content/articles/new-post.md :
---
title: Title
description: This is description
img: /images/img1.png
alt: Article 1
---
## Example
2- in vue file (pages/index.vue) :
<template>
<div>
<div v-for="article of articles" :key="article.slug">
<h1>{{ article.title }}</h1>
<img
v-if="article.img"
class="h-48 xxlmin:w-1/2 xxlmax:w-full object-cover"
:src="article.img"
/>
</div>
</div>
</template>
<script>
export default {
async asyncData({ $content, params }) {
const articles = await $content('articles', params.slug)
.only(['title', 'description', 'img'])
.fetch()
return {
articles,
}
},
}
</script>
notes :
place your images in static folder for example (static/images/*)
use '/' in start of addresses
true => /images/img1.png
false => images/img1.png
Looks like relative paths are not currently available: https://github.com/nuxt/content/issues/693#issuecomment-750412810
Only absolute paths should work after placing the image in the /static directory
![alt text](/images/blog/trees.png)
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.
I am trying to use a simple polygon cropper from Vue within an application by following the steps in this article.
I created my app using:
vue init webpack myproject
Now, I need to add the sample template to my app (it has a src folder), but I am not sure how to amend or add this piece of code to my application. The template should be as follows per the linked article:
// Global
import Vue from 'vue';
import VuePolygonCropper from 'vue-polygon-cropper';
Vue.component(VuePolygonCropper);
// Local
import VueCropper from 'vue-polygon-cropper';
export default {
components: { VueCropper}
}
<template>
<div id="app">
<polygon-crop :imageSource="'/demo.png'" ref="canvas"></polygon-crop>
<button #click.prevent="crop">Crop</button>
<button #click.prevent="undo">Undo</button>
<button #click.prevent="redo">Redo</button>
<button #click.prevent="reset">Reset</button>
</div>
</template>
<script>
export default {
name: 'App',
methods: {
crop: function () {
this.$refs.canvas.crop();
},
undo: function () {
this.$refs.canvas.undo();
},
redo: function () {
this.$refs.canvas.redo();
},
reset: function () {
this.$refs.canvas.reset();
}
}
};
</script>
I am not sure what the meaning of global or local is there. My src folder structure is as follows:
Directory of C:\ThermoAnalyser\vue_js\myproject\src
27/12/2020 11:37 AM <DIR> .
27/12/2020 11:37 AM <DIR> ..
27/12/2020 11:37 AM 374 App.vue
27/12/2020 11:37 AM <DIR> assets
27/12/2020 11:37 AM <DIR> components
27/12/2020 11:37 AM 360 main.js
27/12/2020 11:37 AM <DIR> router
2 File(s) 734 bytes
5 Dir(s) 301,183,393,792 bytes free
You won't be able to get this component running with just this code snippet, there's a couple of things that you would need to do to fix this up.
Before we go any deeper, I would like you to make sure if you have installed this vue-polygon-cropper component. If you navigated to the package.json that is located in the same level as your "src" folder, you would see a mention of vue-polygon-cropper there, if not please install it by npm install vue-polygon-croper .
Let's take a look at your <template> section first:
1- In the template, you call a component <polygon-crop> but, there is no component registered by that name in your script (What you are attempting to register is 'VuePolygonCropper' so you should try using <VuePolygonCropper> component instead.
2-I see there you copied and pasted the logo image in assets, that's a great way to test it! However, Digging through the creator's example that they put up on github, It seems like this component requires a full path to your image file instead of the relative path. so instead of /src/assets/logo.png try doing :imageSource="require('../assets/logo.png')"
I'm assuming the assets logo is on a folder that is one level above your current component.
So your template should look like this:
<template>
<div id="app">
<VuePolygonCropper :imageSource = "require('../assets/logo.png')"
ref="canvas"> </VuePolygonCropper>
<button #click.prevent="crop"> Crop </button>
<button #click.prevent="undo"> Undo </button>
<button #click.prevent="redo"> Redo </button>
<button #click.prevent="reset"> Reset </button>
</div>
</template>
Now on to your script!
just import the VuePolygonCropper and mention it as a component in the components section.
You don't need to import vue and do Vue.component(VuePolygonCropper). The correct way to register this component would be like this
<script>
import VuePolygonCropper from 'vue-polygon-cropper';
export
default
{
name: 'App',
components:{VuePolygonCropper},
methods: {
crop: function() {
this.$refs.canvas.crop();
},
undo: function()
{
this.$refs.canvas.undo();
},
redo: function()
{
this.$refs.canvas.redo();
},
reset: function()
{
this.$refs.canvas.reset();
}
}
};
</script>
For the heck of it, I have created a codesandbox that you can play around with . You can try to play around with the App.vue file and see how it was created.
Happy coding!
"Global" vs "Local"
The "global" and "local" comments refer to global component registration and local component registration. The article shows both ways of registering the vue-polygon-cropper component probably to make it easier to copy-paste into your own code.
Global component registration
You can register a component globally so that it could be used in another component without the consuming component having to register it locally. This is normally used for commonly used components that are frequently found in several components (e.g., a button).
Below is an example of global component registration that allows MyButton to be used in MyForm. Notice how MyForm's <template> uses MyButton without any component registration for MyButton in its <script>.
// main.js
import Vue from 'vue'
Vue.component('MyButton', { /*...*/ })
// MyForm.vue
<template>
<MyButton #click="onClick" />
</template>
<script>
export default {
methods: {
onClick() { /*...*/ }
}
}
</script>
Local component registration
For seldom used components (ones that are only found in a few components of your app), register the components locally instead to help minimize the bundle size if needed. If your component is never used (e.g., from a refactoring down the road), local registration allows the bundler to crop out your component from the final output.
Here's the previous example with local registration instead:
// MyButton.vue
<template>
<button />
</template>
// MyForm.vue
<template>
<MyButton #click="onClick" />
</template>
<script>
import MyButton from './MyButton.vue'
export default {
components: {
MyButton 👈
},
methods: {
onClick() { /*...*/ }
}
}
</script>
Getting started with the sample code
To quickly get the sample code working in your project:
Copy the <template> and <script> parts of the sample code into your src/App.vue, replacing everything.
In the App.vue's component definition, locally register vue-polygon-cropper as polygon-crop:
<script>
import VuePolygonCropper from 'vue-polygon-cropper'
export default {
components: {
'polygon-crop': VuePolygonCropper
}
}
<script>
The sample code refers to an image at /demo.png, but your sample app only has src/assets/logo.png, so edit src/App.vue's <template> so that polygon-crop uses src/assets/logo.png. We have to require the asset's path so that Webpack properly resolves the path from source:
<polygon-crop :imageSource="require('#/assets/logo.png')">
sample GitHub repo
Update to Vue CLI
Consider using Vue CLI's default generated templates (from vue create) instead of that outdated webpack template. The newly created project would still use Webpack, but most of the config for developer ergonomics are abstracted away, which can be helpful for beginners.
vue create myproject
I'm currently using Vue and Vue Router CDN. I want to import a single file component (user.html) to my index.html with Vue router. But when I click on "Go to user" the data didn't display. I read a few guides about Vue router but they used NPM or CIL instead of Vue CDN.
Index.html
<div id="app">
<h1>Hello App!</h1>
<p>
<!-- use router-link component for navigation. -->
<!-- specify the link by passing the `to` prop. -->
<!-- `<router-link>` will be rendered as an `<a>` tag by default -->
<router-link to="/foo">Go to Foo</router-link>
<router-link to="/bar">Go to Bar</router-link>
<router-link to='/User.html'>Go to User</router-link>
</p>
<!-- route outlet -->
<!-- component matched by the route will render here -->
<router-view></router-view>
</div>
<script>
const Foo = { template: '<div>foo</div>' }
const Bar = { template: '<div>bar</div>' }
const User = { template: '#test'}
const routes = [
{ path: '/foo', component: Foo },
{ path: '/bar', component: Bar },
{
path:'/User.html', component: User
}
]
const router = new VueRouter({
routes
})
const app = new Vue({
router
}).$mount('#app')
</script>
User.html
<template id = "test">
fsjdfjdfldskjflkd
</template>
I think there's been a misunderstanding. As far as I can tell you're trying to load User.html in the way you would load a .vue file, but that doesn't work unless you're using Webpack to build your project since the .vue filetype is something that the vue-loader project is responsible for parsing.
Vue Router CDN means you're not building with Webpack, so loading a component like you're doing is impossible. You can configure Vue Router to serve a User.html file, but that file can't be a template component since in CDN mode Vue Router has no idea what files are on your server, nor can it simply retreive them.
So you need to do one of the following three options:
Option 1: Start using Webpack for your project
This is what I recommend you doing. You'll find that your project will perform better and will be easier to develop using it.
Option 2: Use the proper template syntax for CDN
This is for example:
var mytemplate = `<div>
<h1>This is my template</h1>
</div>`
Vue.component('mycomp1', {
template: mytemplate
});
Vue.component('mycomp2', {
template: `
<div>
Hello, {{ name }}!
</div>
`,
props: ['name'],
});
You can't load other files as templates when you're not building using Webpack. Webpack puts them into your single page application for you, Vue Router has no idea what's inside User.html nor can Vue use it as a template. Vue Router can be told to redirect to a fully functional User.html website page, but not to just use it as a template.
Option 3: Use Ajax requests to fetch the template file
This is something I STRONGLY URGE YOU NOT TO DO, but for the sake of completeness, you can use the CDN version if you fetch the contents of User.html file using an Ajax request and create a component from that.
I really, really recommend that you stop using the CDN version and instead go for a Webpack based solution, give in to the dark side! Or make it even simpler and use Nuxt.js instead since it's easier for beginners to use.
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.