v-img can't locate resource from assets folder - vue.js

I am having problems displaying a static image located at src/assets/images/logo.png folder with the v-img Vuetify component.
<v-img src="#/assets/images/rima_logo.png"></v-img>
It doesn't load with Vuetify, but using a plain img tag it does find the resource. Also vscode provides completion for the image relative path so I don't know why vuetify isn't loading it correctly.

You code looks no problem, on condition that you have configured the path alias in your vue.config.js:
module.exports = {
...
chainWebpack: (config) => {
config.resolve.alias
.set('assets', resolve('src/assets'));
},
};
There some other solutions which should also works:
use required
<v-img :src="require(#/assets/images/rima_logo.png)"></v-img>
Import image as a resource:
// template
<v-img :src="rimaLogo"></v-img>
// scripts
import rimaLogo from 'assets/images/rima_logo.png';

It works on <img> due to the vue compiler feature transformAssetUrls. Vuetify's vite plugin has a preset for this to support vuetify components:
// vite.config.js
import vuetify, { transformAssetUrls } from 'vite-plugin-vuetify'
export default {
plugins: [
vue({
template: { transformAssetUrls }
}),
vuetify(),
],
}
https://github.com/vuetifyjs/vuetify-loader/tree/next/packages/vite-plugin#image-loading

Related

Vite vuetify plugin doesn't load components listed in external libraries

I am creating a library that wraps Vuetify 3 components. But when I try to use the library it gives the following error:
[Vue warn]: Failed to resolve component: v-btn If this is a native custom element, make sure to exclude it from component resolution via compilerOptions.isCustomElement.
Library vite.config.ts :
import { fileURLToPath, URL } from 'node:url';
import { resolve } from 'node:path';
import { defineConfig } from 'vite';
import vue from '#vitejs/plugin-vue';
import vueJsx from '#vitejs/plugin-vue-jsx';
import vuetify from 'vite-plugin-vuetify';
export default defineConfig({
plugins: [
vue(),
vueJsx(),
// vuetify({ autoImport: true, styles: 'none' }), // Don't export vuetify
],
resolve: {
alias: {
'#': fileURLToPath(new URL('./src', import.meta.url)),
},
},
build: {
lib: {
entry: resolve(__dirname, 'src/main.ts'),
name: '#my/ui',
// the proper extensions will be added
fileName: 'my-ui',
},
rollupOptions: {
// make sure to externalize deps that shouldn't be bundled
// into your library
external: ['vue', 'vuetify'],
output: {
// Provide global variables to use in the UMD build
// for externalized deps
globals: {
vue: 'Vue',
vuetify: 'Vuetify',
},
},
},
},
});
Nuxt project nuxt.config.ts:
import { defineNuxtConfig } from 'nuxt';
import vuetify from 'vite-plugin-vuetify';
export default defineNuxtConfig({
css: ['#/assets/css/main.css'],
modules: [
async (options, nuxt) => {
nuxt.hooks.hook('vite:extendConfig', (config) =>
config.plugins.push(vuetify({ autoImport: true }))
);
},
],
build: {
transpile: ['#my/ui', 'vuetify'],
},
});
Nuxt project app.vue:
<template>
<v-app>
<v-main>
<HelloWorld label="Test" primary />
</v-main>
</v-app>
</template>
<script lang="ts" setup>
import { HelloWorld } from '#my/ui';
</script>
Nuxt project plugin vuetify.ts:
import 'vuetify/styles';
import { createVuetify } from 'vuetify';
import * as components from 'vuetify/components';
import * as directives from 'vuetify/directives';
export default defineNuxtPlugin((nuxtApp) => {
const vuetify = createVuetify({
// components, if imported components getting resolved but treeshaking doesn't work.
// directives
});
nuxtApp.vueApp.use(vuetify);
});
Expected Behavior
Vuetify components from the Library project should be auto imported.
Current workaround:
If the vuetify components are imported in the parent project then the components are resolved. But this causes issue as the library users has to know what to import or import on global which is creating larger bundle size.
Is there an alternative way to implement and meet the following criteria:
Wrapping module doesn't depend on vuetify (Peer dep only)
Consuming app can auto import and get all of the benefits of tree shaking
Consuming app doesn't need to import any of the peer dependencies of the wrapping module.
Thank you so much in advance.
Just to create an answer for the workaround Sasank described:
If you just want to get rid of the error, import the components into the parent project as described in this link: https://next.vuetifyjs.com/en/features/treeshaking/#manual-imports

What are some ways to use aframe with Vue?

