import and export statements - javascript

I am new to Vue and have previously used React to build small apps.
Now, I was going through through the boilerplate code for Vue
Consider this app.vue
<template>
<div id="app">
<img alt="Vue logo" src="./assets/logo.png">
<HelloWorld msg="Welcome to Your Vue.js App"/>
</div>
</template>
<script>
import HelloWorld from './components/HelloWorld.vue'
export default {
name: 'app',
components: {
HelloWorld
}
}
</script>
Here, I am unable to comprehend following things related to import and export.
Starting from the beginning
<HelloWorld msg="Welcome to Your Vue.js App"/>
here it seems we passing props to our child component.
In react, we used to import statements at the top of the app and then use it in our stateful or stateless component but in contrast, in the above code snippet we are importing it after inside the script tag so as JS compiles the code, how would it know what
<HelloWorld msg="Welcome to Your Vue.js App"/>
is HelloWorld? since it is declared afterwards.
Secondly, I have always worked with exporting and importing functions/classes and this is different and now for me to comprehend. Consider this children component
<template>
<div class="hello">
<h1>{{ msg }}</h1>
</div>
</template>
<script>
export default {
name: 'HelloWorld',
props: {
msg: String
}
}
</script>
Here, I am unable to comprehend what is happening in export default? I know what export default does but like what is the significance of having properties like name and props inside it?

It's the way Vue is structured. The export default part that you write in the bottom is the part you import/export to the Vue ecosystem (and to your components), this is an ES6/ES2015 feature(module system), one thing to note that the structure you are using is called Single File Components (.vue files).
One great thing about single file components that I like is that you can import another component within the script tag (See: Component Registration), just above export default, then you can reference it in the export default object(exposing it to your component).

Related

How to use an Vanilla Javascript Class in a Vue Component?

I have a laravel project which globally registers vue in the app.js file. Vue works in my project as I have it working in another part of my app. What makes this situation unique is I do not have somewhere, like a blade file, to pass my vue component through as a medium to be used. I have a vanilla js file and a vue component. I want to be able to use the methods created in my test.js file inside of my testing.vue file. I am simply trying to pass some data from my js to my vue and then console.log() it out to ensure the data is being passed properly. I do use npm run dev to compile assets. The code is pretty boiler plate at this point since my main objective right now is to just pass the data properly. I did confirm the import path as well and it is correct. Not sure why the console.log() is not showing in the browser. This is my current code:
Test.js
export class Test {
testing() {
console.log('this is a test');
}
}
Testing.vue
<template>
<div>
</div>
</template>
<script>
import {Test} from '../../js/Test';
export default {
name: 'Testing',
mounted() {
console.log(Test.testing());
}
}
</script>
You must create an instance of the Test class in order to use the method. In your Testing.vue file:
<template>
<div>
</div>
</template>
<script>
import {Test} from '../../js/Test';
export default {
name: 'Testing',
mounted() {
// IMPORTANT to create instance of a class
const myTestInstance = new Test();
console.log(myTestInstance.testing());
}
}
</script>
The method testing() on class Test is not a static method and thus need object to invoke. Alternately, if you don't want to create object, then you can declare the method as static as shown below:
export class Test {
static testing() {
console.log('this is a test');
}
}
You can then us it like this: Test.testing().

Vue 3 - "Failed to resolve component" with global components

