Skip to content

Commit f9cdfe5

Browse files
authored
1 parent 5435808 commit f9cdfe5

File tree

10 files changed

+693
-11
lines changed

10 files changed

+693
-11
lines changed

src/.vuepress/config.js

Lines changed: 10 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -211,16 +211,16 @@ const sidebar = {
211211
]
212212
},
213213
],
214-
// ssr: [
215-
// ['/guide/ssr/introduction', 'Introduction'],
216-
// '/guide/ssr/getting-started',
217-
// '/guide/ssr/universal',
218-
// '/guide/ssr/structure',
219-
// '/guide/ssr/build-config',
220-
// '/guide/ssr/server',
221-
// '/guide/ssr/routing',
222-
// '/guide/ssr/hydration'
223-
// ],
214+
ssr: [
215+
['/guide/ssr/introduction', 'Introduction'],
216+
'/guide/ssr/getting-started',
217+
'/guide/ssr/universal',
218+
'/guide/ssr/structure',
219+
'/guide/ssr/build-config',
220+
'/guide/ssr/server',
221+
'/guide/ssr/routing',
222+
'/guide/ssr/hydration'
223+
],
224224
contributing: [
225225
{
226226
title: 'ドキュメントへの貢献',

src/guide/ssr.md

Lines changed: 11 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@
22

33
## 完全な SSR ガイド
44

5-
私たちは、サーバーでレンダリングされた Vue アプリケーションを作成するためのスタンドアロンのガイドを作成しました。これは、すでにクライアント側の Vue 開発、サーバー側の Node.js 開発そして Webpack に精通している方にとって非常に詳細なガイドです。[ssr.vuejs.org](https://ssr.vuejs.org/) を確認してください。
5+
私たちは、サーバーでレンダリングされた Vue アプリケーションを作成するためのスタンドアロンのガイドを作成しました。これは、すでにクライアント側の Vue 開発、サーバー側の Node.js 開発そして Webpack に精通している方にとって非常に詳細なガイドです。[こちら](/guide/ssr/introduction.html) を確認してください。
66

77
## Nuxt.js
88

@@ -11,3 +11,13 @@
1111
## Quasar Framework SSR + PWA
1212

1313
[Quasar Framework](https://quasar.dev) は、SSR アプリケーション (PWA ハンドオフオプションあり) を生成するフレームワークで、最高クラスのビルドシステム、実用的な環境設定、そして開発者の拡張性を活用して、あなたのアイデアを設計し構築することを簡単にします。100 を超える "Material Design 2.0" に準拠したコンポーネントがあり、どれかひとつをサーバ上で実行できます。これはブラウザでも使用でき、サイト内の `<meta>` タグで管理もできます。 Quasar は Node.js と webpack ベースの開発環境で、SPA、PWA、SSR、Electron、Capacitor、そして Cordova アプリケーション、全て 1 つのコードベースからの迅速な開発を合理化し、加速させます。
14+
15+
## Vite SSR
16+
17+
[Vite](https://vitejs.dev/) は、フロントエンド開発の経験を大幅に改善する新しいタイプのフロントエンドビルドツールです。大きく分けて 2 つの部分で構成されています。:
18+
19+
- ソースファイルをネイティブ ES モジュールで提供する開発サーバで、豊富な組み込み機能と、驚異的な速さの HMR (Hot Module Replacement) を備えています。
20+
21+
- コードを [Rollup](https://rollupjs.org/) でバンドルするビルドコマンドで、本番用に高度に最適化した静的アセットを出力するようにあらかじめ設定されています。
22+
23+
Vite は組み込みの [サーバサイドレンダリングのサポート](https://vitejs.dev/guide/ssr.html) もあります。Vue を使ったプロジェクトの例は [こちら](https://github.com/vitejs/vite/tree/main/packages/playground/ssr-vue) で見ることができます。

src/guide/ssr/build-config.md

Lines changed: 106 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,106 @@
1+
# Build Configuration
2+
3+
The webpack config for an SSR project will be similar to a client-only project. If you're not familiar with configuring webpack, you can find more information in the documentation for [Vue CLI](https://cli.vuejs.org/guide/webpack.html#working-with-webpack) or [configuring Vue Loader manually](https://vue-loader.vuejs.org/guide/#manual-setup).
4+
5+
## Key Differences with Client-Only Builds
6+
7+
1. We need to create a [webpack manifest](https://webpack.js.org/concepts/manifest/) for our server-side code. This is a JSON file that webpack keeps to track how all the modules map to the output bundles.
8+
9+
2. We should [externalize application dependencies](https://webpack.js.org/configuration/externals/). This makes the server build much faster and generates a smaller bundle file. When doing this, we have to exclude dependencies that need to be processed by webpack (like `.css`. or `.vue` files).
10+
11+
3. We need to change webpack [target](https://webpack.js.org/concepts/targets/) to Node.js. This allows webpack to handle dynamic imports in a Node-appropriate fashion, and also tells `vue-loader` to emit server-oriented code when compiling Vue components.
12+
13+
4. When building a server entry, we would need to define an environment variable to indicate we are working with SSR. It might be helpful to add a few `scripts` to the project's `package.json`:
14+
15+
```json
16+
"scripts": {
17+
"build:client": "vue-cli-service build --dest dist/client",
18+
"build:server": "SSR=1 vue-cli-service build --dest dist/server",
19+
"build": "npm run build:client && npm run build:server",
20+
}
21+
```
22+
23+
## Example Configuration
24+
25+
Below is a sample `vue.config.js` that adds SSR rendering to a Vue CLI project, but it can be adapted for any webpack build.
26+
27+
```js
28+
const { WebpackManifestPlugin } = require('webpack-manifest-plugin')
29+
const nodeExternals = require('webpack-node-externals')
30+
const webpack = require('webpack')
31+
32+
module.exports = {
33+
chainWebpack: webpackConfig => {
34+
// We need to disable cache loader, otherwise the client build
35+
// will used cached components from the server build
36+
webpackConfig.module.rule('vue').uses.delete('cache-loader')
37+
webpackConfig.module.rule('js').uses.delete('cache-loader')
38+
webpackConfig.module.rule('ts').uses.delete('cache-loader')
39+
webpackConfig.module.rule('tsx').uses.delete('cache-loader')
40+
41+
if (!process.env.SSR) {
42+
// Point entry to your app's client entry file
43+
webpackConfig
44+
.entry('app')
45+
.clear()
46+
.add('./src/entry-client.js')
47+
return
48+
}
49+
50+
// Point entry to your app's server entry file
51+
webpackConfig
52+
.entry('app')
53+
.clear()
54+
.add('./src/entry-server.js')
55+
56+
// This allows webpack to handle dynamic imports in a Node-appropriate
57+
// fashion, and also tells `vue-loader` to emit server-oriented code when
58+
// compiling Vue components.
59+
webpackConfig.target('node')
60+
// This tells the server bundle to use Node-style exports
61+
webpackConfig.output.libraryTarget('commonjs2')
62+
63+
webpackConfig
64+
.plugin('manifest')
65+
.use(new WebpackManifestPlugin({ fileName: 'ssr-manifest.json' }))
66+
67+
// https://webpack.js.org/configuration/externals/#function
68+
// https://github.com/liady/webpack-node-externals
69+
// Externalize app dependencies. This makes the server build much faster
70+
// and generates a smaller bundle file.
71+
72+
// Do not externalize dependencies that need to be processed by webpack.
73+
// You should also whitelist deps that modify `global` (e.g. polyfills)
74+
webpackConfig.externals(nodeExternals({ allowlist: /\.(css|vue)$/ }))
75+
76+
webpackConfig.optimization.splitChunks(false).minimize(false)
77+
78+
webpackConfig.plugins.delete('preload')
79+
webpackConfig.plugins.delete('prefetch')
80+
webpackConfig.plugins.delete('progress')
81+
webpackConfig.plugins.delete('friendly-errors')
82+
83+
webpackConfig.plugin('limit').use(
84+
new webpack.optimize.LimitChunkCountPlugin({
85+
maxChunks: 1
86+
})
87+
)
88+
}
89+
}
90+
```
91+
92+
## Externals Caveats
93+
94+
Notice that in the `externals` option we are whitelisting CSS files. This is because CSS imported from dependencies should still be handled by webpack. If you are importing any other types of files that also rely on webpack (e.g. `*.vue`, `*.sass`), you should add them to the whitelist as well.
95+
96+
If you are using `runInNewContext: 'once'` or `runInNewContext: true`, then you also need to whitelist polyfills that modify `global`, e.g. `babel-polyfill`. This is because when using the new context mode, **code inside a server bundle has its own `global` object.** Since you don't really need it on the server, it's actually easier to just import it in the client entry.
97+
98+
## Generating `clientManifest`
99+
100+
In addition to the server bundle, we can also generate a client build manifest. With the client manifest and the server bundle, the renderer now has information of both the server _and_ client builds. This way it can automatically infer and inject [preload / prefetch directives](https://css-tricks.com/prefetching-preloading-prebrowsing/), `<link>` and `<script>` tags into the rendered HTML.
101+
102+
The benefits are two-fold:
103+
104+
1. It can replace `html-webpack-plugin` for injecting the correct asset URLs when there are hashes in your generated filenames.
105+
106+
2. When rendering a bundle that leverages webpack's on-demand code splitting features, we can ensure the optimal chunks are preloaded / prefetched, and also intelligently inject `<script>` tags for needed async chunks to avoid waterfall requests on the client, thus improving TTI (time-to-interactive).

src/guide/ssr/getting-started.md

Lines changed: 99 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,99 @@
1+
# Getting Started
2+
3+
> This guide is currently under active development
4+
5+
## Installation
6+
7+
In order to create a server-side rendered application, we need to install the `@vue/server-renderer` package:
8+
9+
```bash
10+
npm install @vue/server-renderer
11+
## OR
12+
yarn add @vue/server-renderer
13+
```
14+
15+
#### Notes
16+
17+
- It's recommended to use Node.js version 10+.
18+
- `@vue/server-renderer` and `vue` must have matching versions.
19+
- `@vue/server-renderer` relies on some Node.js native modules and therefore can only be used in Node.js. We may provide a simpler build that can be run in other JavaScript runtimes in the future.
20+
21+
## Rendering a Vue Application
22+
23+
Unlike a client-only Vue application, which is created using `createApp`, an SSR application needs to be created using `createSSRApp`:
24+
25+
```js
26+
const { createSSRApp } = require('vue')
27+
28+
const app = createSSRApp({
29+
data() {
30+
return {
31+
user: 'John Doe'
32+
}
33+
},
34+
template: `<div>Current user is: {{ user }}</div>`
35+
})
36+
```
37+
38+
Now, we can use the `renderToString` function to render our application instance to a string. This function returns a Promise which resolves to the rendered HTML.
39+
40+
```js{2,13}
41+
const { createSSRApp } = require('vue')
42+
const { renderToString } = require('@vue/server-renderer')
43+
44+
const app = createSSRApp({
45+
data() {
46+
return {
47+
user: 'John Doe'
48+
}
49+
},
50+
template: `<div>Current user is: {{ user }}</div>`
51+
})
52+
53+
const appContent = await renderToString(app)
54+
```
55+
56+
## Integrating with a Server
57+
58+
To run an application, in this example we will use [Express](https://expressjs.com/):
59+
60+
```bash
61+
npm install express
62+
## OR
63+
yarn add express
64+
```
65+
66+
```js
67+
// server.js
68+
69+
const { createSSRApp } = require('vue')
70+
const { renderToString } = require('@vue/server-renderer')
71+
const server = require('express')()
72+
73+
server.get('*', async (req, res) => {
74+
const app = createSSRApp({
75+
data() {
76+
return {
77+
user: 'John Doe'
78+
}
79+
},
80+
template: `<div>Current user is: {{ user }}</div>`
81+
})
82+
83+
const appContent = await renderToString(app)
84+
const html = `
85+
<html>
86+
<body>
87+
<h1>My First Heading</h1>
88+
<div id="app">${appContent}</div>
89+
</body>
90+
</html>
91+
`
92+
93+
res.end(html)
94+
})
95+
96+
server.listen(8080)
97+
```
98+
99+
Now, when running this Node.js script, we can see a static HTML page on `localhost:8080`. However, this code is not _hydrated_: Vue hasn't yet taken over the static HTML sent by the server to turn it into dynamic DOM that can react to client-side data changes. This will be covered in the [Client Side Hydration](hydration.html) section.

src/guide/ssr/hydration.md

Lines changed: 33 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,33 @@
1+
# Client Side Hydration
2+
3+
Hydration refers to the client-side process during which Vue takes over the static HTML sent by the server and turns it into dynamic DOM that can react to client-side data changes.
4+
5+
In `entry-client.js`, we are simply mounting the app with this line:
6+
7+
```js
8+
app.mount('#app')
9+
```
10+
11+
Since the server has already rendered the markup, we obviously do not want to throw that away and re-create all the DOM elements. Instead, we want to "hydrate" the static markup and make it interactive.
12+
13+
Vue provides a `createSSRApp` method for use in client-side code (in this case, in our `entry-client.js`) to tell Vue to hydrate the existing static HTML instead of re-creating all the DOM elements.
14+
15+
### Hydration Caveats
16+
17+
Vue will assert the client-side generated virtual DOM tree matches the DOM structure rendered from the server. If there is a mismatch, it will bail hydration, discard existing DOM and render from scratch. There will be a warning in the browser console but your site will still work.
18+
19+
The first key way to ensure that SSR is working to ensuring your application state is the same on client and server. Take special care not to depend on APIs specific to the browser (like window width, device capability or localStorage) or server (such as Node built-ins), and take care where the same code will give different results when run in different places (such as when using timezones, timestamps, normalizing URLs or generating random numbers). See [Writing Universal Code](./universal.md) for more details.
20+
21+
A second key thing to be aware of when using SSR + client hydration is that invalid HTML may be altered by the browser. For example, when you write this in a Vue template:
22+
23+
```html
24+
<table>
25+
<tr>
26+
<td>hi</td>
27+
</tr>
28+
</table>
29+
```
30+
31+
The browser will automatically inject `<tbody>` inside `<table>`, however, the virtual DOM generated by Vue does not contain `<tbody>`, so it will cause a mismatch. To ensure correct matching, make sure to write valid HTML in your templates.
32+
33+
You might consider using a HTML validator like [the W3C Markup Validation Service](https://validator.w3.org/) or [HTML-validate](https://html-validate.org/) to check your templates in development.

src/guide/ssr/introduction.md

Lines changed: 47 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,47 @@
1+
# Server-Side Rendering Guide
2+
3+
> This guide is currently under active development
4+
5+
## What is Server-Side Rendering (SSR)?
6+
7+
Vue.js is a framework for building client-side applications. By default, Vue components produce and manipulate DOM in the browser as output. However, it is also possible to render the same components into HTML strings on the server, send them directly to the browser, and finally "hydrate" the static markup into a fully interactive application on the client.
8+
9+
A server-rendered Vue.js application can also be considered "isomorphic" or "universal". This means that the majority of your app's code runs on both the server **and** the client.
10+
11+
## Why SSR?
12+
13+
Compared to a traditional SPA (Single-Page Application), the advantage of SSR primarily lies in:
14+
15+
- Better search engine optimization (SEO), as the search engine crawlers will directly see the fully rendered page.
16+
17+
Note that as of now, Google and Bing can index synchronous JavaScript applications just fine. Synchronous being the key word there. If your app starts with a loading spinner, then fetches content via API call, the crawler will not wait for you to finish. This means if you have content fetched asynchronously on pages where SEO is important, SSR might be necessary.
18+
19+
- Faster time-to-content, especially on the slow Internet connection or slow devices. Server-rendered markup doesn't need to wait until all JavaScript has been downloaded and executed to be displayed, so your user will see a fully-rendered page sooner. This generally results in better user experience, and can be critical for applications where time-to-content is directly associated with the conversion rate.
20+
21+
There are also some trade-offs to consider when using SSR:
22+
23+
- Development constraints. Browser-specific code can only be used inside certain lifecycle hooks; some external libraries may need special treatment to be able to run in a server-rendered app.
24+
25+
- More involved build setup and deployment requirements. Unlike a fully static SPA that can be deployed on any static file server, a server-rendered app requires an environment where a Node.js server can run.
26+
27+
- More server-side load. Rendering a full app in Node.js is going to be more CPU-intensive than just serving static files, so if you expect high traffic, be prepared for corresponding server load and wisely employ caching strategies.
28+
29+
Before using SSR for your application, the first question you should ask is whether you actually need it. It mostly depends on how important time-to-content is for your application. For example, if you are building an internal dashboard where an extra few hundred milliseconds on the initial load doesn't matter that much, SSR would be overkill. However, in cases where time-to-content is absolutely critical, SSR can help you achieve the best possible initial load performance.
30+
31+
## SSR vs Prerendering
32+
33+
If you're only investigating SSR to improve the SEO of a handful of marketing pages (e.g. `/`, `/about`, `/contact`, etc), then you probably want **prerendering** instead. Rather than using a web server to compile HTML on-the-fly, prerendering generates static HTML files for specific routes at build time. The advantage is setting up prerendering is much simpler and allows you to keep your frontend as a fully static site.
34+
35+
If you're using [webpack](https://webpack.js.org/), you can add prerendering with the [prerender-spa-plugin](https://github.com/chrisvfritz/prerender-spa-plugin). It's been extensively tested with Vue apps.
36+
37+
## About This Guide
38+
39+
[//]: # 'TODO: This guide is focused on server-rendered Single-Page Applications using Node.js as the server. Mixing Vue SSR with other backend setups is a topic of its own and briefly discussed in a [dedicated section].'
40+
41+
This guide will be very in-depth and assumes you are already familiar with Vue.js itself, and have a decent working knowledge of Node.js and webpack.
42+
43+
[//]: # 'If you prefer a higher-level solution that provides a smooth out-of-the-box experience, you should probably give [Nuxt.js](https://nuxtjs.org/) a try. It's built upon the same Vue stack but abstracts away a lot of the boilerplate, and provides some extra features such as static site generation. However, it may not suit your use case if you need more direct control of your app's structure. Regardless, it would still be beneficial to read through this guide to understand better how things work together.'
44+
45+
[//]: # 'TODO: As you read along, it would be helpful to refer to the official [HackerNews Demo](https://github.com/vuejs/vue-hackernews-2.0/), which makes use of most of the techniques covered in this guide'
46+
47+
Finally, note that the solutions in this guide are not definitive - we've found them to be working well for us, but that doesn't mean they cannot be improved. They might get revised in the future - and feel free to contribute by submitting pull requests!

0 commit comments

Comments
 (0)