From eefde90bb37c20701e140834ee69f8b0f562cbbb Mon Sep 17 00:00:00 2001 From: Fran Dios Date: Sat, 13 Feb 2021 03:00:33 +0900 Subject: [PATCH] docs: Update docs and improve example API --- README.md | 5 ++--- example/node-server/api.js | 19 +++++++++++++++++++ example/node-server/index.js | 31 +++++++++++++++++++------------ example/src/main.js | 10 ++++++++++ example/vite.config.js | 21 ++++++++++++--------- 5 files changed, 62 insertions(+), 24 deletions(-) create mode 100644 example/node-server/api.js diff --git a/README.md b/README.md index a8e676d..8fe9967 100644 --- a/README.md +++ b/README.md @@ -17,7 +17,7 @@ Create a normal [Vite](https://github.com/vitejs/vite) app. 1. Add `vite-ssr` with your package manager (direct dependency). 2. Import `vite-ssr/plugin.js` in your `vite.config.js` file (see [`vite.config.js`](./example/vite.config.js) for an example). 3. You can import `vite-ssr/entry-client.js` or `vite-ssr/entry-server.js` depending on you environment. Or you can directly import from `vite-ssr` to get the corresponding handler according to the running environment (client or server). See an example in [`main.js`](./example/src/main.js). -4. Run `vite-ssr build` for buildling your app. Then, you can import the built file in your backend (see [`node-server/index.js`](./example/node-server/index.js) for an example). +4. Run `vite-ssr build` for buildling your app. Then, you can import the built files in your backend (see [`node-server/index.js`](./example/node-server/index.js) for an example). While rendering the first view, you can provide the initial state in `route.meta.state` and `vite-ssr` will take care of rehydration in the client. See [`main.js`](./example/src/main.js) for an example. @@ -39,6 +39,5 @@ Run `yarn refresh` for moving latest version of `core` to `example/node_modules` - ~~Export a build plugin so a project can be built using `vite build` (not sure if it's possible)~~ - [x] Make `src/main.js` file name configurable - [ ] Support build options in CLI (currently only configurable via JS API) -- Rethink router requirement (currently it relies on `vue-router`) -- Support React? (PRs welcome) +- [ ] Support React - Better docs diff --git a/example/node-server/api.js b/example/node-server/api.js new file mode 100644 index 0000000..b83e621 --- /dev/null +++ b/example/node-server/api.js @@ -0,0 +1,19 @@ +// Example API + +module.exports = [ + { + route: '/api/getProps', + handler(req, res) { + const url = new URL('http://e.c' + req.originalUrl) + console.log('getProps', url.searchParams.toString()) + res.end( + JSON.stringify({ + server: true, + msg: + 'This is page ' + + (url.searchParams.get('name') || '').toUpperCase(), + }) + ) + }, + }, +] diff --git a/example/node-server/index.js b/example/node-server/index.js index 4b7bfe4..b8211b8 100644 --- a/example/node-server/index.js +++ b/example/node-server/index.js @@ -1,13 +1,23 @@ +// This is a simple Node server that uses the built project. + global.fetch = require('node-fetch') const path = require('path') const express = require('express') +// This contains a list of static routes (assets) const { ssr } = require('../dist/server/package.json') + +// The manifest is required for preloading assets const manifest = require('../dist/client/ssr-manifest.json') -const { default: handler } = require('../dist/server') + +// This is the server renderer we just built +const { default: renderPage } = require('../dist/server') + +const api = require('./api') const server = express() +// Serve every static asset route for (const asset of ssr.assets || []) { server.use( '/' + asset, @@ -15,23 +25,20 @@ for (const asset of ssr.assets || []) { ) } -server.get('*', async (req, res) => { - if (req.path === '/api/getProps') { - console.log('getProps', req.query) - return res.end( - JSON.stringify({ - server: true, - msg: 'This is page ' + (req.query.name || '').toUpperCase(), - }) - ) - } +// Custom API to get data for each page +// See src/main.js to see how this is called +api.forEach(({ route, handler }) => server.get(route, handler)) +// Everything else is treated as a "rendering request" +server.get('*', async (req, res) => { const url = req.protocol + '://' + req.get('host') + req.originalUrl - const { html } = await handler({ + + const { html } = await renderPage({ request: { ...req, url }, manifest, preload: true, }) + res.end(html) }) diff --git a/example/src/main.js b/example/src/main.js index 634889c..94ebb13 100644 --- a/example/src/main.js +++ b/example/src/main.js @@ -20,6 +20,7 @@ export default viteSSR( // The 'initialState' is only available in the browser and can be used to // pass it to Vuex, for example, if you prefer to rely on stores rather than Page props. + // Before each route navigation we request the data needed for showing the page. router.beforeEach(async (to, from, next) => { if (process.env.NODE_ENV !== 'development' && to.meta.state) { // This route has state already (from server) so it can be reused. @@ -28,6 +29,15 @@ export default viteSSR( const baseUrl = isClient ? '' : new URL(request.url).origin + // Explanation: + // The first rendering happens in the server. Therefore, when this code runs, + // the server makes a request to itself (running the code below) in order to + // get the current page props and use that response to render the HTML. + // The browser shows this HTML and rehydrates the application, turning it into + // an normal SPA. After that, subsequent route navigation runs this code below + // from the browser and get the new page props, which is this time rendered + // directly in the browser, as opposed to the first page rendering. + try { // Get our page props from our custom API: const res = await fetch( diff --git a/example/vite.config.js b/example/vite.config.js index 0b86d0b..271d2a1 100644 --- a/example/vite.config.js +++ b/example/vite.config.js @@ -1,14 +1,17 @@ +const { defineConfig } = require('vite') const viteSSR = require('vite-ssr/plugin') const vue = require('@vitejs/plugin-vue') +const api = require('./node-server/api') -module.exports = { - plugins: [viteSSR(), vue()], - server: { - proxy: { - '/api': { - // This is the server in `node-site` directory - target: 'http://localhost:8080', +module.exports = defineConfig({ + plugins: [ + viteSSR(), + vue(), + { + // Mock API during development + configureServer({ app }) { + api.forEach(({ route, handler }) => app.use(route, handler)) }, }, - }, -} + ], +})