My Vue components work fine when declared in the top level HTML file, like this
<body>
<div class='app' id='app'>
<header-bar id='headerBar'></header-bar>
<journal-page></journal-page>
</div>
<script src="js/app.js"></script>
</body>
but using a <journal-card> component inside the <journal-page> component gives me the error:
[Vue warn]: Failed to resolve component: journal-card at <JournalPage>.
How do I fix this please?
Here's my top level code that loads the Vue components, app.js:
import * as _vue from 'vue';
import _headerBar from './widgets/headerBar.vue';
import _journalCard from './widgets/journalCard.vue';
import _journalPage from './widgets/journalPage.vue';
import _store from './data/store.js';
const app = _vue.createApp
({
components:
{
'headerBar': _headerBar,
'journalCard': _journalCard,
'journalPage': _journalPage
},
data : _store,
methods: {}
});
const mountedApp = app.mount('#app');
and here's my journal-page.vue container
<template>
<ul>
<journal-card v-for="item in journal" :key="item.id" :entry=item></journal-card>
</ul>
</template>
<script lang="js">
import _store from '../data/store.js';
export default {
'data': _store
};
</script>
and journal-card.vue component
<template>
<div>
hi imma journal entry
</div>
</template>
<script lang="js">
export default {
'data': null,
'props': [ 'entry' ]
};
</script>
Registering components in the root component's components option doesn't make them global. Doing that just makes them available to the root component itself, not its children.
To register components globally, use app.component in your top-level code:
main.js
import { createApp } from 'vue';
import App from './App.vue';
import MyGlobalComponent from './components/MyGlobalComponent.vue';
const app = createApp(App);
app.component('MyGlobalComponent', MyGlobalComponent); ✅
const mountedApp = app.mount('#app');
In my scenario issue was different. I was trying to render a similar multi word Vue component in a laravel blade file.
If you're referring a Vue component in a non .Vue file (like HTML / Laravel Blade etc), you should use kebab-cased format to refer the component name. Like my-global-component
Vue documentation - https://vuejs.org/guide/essentials/component-basics.html#dom-template-parsing-caveats
Also as a side note since this page shows up when looking in search engines for some problems with "Vue 3 Failed to resolve component", there are a few things that were deprecated with Vue 3 / Quasar 2:
eg. q-side-link
that silently disappeared (previous doc here)
as per this comment:
QSideLink -- no longer required! Simply use a QItem or whatever
component you want and bind an #click="$router.push(...)" to it.
Sorry if it's not exactly on topic but it will bite some other people, so I prefer to help one person with this comment ;-)
make sure the mounting code comes at last so
app.mount('#app')
app.component('list-view', ListView2)
is wrong and it will not work but
app.component('list-view', ListView2)
app.mount('#app')
is correct.

Why Vue regular slots also available in this.$scopedSlots?

Please see this minimum example
Test.vue
<template>
<div>
<slot name="this_is_not_scoped_slots"/>
</div>
</template>
<script >
import Vue from "vue";
export default Vue.extend({
mounted() {
console.log(Object.keys(this.$scopedSlots));
}
});
</script>
App.vue
<template>
<Test>
<template #this_is_not_scoped_slots>But it shows in this.$scopedSlots</template>
</Test>
</template>
<script>
import Test from "./Test.vue";
export default {
components: {
Test
}
};
</script>
In the above example, this console will log out ["this_is_not_scoped_slots"].
Why is this happening?
There are two properties in Vue instance
this.$slots
this.$scopedSlots
These two act really differently:
If you use v-slot:my_scope_name="{ someValue }", then my_scope_name won't show up in this.$slots
However, no matter what you define, your named slots will always show up in this.$scopedSlots
Why is this happening?
I'm building a library, I want to conditional rendering if the user has provided named slots or not, should I always use this.$scopedSlots to detect those things?
According to the official API :
....
All $slots are now also exposed on $scopedSlots as functions. If you work with render functions, it is now recommended to always access slots via $scopedSlots, whether they currently use a scope or not. This will not only make future refactors to add a scope simpler, but also ease your eventual migration to Vue 3, where all slots will be functions.

Using component without even declaring it