I'm using vue-cli. I've tried adding aframe directly in the index.html file, and also importing it in my top level main.js file. I just can't get Vue to recognize aframe elements.
Am I missing some boilerplate in the documentation?
Example warning:
vue.runtime.esm.js:619 [Vue warn]: Unknown custom element: -
did you register the component correctly? For recursive components,
make sure to provide the "name" option.
You can add aframe to the dependencies in your package.json:
"browserify-css": "*", // required by aframe
"envify": "*", // required by aframe
"aframe": "^1.2.0", // aframe
add aframe to the compiled bundle:
var Vue = require('vue')
var App = require('./app.vue')
require('aframe')
new Vue({
el: '#app',
render: function (createElement) {
return createElement(App)
}
})
and use the elements in your <template>:
<template>
<div>
<a-scene>
<a-cylinder color="#FFC65D"></a-cylinder>
</a-scene>
</div>
</template>
check it out in this glitch
For development it is fine to import the CDN into your index.html
For production it is recommended to require it into your Main.js
Vue 2
To get rid of the warnings i recommend adding a array of components using Vue.config.ignoredElements placed in the main.js
Like so:
Vue.config.ignoredElements = [
'a-scene',
'a-camera',
'a-box'
'a-image',
]
For a full list of components check out this Repo: aframe-components-list
I recommend using the Vue.config.ignoredElements instead of registering your A-Frame components like normal Vue component, since they are not Vue components.
Edit - Vue 3:
In Vue 3 Vue.config.ignoredElements in main.js will not work.
Instead, in your vue.config.js file add the code below:
// vue.config.js
module.exports = {
chainWebpack: config => {
config.module
.rule('vue')
.use('vue-loader')
.tap(options => {
options.compilerOptions = {
...options.compilerOptions,
isCustomElement: tag => tag.startsWith('a-')
}
return options
})
}
}
This should cover custom elements that start with 'a-'.

Vuetify VIcon doesn't show up in Storybook

