Routing

Similarly to Nuxt, we define a new page by creating a new +Page.vue file.

<!-- /pages/index/+Page.vue -->
<!-- Environment: browser and server -->
 
<template>
  This page is rendered to HTML and interactive: <Counter />
</template>
 
<script setup>
import Counter from '../../components/Counter.vue'
</script>

By default, Vike does Filesystem Routing.

FILESYSTEM                  URL
/pages/index/+Page.vue      /
/pages/about/+Page.vue      /about

We can also define a page's route with a Route String (for parameterized routes such as /movies/@id) or a Route Function (for full programmatic flexibility).

// /pages/index/+route.js
 
// Note how the two files share the same folder `/pages/index/`; this is how Vike
// knows that `/pages/index/+route.js` defines the route of `/pages/index/+Page.vue`.
 
// Route Function
export default pageContext => pageContext.urlPathname === '/'
 
// If we don't create a `+route.js` file then Vike does Filesystem Routing

Render Control

Unlike Nuxt, we control how our pages are rendered.

// /renderer/+onRenderHtml.js
// Environment: server
 
import { createSSRApp, h } from 'vue'
import { renderToString } from '@vue/server-renderer'
import { escapeInject, dangerouslySkipEscape } from 'vike/server'
 
export { onRenderHtml }
 
async function onRenderHtml(pageContext) {
  const { Page, data } = pageContext
  const app = createSSRApp({
    render: () => h(Page, data)
  })
 
  const appHtml = await renderToString(app)
 
  const title = 'Vite SSR'
 
  return escapeInject`<!DOCTYPE html>
    <html>
      <head>
        <title>${title}</title>
      </head>
      <body>
        <div id="app">${dangerouslySkipEscape(appHtml)}</div>
      </body>
    </html>`
}
// /renderer/+onRenderClient.js
// Environment: browser
 
import { createSSRApp, h } from 'vue'
 
export { onRenderClient }
 
async function onRenderClient(pageContext) {
  const { Page, data } = pageContext
  const app = createSSRApp({
    render: () => h(Page, data)
  })
  app.mount('#app')
}

You may see import { createSSRApp, h } from 'vue' for the first time, but every SSR app actually uses createSSRApp and h.

Nuxt abstracts this away from us and gives us a slightly faster getting started. But this also means that we lose control over a central piece of our app architecture and we'll eventually lose more time circumventing Nuxt's limiting black box.

This control enables us to easily and naturally integrate any tool we want (Vuex, GraphQL, Service Worker, ...).

Note how we defined the files onRenderClient.js and onRenderHtml.js in a directory called /renderer/: that way, we tell Vike to apply the onRenderHtml() and onRenderClient() hooks to all our pages.

This means we can now create a new page just by defining a new +Page.jsx file (the +route.js file is optional).

Plus files in /renderer/ can be overridden. For example, we can override the onRenderHtml() and onRenderClient() hooks for rendering some of our pages with a completely different UI framework such as React.

Data Fetching

Let's now have a look at how to fetch data.

<!-- /pages/star-wars/movie/+Page.vue -->
<!-- Environment: browser and server -->
 
<template>
  <h1>{{movie.title}}</h1>
  <p>Release Date: {{movie.release_date}}</p>
  <p>Director: {{movie.director}}</p>
</template>
 
<script lang="js">
const data = ['movie']
export default { props: data }
</script>
// /pages/star-wars/movie/+route.js
// Environment: server
 
// Route String
export default '/star-wars/@movieId'
// /pages/star-wars/movie/+data.js
// Environment: server
 
import fetch from 'node-fetch'
 
export async function data(pageContext) {
  // The route parameter of `/star-wars/@movieId` is available at `pageContext.routeParams`
  const { movieId } = pageContext.routeParams
 
  // By default, +data.js files are loaded and executed only on the server-side
  // which means we can use SQL/ORM queries here.
  const response = await fetch(`https://star-wars.brillout.com/api/films/${id}.json`)
  let movie = await response.json()
 
  // Our render and hydrate functions we defined earlier pass `pageContext.data` to
  // the root Vue component `Page`; this is where we define it.
  return {
    movie
  }
}

That's it for the tour and we have actually already seen most of Vike's interface; not only is Vike flexible but it's also simple to use!