Vuepress - Add component to each page - vue.js

The documentation in regards to this is quite short. I want to add a Vue component to each page in the app, without having to manually declare it in each template.
I managed to add a enhanceApp.js file and add this to it:
import MyComponent from './components/my-component'
export default ({
Vue,
options,
router,
siteData
}) => {
Vue.component('MyComponent', MyComponent)
}
The app runs but I don't see the component anywhere. Any tips or other ways I can achieve this? Thanks!

You do not need to put it in enhanceApp.js, just having the component in /.vuepress/components is sufficient.
Although, if you want to keep them outside that folder, that might be the way to get Vuepress to know about them.
Use it in an md file as you would do in a Vue template,
<MyComponent></MyComponent>

This is an old question and might only apply to V1 but this is simple to accomplish.
Add your Vue component to .vuepress/components as usual
In .vuepress/config.js add the following:
module.exports = {
// rest of config...
globalUIComponents: [
'YourComponent'
]
}
Don't even need to import your component.
See docs for more info.

Related

Import Vue components from folder programmatically not hard coded (I'm actually using Nuxt but I'll call it Vue)

I have a number of SVG cards as components in Vue; I probably have 50 or more. I could import them one by one just after the script tag:
<script>
import MyVueComponent1 from "~/components/MyVueComponent1.vue";
import MyVueComponent2 from "~/components/MyVueComponent2.vue";
import MyVueComponent3 from "~/components/MyVueComponent3.vue";
...
import MyVueComponent50 from "~/components/MyVueComponent50.vue";
But I've been reading that I can do this programmatically. I just haven't found any one example that makes it crystal clear to me. I able to register the components dynamically but I'm not certain how to import an entire folder of components.
I was able to register the components dynamically using this code in the created hook:
created() {
const requireComponent = require.context(
// Look for files in the current directory
"../components",
// Do not look in subdirectories
false,
// Only include "S" prefixed .vue files
/S[\w-]+\.vue$/
);
// For each matching file name...
requireComponent.keys().forEach((fileName) => {
// Get the component config
const componentConfig = requireComponent(fileName);
// Get the PascalCase version of the component name
fileName = fileName.replace("./", "");
fileName = fileName.replace(".vue", "");
const componentName = fileName;
this.generatedComponentList.push(componentName);
// Globally register the component
Vue.component(componentName, componentConfig.default || componentConfig);
});
},
And I'm using the generatedComponentList of component names to display the cards:
<div
v-for="componentName in generatedComponentList"
:key="componentName"
>
<component :is="componentName" :id="componentName"></component>
</div>
But I'd love to get rid of all the import lines under the script tag and have cleaner and more dynamic code. That way if I add a new component card to the folder, it will simply be "picked up" and displayed without having to add the "import" line, or register the component etc. Hopefully I've clearly articulated what I'm looking to achieve.
Nuxt auto-imports the components you use from the ~/components directory, so you don't need to import or register them explicitly.
This feature is enabled in nuxt.config.js with the components config:
// nuxt.config.js
export default {
components: true
}
Thanks tony19 suggesting I look at the nuxt.config.js file, your answer definitely put me on the right track; also thanks to whoever suggested 1 might be the right answer.
Here's the solution that worked for me:
Based on tony19's suggestion I looked at my nuxt.config.js file; specifically the component section. I already had this line in my code to automatically import any components in my components folder:
components: true,
But the components I wanted to import were nested within another folder within the components folder.
After reading this 2 from the nuxt.org docs, I replaced my previous code with this:
//components: true,
components: [
// Equivalent to { path: '~/components' }
'~/components',
{ path: '~/components/myTargetComponents', extensions: ['vue'] }
],
Then, I was able to remove all of my import lines:
<script>
import MyVueComponent1 from "~/components/MyVueComponent1.vue";
import MyVueComponent2 from "~/components/MyVueComponent2.vue";
import MyVueComponent3 from "~/components/MyVueComponent3.vue";
...
import MyVueComponent50 from "~/components/MyVueComponent50.vue";
In my index.vue file I don't have anything listed in the components section anymore...just this as a reminder to myself:
,
components: {
//see nuxt.config.js file ...component section
},
Just to be clear, in my index.vue file, I don't import any components using this format, "import MyVueComponent1 from "~/components/MyVueComponent1.vue"; AND I don't have anything listed in the components section. Also just to clarify, the components I'm wanting to import ARE in a sub folder of the components folder (~/components/myTargetComponents). I realize now that I didn't clearly articulate that in my original post.
As a related piece of this...
As you can see from my original post, I'm using a block of code in the created hook to populate a list of the component names:
this.generatedComponentList.push(componentName);
And then using this list to iterate through the component cards:
<div
v-for="componentName in generatedComponentList"
:key="componentName"
>
<component :is="componentName" :id="componentName"></component>
</div>
But I'm wondering if there's a list of these components already generated by nuxt.config.js file. Any suggestions? And again, thanks everyone for the help, I greatly appreciate it.

