vite-ssg
vite-ssg copied to clipboard
Preload routes and meta information and store them in Pinia
Hi,
I'm trying to preload the routes from my graphql endpoint, so I can get the routes and meta information (page title, description, etc). My main.js:
import App from './App.vue';
import { ViteSSG } from 'vite-ssg';
import { router } from './router.js';
import { provideApolloClient } from '@vue/apollo-composable';
import { ApolloClient, InMemoryCache } from '@apollo/client/core';
import { useHead } from '@vueuse/head';
const cache = new InMemoryCache();
const apolloClient = new ApolloClient({
cache,
uri: 'https://localhost:44351/graphql/',
});
provideApolloClient(apolloClient);
import { createPinia } from 'pinia';
import { usePagesStore } from './stores/pages';
export const createApp = ViteSSG(
// the root component
App,
// vue-router options
{ routes: router },
// function to have custom setups
({ app, router, routes, isClient, initialState }) => {
const pinia = createPinia();
if (isClient) {
pinia.state.value = initialState.pinia || {};
} else {
onSSRAppRendered(() => {
initialState.pinia = pinia.state.value;
});
}
app.use(pinia);
router.beforeEach((to, from, next) => {
const pagesStore = usePagesStore(pinia);
if (!pagesStore.ready) {
pagesStore.initialize();
}
next();
let toPath = to.fullPath;
if (!toPath.endsWith('/')) {
toPath = toPath + '/';
}
var currentPage = pagesStore.getPageById(toPath);
if (currentPage != null) {
useHead({
title: currentPage.name,
meta: [
{
name: 'description',
content: currentPage.metaDescription[0].value.value,
},
],
});
}
});
provideApolloClient,
}
);
router.js:
import Home from '@/pages/Home.vue';
import About from '@/pages/About.vue';
import Blog from '@/pages/Blog.vue';
import BlogItem from '@/pages/BlogItem.vue';
export const router = [
{
path: '/',
name: 'Home',
component: Home,
},
{
path: '/about',
name: 'About',
component: About,
},
{
path: '/blogs',
name: 'Blog',
component: Blog,
},
{
path: '/blogs/:url',
name: 'BlogItem',
component: BlogItem,
},
];
pages.js store:
import { defineStore } from 'pinia';
export const usePagesStore = defineStore('pages', {
state: () => {
return { pages: [] };
},
actions: {
async initialize() {
await fetch('https://localhost:44351/graphql/', {
method: 'POST',
headers: {
'Content-Type': 'application/json',
},
body: JSON.stringify({
query: `
query {
contentAll {
nodes {
url
name
metaDescription: properties(
where: { alias: { eq: "metaDescription" } }
) {
value {
... on BasicPropertyValue {
value
}
}
}
}
}
}
`,
}),
})
.then((res) => res.json())
.then((result) => {
this.pages = result.data.contentAll.nodes;
});
},
},
getters: {
allPages: (state) => state.pages,
getPageById: (state) => {
return (pageUrl) =>
state.pages.find((page) => page.url === pageUrl);
},
},
});
vite.config.js:
import { defineConfig } from 'vite';
import vue from '@vitejs/plugin-vue';
import path from 'path';
// https://vitejs.dev/config/
export default defineConfig({
plugins: [vue()],
ssgOptions: {
script: 'async',
},
server: {
proxy: {
'/media': {
target: 'https://localhost:44351',
changeOrigin: true,
secure: false,
ws: true,
},
},
},
resolve: {
alias: {
'@': path.resolve(__dirname, './src'),
'~bootstrap': path.resolve(__dirname, 'node_modules/bootstrap'),
},
},
});
Now when the page loads, it loads the Pinia-store, but it's too late to load the initial data, but it works when I switch to another page. Once I hit F5 on any page, the pinia-store isn't loaded and it shows the default title from the index.html. I'd love to prerender all the routes in my pinia-store, but I don't know how to do this. I've also tried adjusting my app.vue and using serverPrefetch, but all to no avail.
When I build for production, none of the blogpages are created, since router.js doesn't know them yet.
Can somebody help me out?