vikejs / vike

🔨 Flexible, lean, community-driven, dependable, fast Vite-based frontend framework.
https://vike.dev
MIT License
4.3k stars 348 forks source link

V1 Design #578

Closed brillout closed 10 months ago

brillout commented 1 year ago

The vite-plugin-ssr@1.0.0 design in a nutshell:

// /pages/product/+config.ts

import type { Config } from 'vite-plugin-ssr'

export default {
  Page: './Page.vue',
  route: '/product/@id',
  onBeforeRender: './onBeforeRender.ts',
  onPrerender: './onPrerender.ts',
  prerender: true,
  ssr: false // render as SPA
} satisfies Config // `satisfies` is a new TypeScript 4.9 operator

I.e. +config.js replaces .page.js, .page.server.js, .page.client.js, and .page.route.js.

It also replaces _default.page.js as well as VPS configurations set in vite.config.js:

// /pages/+config.ts

import type { Config } from 'vite-plugin-ssr'

// Applies as default to all pages
export default {
  onRenderHtml: './config/onRenderHtml.tsx',
  onRenderClient: './config/onRenderClient.tsx',
  prerender: {
    partial: true // Currently lives in vite.config.js
  },
  ssr: true
} satisfies Config

VPS's architecture stays the same and therefore migration is relatively simple.

Succint syntax

Under consideration is the possibility to create new pages in a succint manner:

/pages/index/+Page.js
/pages/about/+Page.js
/pages/jobs/+Page.js

Here, a new +config.js file isn't needed each time a new page is created.

I'm currently leaning towards not supporting such succint syntax (in favor of "Single Config File", see next section). But I may reconsider if users want this.

Single Config File (aka Single Route File)

// /pages/+config.ts

import type { Config } from 'vite-plugin-ssr'

const marketingPagesConfig = {
  Layout: './layouts/LayoutMarketingPages.vue',
  ssr: true,
}
const adminPagesConfig = {
  title: 'Admin Panel',
  Layout: './layouts/LayoutAdminPages.vue',
  ssr: false
}

export default {
  // If `singleConfigFile: true` then only one `+` file is allowed (this file). If there is
  // anothoer `+` file, then VPS displays a warning.
  singleConfigFile: true,
  pages: [
    { ...marketingPagesConfig, Page: './LandingPage.vue', route: '/'        , title: 'Awesome Startup'                          },
    { ...marketingPagesConfig, Page:    './JobsPage.vue', route: '/jobs'    , title: "We're hiring!"                            },
    { ...marketingPagesConfig, Page:  './VisionPage.vue', route: '/vision'  , title: 'Our mission <3'                           },
    {     ...adminPagesConfig, Page:   './AdminPage.vue', route: '/admin'   , onBeforeRender:   './AdminPage-onBeforeRender.ts' },
    {     ...adminPagesConfig, Page: './AdminDbPage.vue', route: '/admin/db', onBeforeRender: './AdminDbPage-onBeforeRender.ts' }
  ]
} satisfies Config

The singleConfigFile enforces the entire app to be defined in that single +config.js file. This means that this single +config.js file represents the entire interface between VPS and your app.

Nested Views

(Aka "Nested Layouts", but "Nested Views" is a better name for this design.)

// /pages/product/+config.js

export default {
  Page: './Page.js',
  onBeforeRender: './onBeforeRender.js',
  route: '/product/@id',
  nested: [
    {
      route: '/product/@id/details',
      View: './DetailsView.js'
    },
    {
      route: '/product/@id/reviews',
      View: './ReviewsView.js',
      onBeforeRender: './onBeforeRenderReviewsView.js'
    }
  ]
}

This example doesn't use singleConfigFile (otherwise nested would be defined in /pages/+config.js instead).

Custom Exports

The V1 design requires Custom Exports to be registered.

// /pages/+config.ts

import type { Config } from 'vite-plugin-ssr'

export default {
  configDefinitions: [
    {
      name: 'title',
      env: 'SERVER_ONLY' // | 'CLIENT_ONLY' | 'CLIENT_AND_SERVER'
    }
  ]
} satisfies Config
// /pages/index/+title.ts

// The file is loaded only in Node.js. (At server run-time or at build-time while pre-rendering.)
export const title = 'Welcome to the V1 design'

More

The V1 design unlocks many more capabilities, in particular around building frameworks on top of VPS. See end of this comment.

This is a very exciting design. (In many subtle ways, you'll see when you'll use a VPS framework.)

Feedback

(Negative) feedback welcome.

Acknowledgement

🙏 @AaronBeaudoin for the fruitful conversation.

redbar0n commented 1 year ago

It’s intuitively understood by everyone that a custom config overrides a default config. It’s pretty much a standard.

Sometimes it’s the things we think about at first that are the best solutions, because that will also be what others intuitively think first. :-)

phiberber commented 1 year ago

Tailwind had that problem with theme replacing the default config instead of modifying it, they sure had a lot of questions because people weren't used to the extend prefix.

Not sure how much the extend prefix could be useful here, although it doesn't seem to me that it's really a config, the current state of the meta parameter looks to me more like route props than a config itself.

If I'm not wrong with the above thought, I'd say meta is better than extendConfig or customConfig, although in that sense I think props would be a better name.

redbar0n commented 1 year ago

Just saw this. It gives some precedence and inspiration:

https://remix.run/docs/en/main/file-conventions/remix-config#routes

remix.config.js

A function for defining custom routes, in addition to those already defined using the filesystem convention in app/routes. Both sets of routes will be merged.

brillout commented 10 months ago

The V1 design is out of beta and now the official design.

brillout commented 10 months ago

#1322: [Filesystem Routing] Avoid samed named files (e.g. 10 filenames +Page.tsx)