Can not read property "document" of undefined - ApexCharts

I'm attempting to import apexcharts into my nuxt project. I've used both the vanilla lib and vue wrapper.
Simply importing the library causes the following error
Like I said, i've attempted importing both
import ApexCharts from "apexcharts"
and
import VueApexCharts from "vue-apexcharts"
Both return the same error.
Is this something I have to configure in the nuxt.config.js file?
This is because you are using Nuxt.js which does SSR for you. Since document does not exist on the server-side it will break.
To work around it there a couple of approaches:
First you can create a plugin/apex.js which is responsible for registering your components
import Vue from 'vue';
import ApexChart from 'vue-apexcharts';
Vue.component('ApexChart', ApexChart);
and in your nuxt.config.js make sure to load the plugin file on the client-side:
module.exports = {
// ...
plugins: [
// ...
{ src: '~/plugins/charts', ssr: false }
],
// ....
};
Now you can reference the ApexChart component anywhere in your app, also make sure to wrap it with ClientOnly component to prevent Nuxt from attempting to render it on the server-side:
<ClientOnly>
<ApexChart type="donut" :options="chartData.options" :series="chartData.series" />
</ClientOnly>
The other approach is you can import Apex charts as async components, which do not get rendered on the server-side, but it has been a hit and miss with this approach, feel free to experiment.
Generally when using Nuxt or any SSR solution, be careful of the libraries you use as they might have an implicit dependency on the execution environment, like requiring browser-specific APIs in order to work like window or document.

Where to import file js in vue/nuxt

Usually I use a js code with functions to run on some events.
Now I am using nuxt.js and I wonder where to put this file or how to create a global method to use these functions in every component.
I could write the methods that I need inside every a specific component but after it wouldn't be usable outsite of it.
How to do that in vue/nuxt?
So one way to do it in vue.js is by using mixins, in nuxt you can also use mixins, then you should register them as plugins, but first:
Non global mixins
Create an extra folder for your mixins. For example in a /mixins/myMixin.js
export default {
methods: {
commonMethod() {
console.log('Hello')
}
}
}
Then import in a layout, page or component and add it via the mixins object:
<script>
import myMixin from '~/mixins/myMixin.js'
export default {
mixins: [myMixin]
}
</script>
Global mixins
For example in a new file plugins/mixinCommon.js:
import Vue from 'vue'
Vue.mixin({
methods: {
commonMethod() {}
}
})
Include the file in nuxt.config.js like that:
plugins: ['~/plugins/mixinCommon']
After that you would have the method everywhere available and call it there with this.commonMethod(). But here an advice from the vue.js docs:
Use global mixins sparsely and carefully, because it affects every
single Vue instance created, including third party components. In most
cases, you should only use it for custom option handling like
demonstrated in the example above. It’s also a good idea to ship them
as Plugins to avoid duplicate application.

In Vue.js why do we have to export components after importing them?

