How can you module.export a single object? - javascript

I'm trying to map the pages from the config.js file to be used within my nav.js component but am having trouble trying to access the data from the config.js file. When I check the export module in the console, it returns as undefined and I have no idea where I am going wrong. I have tried default export and module.exports so far, I'm not sure if I'm just misunderstanding the whole concept entirely.
Config.js file
module.exports = {
pages: {
home: {
link: '/',
title: 'Home'
},
about: {
link: '/about',
title: 'About'
},
workshops: {
link: '/workshops',
title: 'Workshops'
},
contact: {
link: '/contact',
title: 'Contact'
}
},
info: {
company: 'Roadworks Media'
}
}
nav.js file
import Link from 'next/link'
import config from '../data/config'
export default function Nav() {
return (
<nav role="menubar" aria-expanded='false' className="hidden md:block">
{config.pages.map((page, index) => {
return <Link key={index} href={page.link}><a className="px-4" aria-label={page.title}>{page.title}</a></Link>
})}
</nav>
)
}

Related

Vue3: Nested Routes and Dynamic Layout component

Hi Vue enthusiasts out there,
I have been working on an multi-tenant application and stuck at dynamic layout problem.
Requirement: Load tenant specific layout.vue file from public folder and wrap <router-view> around it.
Tried few things like dynamic imports, defineAsyncComponent etc but couldn't get it working.
// router:
import store from '../store/index';
import NestedApp from '../views/NestedApp.vue';
// const layoutA = () => defineAsyncComponent(import(store.getters.pageLayout('LayoutA')));
const routes = [
{
path: '/:tenant:/:locale',
name: 'NestedApp',
component: NestedApp,
children: [
{
path: 'about',
name: 'About',
component: () => import(/* webpackChunkName: "about" */ '../views/About.vue'),
meta: { layout: () => import(store.getters.pageLayout('LayoutA')) }
}
]
]
// NestedApp.vue:
<template>
<div class="NestedApp">
<navbar/>
<component :is="layoutWrapper">
<router-view/>
</component>
</div>
</template>
<script>
import Navbar from '../components/Navbar.vue';
export default {
name: 'NestedApp',
components: {
Navbar,
},
computed: {
layoutWrapper() {
console.info(`layout: ${this.$route.meta.layout}`);
return this.$route.meta.layout || 'div';
}
}
}
// LayoutA.vue:
<template>
<div class="LayoutA">
<span>Layout A</span>
<slot/>
</div>
</template>
I get following error in browser console:
Got a workaround to this problem.
Sending component via template string from backend API call and then creating a component out of it via defineComponent and markRaw methods.
API response:
"Layouts": {
"LayoutA": {
"name": "LayoutAbout",
"template": "<div class='LayoutA' style='background-color: darkgray'><span>Layout A</span><slot/></div>"
}
},
and then use in App.vue:
import { defineComponent, markRaw } from 'vue';
export default {
name: 'App',
methods: {
loadLayout(pageLayout) {
const layout = this.$store.getters.pageLayout(pageLayout);
this.layoutWrapper = layout ? defineComponent(markRaw({...layout})) : 'div';
}
},
created() {
this.loadLayout(this.$route.meta.layout);
},
beforeRouteUpdate(to) {
this.loadLayout(to.meta.layout);
},
}
<template>
<div class="App">
<navbar/>
<component :is="layoutWrapper">
<router-view/>
</component>
</div>
</template>

GatsbyJS file is null - setting relativePath doesn't work

I'm attempting to pull an image: /src/images/es.png
And to display it on a gatsby page. Here's the page code:
import React from "react"
import {
Header,
Image,
} from 'semantic-ui-react';
import { graphql } from 'gatsby'
import Img from 'gatsby-image'
import Layout from '#/components/layout'
import SEO from '#/components/seo'
export const query = graphql`
query {
file(
relativePath: { eq: "images/es.png" },
sourceInstanceName: {
eq: "images"
}
) {
childImageSharp {
# Specify the image processing specifications right in the query.
# Makes it trivial to update as your page's design changes.
fixed(width: 125, height: 125) {
...GatsbyImageSharpFixed
}
}
}
}
`
export default ({ data }) => (
<Layout>
<SEO title="Earthshaker" />
<div style={{ height: '100%' }}>
<Header
as="h1"
style={{
color: 'white',
}}
>
Earthshaker
</Header>
{ JSON.stringify(data) }
{/* <Img fixed={data.file.childImageSharp.fixed} /> */}
</div>
</Layout>
)
Here's the config code:
plugins: [
{
resolve: `gatsby-source-filesystem`,
options: {
name: `images`,
path: path.join(__dirname, `src`, `images`),
},
},
`gatsby-transformer-sharp`,
`gatsby-plugin-sharp`,
Here's my file structure:
- src
- images
- es.png
- components
- heroes
- earthshaker <---- (this is the page code HERE)
I'm expecting the image to get pulled out but I always get file: null. What am I doing wrong?
Assuming that your query works, I'd change your filesystem paths to:
{
resolve: `gatsby-source-filesystem`,
options: {
name: `images`,
path: `${__dirname}/src/images/`,
},
},
In addition, I would check the query to see if it gathers the expected result in the GraphQL playground (localhost:8000/___graphql). The rest of the code looks fine.

Vue js Prefetch components

I recently learnt about lazy loading components and started using it. Now I am trying to prefetch the lazy loaded components as well as vue-router routes. But using the chrome devtools I found that lazy loaded chunks are only loaded when we actually navigate to the lazy loaded route (in case of a vue-router route) or when the v-if evaluates to true and the component is rendered (in case of a lazy loaded component).
I have also tried using the webpackPrefetch: true magic string in the router as well as component import statement but doing that does not seem to make any difference.
Project structure:
Master-Detail layout
router config:
import Vue from "vue";
import Router from "vue-router";
Vue.use(Router);
var routes = [
{
path: "/DetailPage",
component: () => import(/* webpackChunkName: "Detail-chunk" */ "AppModules/views/MyModuleName/DetailPage.vue")
},
{
path: "/MasterPage",
component: () => import("AppModules/views/MyModuleName/MasterPage.vue")
}
]
export const router = new Router({
routes: routes,
stringifyQuery(query) {
// encrypt query string here
}
});
export default router;
Master view:
<template>
<div #click="navigate">
Some text
</div>
</template>
<script>
export default {
name: "MasterPage",
methods: {
navigate() {
this.$router.push({
path: "/DetailPage",
query: {},
});
},
},
};
</script>
Details page:
<template>
<div>
<my-component v-if="showComponent" />
<div #click="showComponent = true">Show Component</div>
</div>
</template>
<script>
const MyComponent = () => import(/* webpackChunkName: "MyComponent-chunk" */ "AppCore/components/AppElements/Helpers/MyComponent");
export default {
name: "DetailPage",
components: {
MyComponent,
},
data() {
return {
showComponent: false
}
}
};
</script>
vue.js.config file:
const path = require("path");
const BundleAnalyzerPlugin = require("webpack-bundle-analyzer")
.BundleAnalyzerPlugin;
module.exports = {
publicPath: "some-url",
outputDir: "./some/path",
chainWebpack: webapckConfig => {
webapckConfig.plugin("html").tap(() => {
return [
{
inject: true,
filename: "index.html",
template: "./public/index.html"
}
];
});
},
productionSourceMap: true,
configureWebpack: {
plugins: [
new BundleAnalyzerPlugin({
analyzerMode: "server",
generateStatsFile: false,
statsOptions: {
excludeModules: "node_modules"
}
})
],
output: {
filename: "some file name",
libraryTarget: "window"
},
module: {
rules: [
{
test: /\.(woff|woff2)(\?v=\d+\.\d+\.\d+)?$/,
use: [
{
loader: "url-loader",
options: {
limit: 50000,
fallback: "file-loader",
outputPath: "/assets/fonts",
name: "[name].[ext]?hash=[hash]"
}
}
]
}
]
},
resolve: {
alias: {
vue$: process.env.NODE_ENV == 'production' ? 'vue/dist/vue.min.js' : 'vue/dist/vue.js',
AppCore: path.resolve(__dirname, "..", "..", "AppCoreLite"),
AppModules: path.resolve(__dirname, "..", "..", "AppModulesLite")
}
}
}
};
Both the async route and component do get split into separate chunks but these chunks are not prefetched.
When I navigate to the master view, I dont see Detail-chunk.[hash].js in the network tab. It gets requested only when the navigate method in the master page is executed (this the correct lazy load behaviour without prefetch).
Now when I am on the details page, MyComponent-chunk.[hash].js is only requested when the showComponent becomes true (on click of a button)
I've also read at a few places that vue-cli v3 does has prefetch functionality enabled by default and webpack magic string is not needed. I also tried that by removing the webpackPrefetch comment but it made no difference.
I did vue-cli-service inspect and found that prefetch plugin is indeed present in the webpack config:
/* config.plugin('preload') */
new PreloadPlugin(
{
rel: 'preload',
include: 'initial',
fileBlacklist: [
/\.map$/,
/hot-update\.js$/
]
}
),
/* config.plugin('prefetch') */
new PreloadPlugin(
{
rel: 'prefetch',
include: 'asyncChunks'
}
),
UPDATE: I tried removing the prefetch webpack plugin using config.plugins.delete('prefetch'); and then using the webpack magic comment: /* webpackPrefetch: true */ but it made no difference.
How do I implement prefetch functionality?
I solved this by creating a simple prefetch component that loads after a custom amount of time.
Prefetch.vue
<script>
import LazyComp1 from "./LazyComp1.vue";
import LazyComp2 from "./LazyComp2.vue";
export default {
components:{
LazyComp1,
LazyComp2,
}
}
</script>
App.vue
<template>
<Prefech v-if="loadPrefetch"></Prefech>
</template>
<script>
export default {
components: {
Prefech: () => import("./Prefetch");
},
data() {
return {
loadPrefetch: false
}
},
mounted() {
setTimeout(() => {
this.loadPrefetch = true;
}, 1000);
}
}
</script>
Lazy loaded components are meant to be loaded only when user clicks the route. If you want to load component before it, just don't use lazy loading.
vue-router will load components to memory and swap the content of the tag dynamically even if you will use normally loaded component.
You need to implement vue-router-prefetch package for your need. Here is a working demo.
Note: From the working demo, you can notice from console.log that only page 2 is prefetched by the QuickLink component imported from vue-router-prefetch
Code :
import Vue from "vue";
import Router from "vue-router";
import RoutePrefetch from "vue-router-prefetch";
Vue.use(Router);
Vue.use(RoutePrefetch, {
componentName: "QuickLink"
});
const SiteNav = {
template: `<div>
<ul>
<li>
<router-link to="/page/1">page 1</router-link>
</li>
<li>
<quick-link to="/page/2">page 2</quick-link>
</li>
<li>
<router-link to="/page/3">page 3</router-link>
</li>
</ul>
</div>`
};
const createPage = (id) => async() => {
console.log(`fetching page ${id}`);
return {
template: `<div>
<h1>page {id}</h1>
<SiteNav />
</div>`,
components: {
SiteNav
}
};
};
const routers = new Router({
mode: "history",
routes: [{
path: "/",
component: {
template: `<div>
<h1>hi</h1>
<SiteNav />
</div>`,
components: {
SiteNav
}
}
}]
});
for (let i = 1; i <= 3; i++) {
routers.addRoutes([{
path: `/page/${i + 1}`,
component: createPage(i + 1)
}]);
}
export default routers;
I'm working on a mobile app. and wanted to load some components dynamically while showing the splash screen.
#Thomas's answer is a good solution (a Prefetch component), but it doesn't load the component in the shadow dom, and Doesn't pass Vetur validation (each component must have its template)
Here's my code:
main.vue
<template>
<loader />
</template>
<script>
import Loader from './Loader'
const Prefetch = () => import('./Prefetch')
export default {
name: 'Main',
components: {
Loader,
Prefetch
}
}
</script>
Prevetch.vue
<template>
<div id="prefetch">
<lazy-comp-a />
<lazy-comp-b />
</div>
</template>
<script>
import Vue from 'vue'
import LazyCompA from './LazyCompA'
import LazyCompB from './LazyCompB'
Vue.use(LazyCompA)
Vue.use(LazyCompB)
export default {
components: {
LazyCompA,
LazyCompB
}
}
</script>
<style lang="scss" scoped>
#prefetch {
display: none !important;
}
</style>
The loader component is loaded & rendered, then the Prefetch component can load anything dynamically.
since vue-router-prefetch didn't work for me i ended up doing it manually.
Vue 3 Example - all routes are iterated on page load and async components are loaded
const router = createRouter({
history: createWebHistory(),
routes: [{
path: '/',
component: HomeView
}, {
path: '/about',
component: () => import('./views/AboutView.vue')
}]
});
async function preloadAsyncRoutes() {
// iterate all routes and if the component is async - prefetch it!
for (const route of router.getRoutes()) {
if (!route.components) continue;
// most routes have just a "default" component unless named views are used - iterate all entries just in case
for (const componentOrImporter of Object.values(route.components)) {
if (typeof componentOrImporter === 'function') {
try {
// prefetch the component and wait until it finishes before moving to the next one
await componentOrImporter();
} catch (err) {
// ignore failing requests
}
}
}
}
}
window.addEventListener('load', preloadAsyncRoutes);

VueJS passing router to child component

How Can I pass router to my child component.
I have this as my router
import Vue from 'vue'
import VueRouter from 'vue-router'
Vue.use(VueRouter)
export default function () {
const Router = new VueRouter({
mode: 'history',
routes : [
{
path: '/',
beforeEnter: ifAuthenticated,
component: () => {
return import('./../container/Index.vue')
}
},
{
path: '/login',
beforeEnter: ifNotAuthenticated,
component: () => {
return import('./../container/logn.vue')
}
}
],
})
return Router
}
Now my "/" (index.vue) route have a component Navbar and the Navbar have a logout button which logs out the user and redirect them to login page
Consider this to be my index.vue (with what I have done)
<template>
<q-layout>
<Navbar :thisInfo="routerAndStore"/>
</q-layout>
</template>
<script>
import Navbar from "./../components/navbar.vue";
export default {
name: "PageIndex",
components: {
Navbar
},
data() {
return {
routerAndStore: this
};
}
};
</script>
And then in my navbar.vue I have done something like this
<template>
<div class="nav-pages-main">
<a #click="logoutUser">
<h5>Logout</h5>
</a>
</div>
</template>
<script>
export default {
name: "navbar",
methods: {
logoutUser: () => {
return this.thisInfo.$store.dispatch("GOOGLE_PROFILE_LOGOUT").then(() => {
this.$router.push("/login");
});
}
},
props: {
thisInfo: {
type: Object
}
}
};
</script>
but this doesn't seem to be working (this is coming out to be undefined), So if someone can help me figure out how we can pass this to our child component
Please refer to Vue-Router official documentation here
Basically, in their use case, the main component (index.vue) take a router as argument and provide <router-view> in its template as placeholder for component that would be rendered based on the current route.
In your code, I see that you use it the other way around using router to render the main component.
routes : [
{
path: '/',
beforeEnter: ifAuthenticated,
component: () => {
return import('./../container/Index.vue')
}
},
...
]
Could you try it again using the right way described in the documentation and tell me the result?
Edit: According to the App.vue that you posted (assuming it's the app entry point) then you should provide router to the App component.
<template>
<div id="q-app"> <router-view/> </div>
</template>
<script>
import router from '/path/to/your/router';
export default { name: "App", router };
</script>
<style>
</style>
The full code for this can be found at Vue-Router example

How do you handle breadcrumbs with lazyloaded components using react router?

I have been looking for an example on how to handle breadcrumbs for routes that are lazy loaded. I have been able to get this to work correctly on the server, but haven't been able to get the lazy loaded component which is masked by getComponent() on the client. How do I get access to the async components?
routes.js
module.exports = {
component: Layout,
childRoutes: [
{
path: '/',
component: App,
childRoutes: [
{
path: '/lazy',
component: Lazy, // Lazy loaded component
}
]
}
]
};
Lazy.js
// Server polyfill
if (typeof require.ensure !== 'function') { require.ensure = function(d, c) { c(require) }; }
module.exports = {
path: 'detail',
getComponent: function(location, cb) {
require.ensure([], (require) => {
cb(null, require('./components/Detail')); //Component I want to access
});
}
};
Breadcrumbs.js
import React, { Component, PropTypes } from 'react';
class Breadcrumbs extends Component {
static propTypes = {
routes: PropTypes.array.isRequired
};
render() {
const { routes } = this.props;
return (
<ol className="breadcrumbs">
{routes.map((route, index) => {
return (
<li key={index}>{route.component.title}</li>
);
})}
</ol>
);
}
}
export default Breadcrumbs;
The problem is the async route looks like:
{
path: 'detail',
component: {
getComponent: function()
}
}
Instead of:
{
path: 'detail',
component: {
title: 'Lazy Detail'
}
}
Instead of what you have, I'd just put the title on the route object rather than the component - that should resolve your issues there and give you a bit more flexibility.
If you have to do this, you can use a custom RoutingContext, but this is a semi-private API that will potentially go away in v1.1.x. In general it'd seem cleaner to define this on the <Route>, though.

Categories

Resources