I'm developing a Vue app with Vuetify and also document the components with Storybook.
I'm writing the stories nicely, all components seem to show up in Storybook (like my custom components & the Vuetify components too). Except for VIcon.
I have a component that uses Vuetify's VIcon, and I couldn't get the icon to show up (in the real app there's no problem with that).
The setup:
src/plugins/vuetify.js
import Vue from 'vue';
import Vuetify from 'vuetify/lib';
Vue.use(Vuetify);
export default new Vuetify({
icons: {
iconfont: 'mdiSvg',
}
});
.storybook/vuetify_storybook.js
import Vue from 'vue';
import Vuetify from 'vuetify'; // loads all components
import 'vuetify/dist/vuetify.min.css'; // all the css for components
import config from '#/plugins/vuetify'; // basic config with theme
Vue.use(Vuetify);
export default new Vuetify(config);
.storybook/preview.js
import { addDecorator } from '#storybook/vue';
import vuetify from './vuetify_storybook';
addDecorator(() => ({
vuetify,
template: `
<v-app>
<story />
</v-app>
`,
}));
.storybook/main.js
const path = require('path');
module.exports = {
stories: [
'../stories/**/*.stories.js',
'../src/**/*.stories.js'
],
addons: [
'#storybook/addon-actions',
'#storybook/addon-links',
'#storybook/addon-knobs',
'#storybook/addon-storysource'
],
webpackFinal: async (config, { configType }) => {
config.resolve.extensions.push('.vue', '.css', '.less', '.scss', '.sass', '.html')
// Use Sass loader for vuetify components
config.module.rules.push({
test: /\.sass$/,
use: ['style-loader', 'css-loader', 'sass-loader'],
include: path.resolve(__dirname, '../'),
});
config.module.rules.push({
test: /\.scss$/,
use: [
'vue-style-loader',
'css-loader',
{
loader: 'sass-loader',
options: {
additionalData: "#import '#/styles/variables.scss';"
}
}
],
});
config.module.rules.push({
resolve: {
alias: {
'#': path.resolve(__dirname, '../src'),
vue: 'vue/dist/vue.js',
'vue$': 'vue/dist/vue.esm.js',
},
},
});
// Return the altered config
return config;
},
};
CustomVIcon.stories.js
import { withKnobs } from '#storybook/addon-knobs'
export default {
title: 'Display that icon',
decorators: [withKnobs]
}
export const displayIcon = () => {
return {
template: `<v-icon>mdi-alert</v-icon>`
}
}
If I add a text that is not an mdi icon (like <v-icon>notmditext</v-icon>, then the text is displayed - but as soon as I add a - (dash/minus sign) to the string, it doesn't show up.
I can see the icon's HTML (well, part of it) in the console, only the ::before part is missing (that should be the actual icon). So styles are set, classes are added, etc when I inspect the Storybook page (where the icon should be).
Already tried adding https://www.npmjs.com/package/storybook-addon-jsx (as in the real case the component is rendered with JSX), nothing changed (no v-icon)
Already tried putting other components in the story (like VCard), and they showed up (and other stories work just perfectly)
Vue is 2.6.12, Vuetify 2.3.10, #storybook/vue 6.0.21 - so quite fresh
Also tried to import components from vuetify/lib (and not just vuetify) in the .storybook/vuetify_storybook.js & registering them locally (in the preview.js and the story file - no change)
OK, just needed another view on the things:
removed the link to the Material design icons CDN:
// remove this from public/index.html
<link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/#mdi/font#latest/css/materialdesignicons.min.css">
changed the package from #mdi/js to #mdi/font
npm remove #mdi/js
npm install #mdi/font -D
imported the corresponding CSS in two places:
// add this to src/main.js & .storybook/vuetify_storybook.js
import '#mdi/font/css/materialdesignicons.css';
changed the Vuetify config
// in src/plugins/vuetify.js
icons: {
// iconfont: 'mdiSvg', // change this
iconfont: 'mdi', // to this
},
AND VOILÁ! VIcon shows up.
So, the problem was that I thought everything had been set up correctly, but it wasn't the case: the icons in the app were coming from the CDN (have not looked at the Network tab), and when I removed the CDN link from the index.html it immediately became apparent.
More on setting up the icons in Vuetify: Install Material Design Icons

nuxt with vue-dragscroll: window is not defined

I am trying to use vue-dragscroll with nuxtjs.
I am new to nuxtjs and I have been using vue-dragscroll before with regular vuejs.
I have been shown an error Window is not defined, I've looked at the vue-dragscroll documentation and I still couldn't find the solution.
This is how I implemented the vue-dragscroll
<template lang="pug">
div
CountriesSearch.mb-2
div#countryList(v-for="country in countries" :key="country.country" v-dragscroll)
CountryItem(:country="country" v-if="country.Country")
</template>
<script>
import { dragscroll } from 'vue-dragscroll'
export default {
directives: {
dragscroll
},
You will have to declare it as a directive within a plugin file.
// plugins/vue-dragscroll.js
import Vue from 'vue'
import { dragscroll } from 'vue-dragscroll'
Vue.directive('dragscroll', dragscroll)
Then, in your nuxt.config.js add that plugin file to your plugins: [] array:
{ src: '#/plugins/vue-dragscroll.js', ssr: false }
This directive leverages the window which is unavailable during SSR, hence your error.

How to Access Vue-Loader Components in an HTML File

I would like to use the modular style and file format of Vue Loader (i.e., where I have a template section, script section and style section in each .vue file).
What I can't figure out how to do (or if it is even possible to do) is use my custom templates in an html file.
For instance, in the App.vue file I can use the following code:
<template>
<div id="app">
<message>Hello there</message>
</div>
</template>
This will work to display a custom message component on the home page.
What I would like to do instead is use my custom components in html files. For instance, in the index.html file to use the following code:
<div id="app">
<message>Hello there</message>
</div>
Any idea how I can do this? Thanks.
NOTE: I am new to Vue Loader and semi-new to Vue (so I apologize in advance if the answer to this question is obvious).
There are many ways you can compile a single file component and then use that component in a web page.
Use vue-cli
Vue released a command line interface tool called vue-cli that can initialize projects and build components with zero configuration. One option to build a component that you can use in your page is to use vue build.
vue build MyComponent.vue --prod --lib MyComponent
This will compile a script that exposes MyComponent. If you include that script in your page and then add it globally,
Vue.component(MyComponent)
That component will be available to you in any of your Vues.
Make a plugin
Here is a sample of a very basic framework for making a plugin.
myPluginDefinition.js
window.MyPlugin= {};
MyPlugin.install = function (Vue) {
Vue.component('my-component', require('./my-component.vue'));
}
webpack.config.js
module.exports = {
entry: "./myPluginDefinition.js",
output: {
path: __dirname+'/dist',
filename: "MyPlugin.js"
},
module: {
loaders: [
{
test: /\.vue$/,
loader: 'vue-loader',
}
]
}
};
This will build a file called MyPlugin.js that will contain each of the single file components that you include in the install function. Include the script on your page and then call
Vue.use(MyPlugin)
and you will have all of your components.
Use a custom webpack configuration
There are many ways you could configure webpack to build your single file components. You could build them all into a single file or build them separately. I suggest if you want to use one of these options you ask a separate question.
Actually you can do this easily by:
register your component :
Vue.component('message', {
template: '<div>A custom component!</div>'
});
then comment the render function in your Vue instance like so:
new Vue({
el: '#app',
// render: h => h(App)
})
after that you will be able to render your message Tag like this:
<div id="app">
<message></message>
</div>
Edit :
if you don't want to use this way you can define it in your view instance:
new Vue({
el: '#app',
// render: h => h(App)
components: {
message: {
template: `
<h1>Hello World</h1>
`
}
}
})
Import desired component definition object and pass it to options.components
<template>
<some-component></some-component>
</template>
<style>...</style>
<script>
import SomeComponent from 'path/to/some-component.vue';
export default {
components: {
// ES2015 shorthand for SomeComponent: SomeComponent
SomeComponent
}
}
</script>
That leverages local component registration
Both the default export and SomeComponent are component definition objects.