In PHP when we include code from another file, we include it and that's it, the code is now available to us within the file in which we performed the include. But in Vue.js, after importing a component we must also export it.
Why? Why don't we simply import it?
in Vue.js, after importing a component we must also export it.
I think you might be referring to the following lines in User.vue and wondering why UserDetail and UserEdit are imported into the file and then exported in the script export's components property:
import UserDetail from './UserDetail.vue';
import UserEdit from './UserEdit.vue';
export default {
components: {
appUserDetail: UserDetail,
appUserEdit: UserEdit
}
}
vue-loader expects the script export of a .vue file to contain the component's definition, which effectively includes a recipe to assemble the component's template. If the template contained other Vue components, the definition of the other components would need to be provided, otherwise known as component registration. As #Sumurai8 indicated, the import of the .vue files itself does not register the corresponding single-file-components; rather those components must be explicitly registered in the importer's components property.
For example, if App.vue's template contained <user /> and User.vue were defined as:
<template>
<div class="user">
<app-user-edit></app-user-edit>
<app-user-detail></app-user-detail>
</div>
</template>
<script>
export default {
name: 'user'
}
</script>
...the User component would be rendered blank, and you would see the following console errors:
[Vue warn]: Unknown custom element: <app-user-edit> - did you register the component correctly? For recursive components, make sure to provide the "name" option.
[Vue warn]: Unknown custom element: <app-user-detail> - did you register the component correctly? For recursive components, make sure to provide the "name" option.
demo 1
When Vue attempts to render <user /> inside App.vue's template, Vue doesn't know how to resolve the inner <app-user-detail> and <app-user-edit> because their component registrations are missing. The errors can be resolved by local component registration in User.vue (i.e., the components property shown above).
Alternatively, the errors can be resolved with global component registration of UserDetail and UserEdit, which would obviate the local registration in User.vue. Note that global registration must be done before creating the Vue instance. Example:
// main.js
import Vue from 'vue';
import UserDetail from '#/components/UserDetail.vue';
import UserEdit from '#/components/UserEdit.vue';
Vue.component('app-user-detail', UserDetail);
Vue.component('app-user-edit', UserEdit);
new Vue(...);
demo 2
Components in vue can be tricky. If you haven't yet, I would highly recommend reading the documentation on the vue.js website on how component registration works, specifically, as tony19 mentions global and local registration. The code example you show in your screenshot is actually doing a couple of things. For one, it is making the components available locally, and only locally (as in that .vue file) for use. In addition, it is making it available to the template as the key you provide in the components object, in this case, app-user-detail and app-user-edit instead of user-detail and user-edit.
Importantly, it should be mentioned that an import is not actually required for this component registration to function. You could have multiple components defined in a single file. The components key gives a way to identify what that component is using. So that import isn't required, so vue does require the components key to understand what you are using as a component, and what is just other code.
Finally, as some of the other answers have alluded to, the components key is not actually an export. The default signature of a vue component requires an export but this is not exporting the components listed under the components key. What it is doing is letting vue build in a top down manner. Depending on what the rest of your application setup looks like, you may be using single file components, or not. Either way, vue will start with the top level vue instance and work its way down through components, with the exception of global registration, no top level component knows which components are being used below it.
This means for vue to render things properly, each component has to include a reference to the extra components it uses. This reference is exported as part of the higher level component (in your case User.vue), but is not the component itself (UserDetail.vue).
So it may appear that vue requires a second export after import, but it is actually doing something else to allow the root vue instance to render your component.
As an aside, the vue documentation on this subject really is quite good, if you haven't already please take a look at the sections I linked above. There is an additional section on module import/export systems that seems highly relevant to what you are asking, you can find that here: Module-systems.
import imports code into the current file, but it does not do anything on its own. Imagine the following non-vue code:
// File helpers.js
export function tickle(target) {
console.log(`You tickle ${target}`)
}
// File main.js
import { tickle } from 'helpers'
You have imported the code, but it does not do anything. To actually tickle something, you need to call the function.
tickle('polar bear');
In Vue this works the same. You define a component (or actually just an Object), but the component does not do anything on it's own. You export this component so you can import it in other places where the Vue library can do something with this object.
In a Vue component you export your current component, and import components you use in your template. You generally do the following:
<template>
<div class="my-component">
<custom-button color="red" value="Don't click me" #click="tickle" />
</div>
</template>
<script>
import CustomButton from './CustomButton';
export default {
name: 'my-component',
components: {
CustomButton
}
}
</script>
Your component mentions a component named "custom-button". This is not a normal html element. It does not know what to do with it normally. So what do we do? We import it, then put it in components. This maps the name CustomButton to the component you imported. It now knows how to render the component.
The "magic" happens when you mount the root component using Vue, usually in your main.js.
import Vue from "vue";
import App from "./App";
Vue.config.productionTip = false;
/* eslint-disable no-new */
new Vue({
el: "#app",
components: { App },
template: "<App/>"
});
What does this do? You tell Vue to render <App/> in a html element identified by #app, and you tell that it should find this element in ./App.vue.
But can't we just omit export if the Vue compiler was 'smarter'? Yes, and no. Yes, because a compiler can transform a lot of things into valid javascript, and no because it makes no sense and severely limits what you can do with your component, while also making the compiler more bug-prone, less understandable and overall less useful.
In order for App.vue to be able to use the User-component you need to export the default object of the User.vue-file.
In the export default { you don't actually export the newly imported components. You are just exporting a completely normal JavaScript Object. This object just happens to have a reference to another Object.
When you import an object (or function or array or ...) it does not actually load the content of that file in to your component like PHP. It simply makes sure that your compiler (probably webpack) knows how to structure the program. It basically creates a reference so webpack knows where to look for functionality.
TL;DR
The import and export here are conceptually different and unrelated things, and both have to be used.
Importing a Vue component is the same with any other importing in JavaScript:
// foo.mjs
export function hello() {
return "hello world!";
}
// bar.mjs
import { hello } from './foo.mjs';
console.log(hello());
Now run node bar.mjs, you will get a feeling how the importing works -- you want to use something that is defined/implemented somewhere else, then you have to import it, regardless of whether it is a Vue component or not.
With regard to export, you are not exporting the components you imported. The only thing you are exporting is the current component. However, this current component may use some other subcomponents in its <template>, so one has to register those subcomponents, by specifying them in the components field in the exported object.

Custom js library(scrollMonitor) inside main Vue instance to be shared with inner components

This is Vue.js question, generally I'm trying to use 'scrollMonitor' function inside of my .vue instance(imported via main.js) but it gives me a typical 'this.scrollMonitor is not a function' error
mounted () {
let watcher = this.$scrollMonitor(this.$refs.nicer)
}
In main.js ScrollMonitor library seems to be properly imported(console shows what's expected):
import scrollMonitor from 'scrollmonitor'
Vue.use(scrollMonitor)
console.log(scrollMonitor)
Again main goal is using scrollMonitor functionality inside of .vue file(in vue component instance). Sorry if I'm missing something silly here - I'm already using some other libraries like Vue-Resource in that file so issue is not in 'filepath' but rather in the way I'm using scrollMonitor functionality, any help is much appreciated, thank you !
For those who are still looking: there is a way of adding plain js libraries to the main.js and then using them with ease globally in inner components(this is not about mixins):
import scrollmonitor from 'scrollmonitor'
Object.defineProperty(Vue.prototype, '$scrollmonitor', {
get() {return this.$root.scrollmonitor}
})
also it should be added to main Vue data object:
data () {
return { scrollmonitor }
},
And then it can be used within mounted() callback (not created() one) inside of the component itself, with scrollmonitor it may look like this(in my specific case the template had a div with ref="nicer" attribute, 'create' is a method specific to the library api):
mounted () {
this.$scrollmonitor.create(this.$refs.nicer)
}
Hooray, I hope someone may find this useful as I did!
Are you using a plain javascript library and trying to Vue.use it? That won't really work. Vue.use will only work with plugins designed to work with Vue. Import the library into the component that needs and and just use it there.
scrollMonitor(this.$refs.nicer)