From a48b28f83695d0616666d95404431831cd27b68e Mon Sep 17 00:00:00 2001 From: Stanislav Lashmanov Date: Tue, 26 Oct 2021 16:07:03 +0300 Subject: [PATCH 1/8] Remove app.js --- src/guide/ssr/routing.md | 77 +++++++++++++++++++++----------------- src/guide/ssr/server.md | 5 ++- src/guide/ssr/structure.md | 73 +++++++++++++++++------------------- 3 files changed, 79 insertions(+), 76 deletions(-) diff --git a/src/guide/ssr/routing.md b/src/guide/ssr/routing.md index 05fe4575f9..fe08b7551a 100644 --- a/src/guide/ssr/routing.md +++ b/src/guide/ssr/routing.md @@ -8,58 +8,60 @@ It is recommended to use the official [vue-router](https://github.com/vuejs/vue- ```js // router.js -import { createRouter, createMemoryHistory, createWebHistory } from 'vue-router' +import { createRouter } from 'vue-router' import MyUser from './components/MyUser.vue' -const isServer = typeof window === 'undefined' - -const createHistory = isServer ? createMemoryHistory : createWebHistory - const routes = [{ path: '/user', component: MyUser }] -export default function() { +export default function (history) { return createRouter({ - history: createHistory(), - routes + history, + routes, }) } ``` -And update our `app.js`, client and server entries: +And update our client and server entries: + +```js +// entry-client.js +import { createApp } from 'vue' +import { createWebHistory } from 'vue-router' +import creteRouter from './router.js' +import App from './App.vue' + +// ... + +const app = createApp(App) + +const router = createRouter(createWebHistory()) + +app.use(router) + +// ... +``` ```js -// app.js +// entry-server.js import { createSSRApp } from 'vue' +// server router uses a different history from the client one +import { createMemoryHistory } from 'vue-router' +import creteRouter from './router.js' import App from './App.vue' -import createRouter from './router' -export default function(args) { - const app = createSSRApp(App) - const router = createRouter() +export default async function () { + const app = createSSRApp(Vue) + const router = createRouter(createMemoryHistory()) app.use(router) return { app, - router + router, } } ``` -```js -// entry-client.js -const { app, router } = createApp({ - /*...*/ -}) -``` - -```js -// entry-server.js -const { app, router } = createApp({ - /*...*/ -}) -``` - ## Code-Splitting Code-splitting, or lazy-loading part of your app, helps reduce the size of assets that need to be downloaded by the browser for the initial render, and can greatly improve TTI (time-to-interactive) for apps with large bundles. The key is "loading just what is needed" for the initial screen. @@ -73,19 +75,24 @@ const routes = [{ path: '/user', component: MyUser }] // to this: const routes = [ - { path: '/user', component: () => import('./components/MyUser.vue') } + { path: '/user', component: () => import('./components/MyUser.vue') }, ] ``` -On both client and server we need to wait for router to resolve async route components ahead of time in order to properly invoke in-component hooks. For this we will be using [router.isReady](https://next.router.vuejs.org/api/#isready) method Let's update our client entry: +On both client and server we need to wait for the router to resolve async route components ahead of time in order to properly invoke in-component hooks. For this we will be using [router.isReady](https://next.router.vuejs.org/api/#isready) method Let's update our client entry: ```js // entry-client.js -import createApp from './app' +import { createApp } from 'vue' +import { createWebHistory } from 'vue-router' +import creteRouter from './router.js' +import App from './App.vue' -const { app, router } = createApp({ - /* ... */ -}) +const app = createApp(App) + +const router = createRouter(createWebHistory()) + +app.use(router) router.isReady().then(() => { app.mount('#app') diff --git a/src/guide/ssr/server.md b/src/guide/ssr/server.md index 0a8b304c04..8f38e7d4ce 100644 --- a/src/guide/ssr/server.md +++ b/src/guide/ssr/server.md @@ -2,13 +2,14 @@ The [code structure](./structure.html) and [webpack configuration](./build-config.html) we've described also require some changes to our Express server code. -- we need to create an application with a built `app.js` from the resulting bundle. A path to it can be found using the webpack manifest: +- we need to create an application with a built `entry-server.js` from the resulting bundle. A path to it can be found using the webpack manifest: ```js // server.js const path = require('path') const manifest = require('./dist/server/ssr-manifest.json') + // the 'app.js' name is taken from the name of the entrypoint with an added `.js` postfix const appPath = path.join(__dirname, './dist', 'server', manifest['app.js']) const createApp = require(appPath).default ``` @@ -78,7 +79,7 @@ server.use( ) server.get('*', async (req, res) => { - const { app } = await createApp() + const { app } = createApp() const appContent = await renderToString(app) diff --git a/src/guide/ssr/structure.md b/src/guide/ssr/structure.md index 1fe86d676a..8770625ee4 100644 --- a/src/guide/ssr/structure.md +++ b/src/guide/ssr/structure.md @@ -4,31 +4,26 @@ When writing client-only code, we can assume that our code will be evaluated in a fresh context every time. However, a Node.js server is a long-running process. When our code is first imported by the process, it will be evaluated once and then stay in memory. This means that if you create a singleton object, it will be shared between every incoming request, with the risk of cross-request state pollution. -Therefore, we need to **create a new root Vue instance for each request.** In order to do that, we need to write a factory function that can be repeatedly executed to create fresh app instances for each request: +Therefore, we need to **create a new root Vue instance for each request.** In order to do that, we need to write a factory function that can be repeatedly executed to create fresh app instances for each request, so our server code now becomes: ```js -// app.js +// server.js const { createSSRApp } = require('vue') +const { renderToString } = require('@vue/server-renderer') +const express = require('express') + +const server = express() function createApp() { return createSSRApp({ data() { return { - user: 'John Doe' + user: 'John Doe', } }, - template: `
Current user is: {{ user }}
` + template: `
Current user is: {{ user }}
`, }) } -``` - -And our server code now becomes: - -```js -// server.js -const { renderToString } = require('@vue/server-renderer') -const server = require('express')() -const { createApp } = require('src/app.js') server.get('*', async (req, res) => { const app = createApp() @@ -49,7 +44,7 @@ server.get('*', async (req, res) => { server.listen(8080) ``` -The same rule applies to other instances as well (such as the router or store). Instead of exporting the router or store directly from a module and importing it across your app, you should create a fresh instance in `createApp` and inject it from the root Vue instance. +The same rule applies to other instances as well (such as the router or store). Instead of exporting the router or store directly from a module and importing it across your app, you should create a fresh instance in `createApp` and inject it from the root Vue instance each time a new request is made. ## Introducing a Build Step @@ -76,42 +71,43 @@ src ├── components │ ├── MyUser.vue │ └── MyTable.vue -├── App.vue -├── app.js # universal entry +├── App.vue # the root of your application ├── entry-client.js # runs in browser only └── entry-server.js # runs on server only ``` -### `app.js` +### `App.vue` -`app.js` is the universal entry to our app. In a client-only app, we would create the Vue application instance right in this file and mount directly to DOM. However, for SSR that responsibility is moved into the client-only entry file. `app.js` instead creates an application instance and exports it: +You may have noticed we now have a file called `App.vue` in the root of our `src` folder. That's where the root component of your application will be stored. We can now safely move the application code from `server.js` to the `App.vue` file: -```js -import { createSSRApp } from 'vue' -import App from './App.vue' - -// export a factory function for creating a root component -export default function(args) { - const app = createSSRApp(App) +```vue + - return { - app - } + ``` ### `entry-client.js` -The client entry creates the application using the root component factory and mounts it to the DOM: +The client entry creates the application using the `App.vue` component and mounts it to the DOM: ```js -import createApp from './app' +import { createSSRApp } from 'vue' +import App from './App.vue' // client-specific bootstrapping logic... -const { app } = createApp({ - // here we can pass additional arguments to app factory -}) +const app = createSSRApp(App) // this assumes App.vue template root element has `id="app"` app.mount('#app') @@ -122,15 +118,14 @@ app.mount('#app') The server entry uses a default export which is a function that can be called repeatedly for each render. At this moment, it doesn't do much other than returning the app instance - but later we will perform server-side route matching and data pre-fetching logic here. ```js -import createApp from './app' +import { createSSRApp } from 'vue' +import App from './App.vue' -export default function() { - const { app } = createApp({ - /*...*/ - }) +export default function () { + const app = createSSRApp(Vue) return { - app + app, } } ``` From 98f595c10f775739f49977fac3a33eaa4199be4a Mon Sep 17 00:00:00 2001 From: Stanislav Lashmanov Date: Tue, 26 Oct 2021 17:05:25 +0300 Subject: [PATCH 2/8] Fix typos, code style Co-authored-by: Natalia Tepluhina --- src/guide/ssr/routing.md | 2 +- src/guide/ssr/structure.md | 8 ++++---- 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/src/guide/ssr/routing.md b/src/guide/ssr/routing.md index fe08b7551a..99d868a434 100644 --- a/src/guide/ssr/routing.md +++ b/src/guide/ssr/routing.md @@ -85,7 +85,7 @@ On both client and server we need to wait for the router to resolve async route // entry-client.js import { createApp } from 'vue' import { createWebHistory } from 'vue-router' -import creteRouter from './router.js' +import createRouter from './router.js' import App from './App.vue' const app = createApp(App) diff --git a/src/guide/ssr/structure.md b/src/guide/ssr/structure.md index 8770625ee4..f69f8a000e 100644 --- a/src/guide/ssr/structure.md +++ b/src/guide/ssr/structure.md @@ -18,10 +18,10 @@ function createApp() { return createSSRApp({ data() { return { - user: 'John Doe', + user: 'John Doe' } }, - template: `
Current user is: {{ user }}
`, + template: `
Current user is: {{ user }}
` }) } @@ -90,9 +90,9 @@ export default { name: 'App', data() { return { - user: 'John Doe', + user: 'John Doe' } - }, + } } ``` From 445261664ca3613cee41eafd4a4996e4d824d1bb Mon Sep 17 00:00:00 2001 From: Stanislav Lashmanov Date: Tue, 26 Oct 2021 17:08:23 +0300 Subject: [PATCH 3/8] Fix typos --- src/guide/ssr/routing.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/guide/ssr/routing.md b/src/guide/ssr/routing.md index 99d868a434..aa5cc5c969 100644 --- a/src/guide/ssr/routing.md +++ b/src/guide/ssr/routing.md @@ -27,7 +27,7 @@ And update our client and server entries: // entry-client.js import { createApp } from 'vue' import { createWebHistory } from 'vue-router' -import creteRouter from './router.js' +import createRouter from './router.js' import App from './App.vue' // ... @@ -46,7 +46,7 @@ app.use(router) import { createSSRApp } from 'vue' // server router uses a different history from the client one import { createMemoryHistory } from 'vue-router' -import creteRouter from './router.js' +import createRouter from './router.js' import App from './App.vue' export default async function () { From f6c394e5be72d2d09932213318e3ffb9ae4b1a7b Mon Sep 17 00:00:00 2001 From: Stanislav Lashmanov Date: Tue, 26 Oct 2021 17:09:37 +0300 Subject: [PATCH 4/8] Fix code style --- src/guide/ssr/structure.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/guide/ssr/structure.md b/src/guide/ssr/structure.md index f69f8a000e..7c1095be20 100644 --- a/src/guide/ssr/structure.md +++ b/src/guide/ssr/structure.md @@ -125,7 +125,7 @@ export default function () { const app = createSSRApp(Vue) return { - app, + app } } ``` From 355e9799dd9a4810d3f64084a32e0d9fb3974046 Mon Sep 17 00:00:00 2001 From: Stanislav Lashmanov Date: Tue, 26 Oct 2021 17:11:33 +0300 Subject: [PATCH 5/8] Fix code style --- src/guide/ssr/routing.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/guide/ssr/routing.md b/src/guide/ssr/routing.md index aa5cc5c969..cf7fb9ba49 100644 --- a/src/guide/ssr/routing.md +++ b/src/guide/ssr/routing.md @@ -75,7 +75,7 @@ const routes = [{ path: '/user', component: MyUser }] // to this: const routes = [ - { path: '/user', component: () => import('./components/MyUser.vue') }, + { path: '/user', component: () => import('./components/MyUser.vue') } ] ``` From 9bd137dbd688ac10ea942d3bc5bcc5a27d816fdb Mon Sep 17 00:00:00 2001 From: Stanislav Lashmanov Date: Tue, 26 Oct 2021 17:12:01 +0300 Subject: [PATCH 6/8] Fix code style --- src/guide/ssr/routing.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/guide/ssr/routing.md b/src/guide/ssr/routing.md index cf7fb9ba49..7b8a84c95f 100644 --- a/src/guide/ssr/routing.md +++ b/src/guide/ssr/routing.md @@ -57,7 +57,7 @@ export default async function () { return { app, - router, + router } } ``` From 821087d0c77957ca4b8a47aded0519a6f3e2da23 Mon Sep 17 00:00:00 2001 From: Stanislav Lashmanov Date: Tue, 26 Oct 2021 17:12:29 +0300 Subject: [PATCH 7/8] Fix code style --- src/guide/ssr/routing.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/guide/ssr/routing.md b/src/guide/ssr/routing.md index 7b8a84c95f..0d62575c9f 100644 --- a/src/guide/ssr/routing.md +++ b/src/guide/ssr/routing.md @@ -16,7 +16,7 @@ const routes = [{ path: '/user', component: MyUser }] export default function (history) { return createRouter({ history, - routes, + routes }) } ``` From 942df6ebe43e0790e1b48add5c21daa62771e30a Mon Sep 17 00:00:00 2001 From: Stanislav Lashmanov Date: Tue, 26 Oct 2021 17:13:47 +0300 Subject: [PATCH 8/8] Fix wrong async function --- src/guide/ssr/routing.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/guide/ssr/routing.md b/src/guide/ssr/routing.md index 0d62575c9f..e52fd982ec 100644 --- a/src/guide/ssr/routing.md +++ b/src/guide/ssr/routing.md @@ -49,7 +49,7 @@ import { createMemoryHistory } from 'vue-router' import createRouter from './router.js' import App from './App.vue' -export default async function () { +export default function () { const app = createSSRApp(Vue) const router = createRouter(createMemoryHistory())