How can I use a Vue 3 plugin in Storybook stories?
For example, I use FormKit to create form elements in my components.
FormKit is rigged globally in my main.ts like so:
import { plugin, defaultConfig } from '#formkit/vue'
const app = createApp(App)
app.use(plugin, defaultConfig)
but this file isn't used by Storybook... so how can I do the same for Storybook?
I was able to find documentation here:
https://github.com/storybookjs/storybook/tree/47f753f5e8d084b4d544cf1ec76077a9382aa6b2/app/vue3
I learned that you are able to access the app created by Storybook in .storybook/preview.js by importing it from #storybook/vue3.
Example is copied from above link:
// .storybook/preview.js
import { app } from '#storybook/vue3';
app.use(MyPlugin);
app.component('my-component', MyComponent);
app.mixin({
/* My mixin */
});
Related
Hello I have a new Vue 3 app using Vite.
I'm registering my UserTextInput component globally but inside all my components this.$options.components is empty
import UserTextInput from "./components/defaultComponents/UserTextInput.vue";
const app = createApp(App);
app.component('UserTextInput', UserTextInput);
app.mount('#app');
Inside any other component
mounted() {
console.log(this.$options.components); // {} empty object
},
I'm looking for a way to see what components have been registered globally.
Any Ideas?
You could try this one
import { getCurrentInstance } from 'vue';
getCurrentInstance().appContext.components // this will list all component registered globally
I found the list stored here in my Vue 3 app:
app._instance.appContext.components
My main.js contains import "mathjs"; and seems to be working fine within main.js as
console.log(median(5,4,6,1));
> 4.5
median() is however not available outside of main.js, i.e. in components. After reading this answer, I assumed that the simple import should be enough for global availability throughout the vue app?
In general when you are working with modern modules or a bundler like webpack, you have to import mathjs in every module (think file) you want to use it.
What is often done in the vue context, is adding the library to the Vue context itself with a plugin.
See https://v3.vuejs.org/guide/plugins.html#writing-a-plugin
So this should be as easy as:
const mathjsPlugin = {
install(app){
app.config.globalProperties.$mathjs = mathjs;
}
}
const app = createApp(...);
app.use(mathjsPlugin);
// inside of a component
export default {
created(){
console.log(this.$mathjs....);
}
}
I personally think explicitly importing is a cleaner approach, but if mathjs is used in most of the components, this approach can make sense
in main.js import all as math then add it as global property :
import * as math from 'mathjs'
const app=createApp({....})
app.config.globalProperties.$math =math
then in any component you could use it like this.$math.theFunctionName
I am designing an architecture for the Vue 3 app with distributed module-based ownership. Module system will be represented with plugins (seems like the most appropriate solution allowing vuex module and vue-router dynamic injects). Each such module/plugin will be developed by dedicated team working within isolated repos. We cannot use npm package-per-plugin approach as deployment process should be isolated as well, and with npm approach core app team will have to rebuild app each time npm package plugin has updates. This means we will have to load such plugins/pages at runtime via http.
So far this approach by Markus Oberlehner seems like some sort of the way to go - it uses custom Promise based solution for webpack's missing "load external url script at runtime" functionality. While it works fine with Vue 2, Vue 3 gives VNode type: undefined error.
The above mentioned article offers the following webpack external component loading solution:
// src/utils/external-component.js
export default async function externalComponent(url) {
const name = url.split('/').reverse()[0].match(/^(.*?)\.umd/)[1];
if (window[name]) return window[name];
window[name] = new Promise((resolve, reject) => {
const script = document.createElement('script');
script.async = true;
script.addEventListener('load', () => {
resolve(window[name]);
});
script.addEventListener('error', () => {
reject(new Error(`Error loading ${url}`));
});
script.src = url;
document.head.appendChild(script);
});
return window[name];
}
But above, as I said, does not work with Vue 3 defineAsyncComponent mechanism.
// 2.x version WORKS
const oldAsyncComponent = () => externalComponent('http://some-external-script-url.js')
// 3.x version DOES NOT WORK
const asyncComponent = defineAsyncComponent(
() => externalComponent('http://some-external-script-url.js')
)
So I have two questions:
Are there any known better solutions/suggestions for above architectural specification?
Is there any working webpack dynamic external import solutions tested with Vue 3 out there?
UPD: Here is small reproduction repo
We solved this problem together via chat.
Components built via the Vue 3 vue-cli rely on Vue being available in the global scope. So in order to render components loaded via the technique described in my article, you need to set window.Vue to a reference to Vue itself. Then everything works as expected.
update:
If import vue from vue/dist/vue.esm-bundler and set to global, then no need to change webpack / Vite config, and no need to load vue from cdn.
import * as Vue from 'vue/dist/vue.esm-bundler';
window.Vue = Vue;
Besides setting window.Vue, some other webpack or Vite configuration should also be set, otherwise some error is presented in console: vue warn invalid vnode type symbol(static) (symbol)
Vue3 + webpack:(https://github.com/vuejs/vue-next/issues/2913#issuecomment-753716888)
// index.html:
<script src="https://cdn.jsdelivr.net/npm/vue#3.0.4"></script>
// vue.config.js
configureWebpack: config => {
...
config.externals = { vue: 'Vue' }
...
}
Vue3 + vite:(https://github.com/crcong/vite-plugin-externals)
// vite.config.js
import { viteExternalsPlugin } from 'vite-plugin-externals'
export default {
plugins: [
viteExternalsPlugin({
vue: 'Vue'
}),
]
}
I have a subcomponent within a file, and I want to extract it out of the file and into another file.
I'm just using vue (not cli) and php, and I don't have access to ES6, so I can't use those import statements.
const form = {
...
components:{
subForm:{
/* lots of code */
}
}
}
If you are using require.js you can just import it from the new file
const subForm = require('path/to/new/file);
const form = {
components { subForm };
}
If not, you can create the component in a new file and registered in the global Vue instance.
Vue.component('subForm', {
/* lots of code */
});
This will work assuming that you import the file into your HTML using a bundler or something like gulp. And that Vue is globally included in the DOM either through a script tag linking to a CDN or through whatever you are using to bundle the application.
I have a question regarding importing an anime.js into my vue project. I am using vue cli. How do I include animejs to my project? I tried it this way:
import anime from 'animejs'
Vue.use(anime);
but I get an error in the console that says:
Uncaught TypeError: a.hasOwnProperty is not a function. . .
can you guys help me?
Vue.use() is used only for plugins designed for Vue.js. You can't simply add a library there, it won't work.
My suggestion is that you create that plugin and use it on your project to make anime.js acessible everywhere.
You could do it like this:
//vue-anime.js
import anime from 'animejs';
const VueAnime = {
install (Vue, options) {
Vue.prototype.$animeJS = anime;
}
}
export default VueAnime
Then later
import VueAnime from './vue-anime';
Vue.use(VueAnime);
Now every Vue component will be able to use anime acessing this.$animeJS.
Or simply -
import Vue from "vue";
import anime from 'animejs/lib/anime.min.js';
Vue.prototype.$anime = anime;
Then this.$anime in all components
#Phiter's answer looked good at first, but I wasn't able to get it to work in my vue-cli 3 environment. The below code worked though, so I think it may work for you. This is just a simple way to install an external library into your Vue app and expose it through a prototype:
// animejs.js
import anime from 'animejs'
const install = (Vue, options) => {
Vue.prototype.$animejs = anime
}
export default install
// Then, in your main.js (at least for me)
import VueAnime from './animejs'
Vue.use(VueAnime)
Now, when you need to access the library, just use this.$animejs in your project.
or simply like this in main.js after npm install:
import anime from 'animejs';
Object.defineProperty(Vue.prototype, '$anime', { value: anime });
then use this.$anime tu use it.
To use AnimeJS globally in Vue 3 project, just create a plugin (plugins/anime.js):
import anime from 'animejs';
export default {
install(app, options) {
app.config.globalProperties.$anime = anime;
},
};
Then include it (main.js or elsewhere):
import VueAnime from './plugins/anime';
createApp(App)
.use(VueAnime)
.mount('#app');
And now, it's accessible everywhere by this.$anime.
Please notice that a minor change from the previous version is installing the plugin for Vue 3.