Cleaner way to require multiple Vue components? - javascript

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.

Related

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.

import and export statements

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).

How can I insert a VueJS app into an existing page?

At this project I'm working on there is a legacy server-rendered web page and some components had problems I've been assigned to fix, and I convinced the team to rewrite those parts in Vue to kickstart our migration.
I wrote the whole mini-app using the Webpack template provided by Vue CLI and it works like a charm... in that specific environment.
If I npm run build the built index.html also works fine in a static server.
However, I can't seem to include the app in an existing page composed of many other elements. Shouldn't it be as simple as adding the <div id='myApp'></div> element to the HTML and loading the generated JS files?
If it helps, the legacy app is a Rails app using .erb templates and the JS files are being loaded through the main pipeline in application.js.
Does anyone know why nothing happens when I try this?
Edit: more information - this is how main.js looks before build:
/* eslint-disable */
import Vue from 'vue'
// UI components
import VueSelect from 'vue-select'
import DynamicForm from './components/DynamicForm/'
Vue.component('vue-select', VueSelect)
Vue.config.productionTip = false
const DynamicForms = new Vue({
el: '.dynamic-form',
render: h => h(DynamicForm)
})
Edit: I managed to get Vue to work by integrating Webpack to Rails with Webpacker. However I still have some problems regarding context:
This is my main.js in one of the Vue components. It was working fine until I tried the PropData stunt so I could reuse the component with different data in a few places.
/* eslint-disable */
import Vue from 'vue'
// UI components
import VueSelect from 'vue-select'
// import 'nouislider'
import DynamicForm from './components/DynamicForm/'
import fields from './fields'
import fieldRules from './field-rules'
Vue.component('vue-select', VueSelect)
Vue.config.productionTip = false
document.addEventListener('DOMContentLoaded', () => {
const el = document.createElement('div')
document.querySelector('.dynamic-form').appendChild(el)
const vm = new DynamicForm({
propsData: {
fields,
fieldRules
},
el,
render: h => h(DynamicForm)
})
})
This is DynamicForm/index.vue
<template>
<div id='app'>
<ParamList :fields='paramFields' :fieldRules='paramRules'></ParamList>
<Output></Output>
</div>
</template>
<script>
import Vue from 'vue'
import ParamList from './ParamList'
import Output from './Output'
export default Vue.extend({
props: [ 'fields', 'fieldRules' ],
name: 'DynamicForm',
components: {
ParamList,
Output
},
data () {
return {
paramFields: this.fields,
paramRules: this.fieldRules
}
}
})
</script>
<style>
</style>
The field and fieldData props are merely JSON/JSONP with some data I'm going to use inside those components. The idea is that I could write another main.js changing just the field and fieldData when initing the Vue instance.
What am I doing wrong?
I've managed to fix everything in a three-step change to my components.
Integrate Webpack into Rails using Webpacker. There's even a Vue template!
Change the root component (the one mounted at a real DOM element) to a Vue subclass using Vue.extend (so the module line # the .vue file read export default Vue.extend({ instead of simply export default {
Remove the render function from the new DynamicForm (the name I assigned Vue.extend to) so it renders its own template.
I hope it helps as it was quite a pain to me!

VueJs Runtime components

I'm using VueJS with my current project and when I tried to register new component and use it like <component></component> i got that runtime error. I found solution to render components like this:
import App from './components/Example.vue'
new Vue({
el: '#app',
render: h => h(App)
})
And yes It works, it really renders my component but is there any way to stick with that method of registering components ?
I've read about script to add in webpack but I'm not really sure what and where to do this is my script for running code: (watching for changes too)
watchify src/app.js -t vueify -t babelify -p browserify-hmr -p [ vueify/plugins/extract-css -o public/styles.bundle.css ] -o public/app.bundle.js
There are two ways you can use the component in your template the way you want. First, you can register the component globally.
import SomeComponent from "./SomeComponent.vue"
Vue.component("some-component", SomeComponent)
You could then use this in a template as <some-component></some-component>.
Second, you can locally register components.
import SomeComponent from "./SomeComponent.vue"
new Vue({
components:{
SomeComponent
}
})
Which, again, you could use in the template as <some-component></some-component>, but only in the root Vue. If you wanted to use it in other components you would need to import it and add it to the local components of the component you want to use it in.

How do I register a Vue component?

I have the following files. All I want to do is to be able to create different components that are injected. How do I achieve this using require.js? Here are my files:
main.js
define(function(require) {
'use strict';
var Vue = require('vue');
var myTemplate = require('text!myTemplate.html');
return new Vue({
template: myTemplate,
});
});
myTemplate.html
<div>
<my-first-component></my-first-component>
</div>
MyFirstComponent.vue
<template>
<div>This is my component!</div>
</template>
<script>
export default {}
</script>
I'm going to assume you're using webpack as explained in the Vue.js docs, or else your .vue file is useless. If you're not, go check how to set up a webpack Vue app first, it's what lets you use .vue files.
import Menubar from '../components/menubar/main.vue';
Vue.component('menubar', Menubar);
That's how you add e.g. a menubar component to the global scope. If you want to add the component to just a small part of your app, here's another way of doing it (this is taken from inside another component, but can be used in exactly the same manner on your primary Vue object):
import Sidebar from '../../components/sidebar/main.vue';
export default {
props: [""],
components: {
'sidebar': Sidebar
},
...
You can load components without webpack, but I don't recommend it, if you're gonna keep using Vue (which I strongly suggest you do) it's worth it to look into using webpack.
Update
Once again, really, really, really consider using webpack instead if you're gonna be continuing with Vue.js, the setup may be slightly more annoying but the end result and development process is waaaay better.
Anyway, here's how you'd create a component without webpack, note that without webpack you can't use .vue files since the .vue format is part of their webpack plugin. If you don't like the below solution you can also use e.g. ajax requests to load .vue files, I believe there is a project somewhere out there that does this but I can't find it right now, but the end result is better with webpack than with ajax anyway so I'd still recommend going with that method.
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'],
});
As you can see, this method is A LOT more cumbersome. If you want to go with this method I'd recommend splitting all components into their own script files and loading all those components separately prior to running your actual app.
Note that `Text` is a multi line string in javascript, it makes it a little easier to write your template.
And as I said, there is some project out there for loading .vue files using ajax, but I can't for the life of me find it right now.

Categories