I am very new to Vue and I have read an article or two about it (probably vaguely).
Also, Since I have some understanding of react, I tend to assume certain things to work the same way (but probably they do not)
Anyway, I just started with Quasar and was going through the Quasar boilerplate code
In the myLayout.vue file, I see being used inside my template
<template>
<q-layout view="lHh Lpr lFf">
<q-layout-header>
<q-toolbar
color="negative"
>
<q-btn
flat
dense
round
#click="leftDrawerOpen = !leftDrawerOpen"
aria-label="Menu"
>
<q-icon name="menu" />
</q-btn>
based on my vaguely understanding, I thought for every component we are using to whom we need to pass props we need to import it as well but unfortunately I can't see it in my import-script area
<script>
import { openURL } from 'quasar'
export default {
name: 'MyLayout',
data () {
return {
leftDrawerOpen: this.$q.platform.is.desktop
}
},
methods: {
openURL
}
}
</script>
I would've thought the script to be something like
<script>
import { openURL } from 'quasar'
import {q-icon} from "quasar"
or at least something like that but here we only have
import { openURL } from 'quasar'
Also, Even if we remove the above snippet, our boilerplate app looks to be working fine so here are my two questions
Question 1: What is the use of import { openURL } from 'quasar' (like what it does)
Question 2: How can template contain <quasar-icon> or <quasar-whatever> without even importing it in script tag?
How can template contain <quasar-icon> or <quasar-whatever> without even importing it in script tag?
There are two ways to import components. The first way (which I recommend, and being most similar to React) is to import the component and add it to the components option inside the component that you want to use it within.
App.vue
<div>
<my-component/>
</div>
import MyComponent from 'my-component'
export default {
components: {
MyComponent
}
}
The second way is to import it globally for use within any Vue component in your app. You need only do this once in the entry script of your app. This is what Quasar is doing.
main.js
import Vue from 'vue'
import MyComponent from 'my-component'
Vue.component('my-component', MyComponent)
What is the use of import { openURL } from 'quasar' (like what it does)
I'm not familiar with Quasar, so I can't give you a specific answer here (I don't know what openURL does). You should check the Quasar docs.
openURL is being used as a method here. Perhaps it is being called from somewhere in the template (which you have excluded from the question).
A1) Import statement is 1 way (es6) way to split your code into different files and then import functions/objects/vars from other files or npm modules see https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Statements/import
A2) Vue allows 2 mechanisms to register components. Global and local. Globally registered components does not have to be imported and registered in every component before use (in template or render fn). See URL from comment above https://v2.vuejs.org/v2/guide/components-registration.html#Global-Registration

Cleaner way to require multiple Vue components?

I've just started working with Vue.JS and there's one small issue that's bugging me. My file structure similar to the following:
+ js
|--+ components
| |-- parent.vue
| |-- child.vue
|-- main.js
Then in my main.js I have the following:
window.Vue = require('vue');
require('vue-resource');
Vue.component('parent', require('./Components/parent'));
Vue.component('child', require('./Components/child'));
var app = new Vue({ el: "#app" });
(I'm not actually certain what vue-resource is, but this was set up for me by a fresh install of Laravel 5.3)
At a glance I immediately noticed that my main.js file was going to get unmanageable if I added too many components. I don't have this issue when working with ReactJS because main.js only needs to include the "parent" component, and the parent component includes the child component. I figured Vue.JS would have a similar trick to help me organize my components - but reading through the docs I didn't find one (maybe I missed it?)
Is there a way to either have a Vue component list its dependencies (for Browserify / Webpack to bundle) or recursively run a javascript statement on every file in a directory (so Browserify / Webpack just packs up the whole thing)?
I'm not concerned with async components at the moment - so if the solution breaks that functionality it will be okay. One day I would like to play around with using Webpack to create async components and only loading them as I need them, but today I'm more interested in just getting this up and running so I can play way Vuex.
The Vue.component syntax is for global components only, if you have a component that is being used inside another component use this:
import Parent from './components/Parent.vue';
import Child from './components/Child.vue';
new Vue({
el: "#app",
components: { Parent, Child }
});
Than inside this components you can use the other components.
The only advantage of using Vue.component(Parent) is that you can use this <parent></parent> component globaly in all your other components without declaring them implicitly.
Good Luck :)
You don't need to import everything at the top level.
In your main.js you can import the Parent component
import Parent from './components/Parent.vue'
new Vue({
el: "#app",
components: {
Parent
}
})
With your Parent.vue
<template>
<div>
<p>I am the parent</p>
<child></child>
</div>
</template>
<script>
import Child from './Child.vue'
export default {
mounted() {
console.log('mounted parent')
}
}
</script>
<style scoped>
// ...
</style>
Then in your Child.vue
<template>
<p>I am the child</p>
</template>
<script>
export default {
mounted() {
console.log('mounted child')
}
}
</script>
<style scoped>
// ...
</style>
And you should end up with
<div>
<p>I am the parent</p>
<p>I am the child</p>
</div>
I found a way, not sure if it's the best in terms of performance and webpack chunk size. I created an index.js file in the components root:
export const HelloWorld = require('./HelloWorld.vue').default
So, inside the components I would use:
const { HelloWorld } = require('#/components')
Due to babel issues I need to make a mix of require and export, also the use of default attribute after require — as I read in some babel use discussions.

Categories