├── CNAME
├── LANGS.md
├── .gitignore
├── deploy.sh
├── README.md
├── book.json
├── yarn.lock
├── package.json
├── en
├── SUMMARY.md
├── streaming.md
├── hydration.md
├── head.md
├── bundle-renderer.md
├── universal.md
├── caching.md
├── css.md
├── basic.md
├── README.md
├── structure.md
├── routing.md
├── build-config.md
├── api.md
└── data.md
├── ru
├── SUMMARY.md
├── streaming.md
├── hydration.md
├── head.md
├── bundle-renderer.md
├── universal.md
├── css.md
├── caching.md
├── basic.md
├── routing.md
├── structure.md
├── README.md
├── build-config.md
├── api.md
└── data.md
└── ko
└── structure.md
/CNAME:
--------------------------------------------------------------------------------
1 | ssr.vuejs.org
2 |
--------------------------------------------------------------------------------
/LANGS.md:
--------------------------------------------------------------------------------
1 | * [English](en/)
2 | * [Русский](ru/)
3 |
--------------------------------------------------------------------------------
/.gitignore:
--------------------------------------------------------------------------------
1 | node_modules
2 | .DS_Store
3 | _book
4 | test
5 |
--------------------------------------------------------------------------------
/deploy.sh:
--------------------------------------------------------------------------------
1 | rm -rf _book
2 | gitbook install
3 | gitbook build
4 | mkdir _book
5 | cp CNAME _book/CNAME
6 | cd _book
7 | git init
8 | git add -A
9 | git commit -m 'update book'
10 | git push -f git@github.com:vuejs/vue-ssr-docs.git master:gh-pages
11 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | # vue-ssr-docs
2 |
3 | Documentation for `vue-server-renderer`. Currently WIP and pending Vue 2.3.0 release - will be at [ssr.vuejs.org](https://ssr.vuejs.org).
4 |
5 | ## Development
6 |
7 | ``` bash
8 | # install gitbook CLI
9 | npm install gitbook-cli -g
10 |
11 | # install local plugins
12 | npm install
13 |
14 | # serve
15 | npm run dev
16 | ```
17 |
--------------------------------------------------------------------------------
/book.json:
--------------------------------------------------------------------------------
1 | {
2 | "gitbook": ">3.0.0",
3 | "plugins": ["edit-link", "theme-vuejs@git+https://github.com/pearofducks/gitbook-plugin-theme-vuejs.git", "-fontsettings", "github"],
4 | "pluginsConfig": {
5 | "edit-link": {
6 | "base": "https://github.com/vuejs/vue-ssr-docs/edit/master/",
7 | "label": "Edit This Page"
8 | },
9 | "github": {
10 | "url": "https://github.com/vuejs/vue-ssr-docs/"
11 | }
12 | },
13 | "links": {
14 | "sharing": {
15 | "facebook": false,
16 | "twitter": false
17 | }
18 | }
19 | }
20 |
--------------------------------------------------------------------------------
/yarn.lock:
--------------------------------------------------------------------------------
1 | # THIS IS AN AUTOGENERATED FILE. DO NOT EDIT THIS FILE DIRECTLY.
2 | # yarn lockfile v1
3 |
4 |
5 | gitbook-plugin-edit-link@^2.0.2:
6 | version "2.0.2"
7 | resolved "https://registry.yarnpkg.com/gitbook-plugin-edit-link/-/gitbook-plugin-edit-link-2.0.2.tgz#d8fcd927eced81e7a662a72d59db609eafd7e72f"
8 |
9 | gitbook-plugin-github@^3.0.0:
10 | version "3.0.0"
11 | resolved "https://registry.yarnpkg.com/gitbook-plugin-github/-/gitbook-plugin-github-3.0.0.tgz#67457df98a57ea8ef9b2518b88340db370a5317b"
12 |
13 | gitbook-plugin-theme-vuejs@pearofducks/gitbook-plugin-theme-vuejs:
14 | version "0.0.2"
15 | resolved "https://codeload.github.com/pearofducks/gitbook-plugin-theme-vuejs/tar.gz/61c9bb8b168814c32331e63b3b9f1dd9266d8452"
16 |
--------------------------------------------------------------------------------
/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "vue-ssr-docs",
3 | "version": "1.0.0",
4 | "private": true,
5 | "description": "docs for vue-server-renderer",
6 | "scripts": {
7 | "dev": "gitbook serve",
8 | "deploy": "bash deploy.sh"
9 | },
10 | "repository": {
11 | "type": "git",
12 | "url": "git+https://github.com/vuejs/vue-ssr-docs.git"
13 | },
14 | "keywords": [
15 | "vue",
16 | "ssr",
17 | "docs"
18 | ],
19 | "author": "Evan You",
20 | "license": "MIT",
21 | "bugs": {
22 | "url": "https://github.com/vuejs/vue-ssr-docs/issues"
23 | },
24 | "homepage": "https://github.com/vuejs/vue-ssr-docs#readme",
25 | "devDependencies": {
26 | "gitbook-plugin-edit-link": "^2.0.2",
27 | "gitbook-plugin-github": "^3.0.0",
28 | "gitbook-plugin-theme-vuejs": "pearofducks/gitbook-plugin-theme-vuejs"
29 | }
30 | }
31 |
--------------------------------------------------------------------------------
/en/SUMMARY.md:
--------------------------------------------------------------------------------
1 | - [Basic Usage](basic.md)
2 | - [Writing Universal Code](universal.md)
3 | - [Source Code Structure](structure.md)
4 | - [Routing and Code-Splitting](routing.md)
5 | - [Data Pre-fetching and State](data.md)
6 | - [Client Side Hydration](hydration.md)
7 | - [Introducing Bundle Renderer](bundle-renderer.md)
8 | - [Build Configuration](build-config.md)
9 | - [CSS Management](css.md)
10 | - [Head Management](head.md)
11 | - [Caching](caching.md)
12 | - [Streaming](streaming.md)
13 | - [API Reference](api.md)
14 | - [createRenderer](api.md#createrendereroptions)
15 | - [createBundleRenderer](api.md#createbundlerendererbundle-options)
16 | - [Class: Renderer](api.md#class-renderer)
17 | - [Class: BundleRenderer](api.md#class-bundlerenderer)
18 | - [Renderer Options](api.md#renderer-options)
19 | - [template](api.md#template)
20 | - [clientManifest](api.md#clientmanifest)
21 | - [inject](api.md#inject)
22 | - [shouldPreload](api.md#shouldpreload)
23 | - [runInNewContext](api.md#runinnewcontext)
24 | - [basedir](api.md#basedir)
25 | - [cache](api.md#cache)
26 | - [directives](api.md#directives)
27 | - [webpack Plugins](api.md#webpack-plugins)
28 |
--------------------------------------------------------------------------------
/ru/SUMMARY.md:
--------------------------------------------------------------------------------
1 | - [Использование](basic.md)
2 | - [Написание универсального кода](universal.md)
3 | - [Структура исходного кода](structure.md)
4 | - [Маршрутизация и разделение кода](routing.md)
5 | - [Предзагрузка данных и состояния](data.md)
6 | - [Гидратация клиентской части](hydration.md)
7 | - [Представляем Bundle Renderer](bundle-renderer.md)
8 | - [Конфигурация сборки](build-config.md)
9 | - [Управление CSS](css.md)
10 | - [Управление заголовочными тегами (head)](head.md)
11 | - [Кэширование](caching.md)
12 | - [Стриминг](streaming.md)
13 | - [Справочник API](api.md)
14 | - [createRenderer](api.md#createrendereroptions)
15 | - [createBundleRenderer](api.md#createbundlerendererbundle-options)
16 | - [Класс: Renderer](api.md#class-renderer)
17 | - [Класс: BundleRenderer](api.md#class-bundlerenderer)
18 | - [Опции рендерера](api.md#renderer-options)
19 | - [template](api.md#template)
20 | - [clientManifest](api.md#clientmanifest)
21 | - [inject](api.md#inject)
22 | - [shouldPreload](api.md#shouldpreload)
23 | - [runInNewContext](api.md#runinnewcontext)
24 | - [basedir](api.md#basedir)
25 | - [cache](api.md#cache)
26 | - [directives](api.md#directives)
27 | - [Webpack плагины](api.md#webpack-plugins)
28 |
--------------------------------------------------------------------------------
/en/streaming.md:
--------------------------------------------------------------------------------
1 | # Streaming
2 |
3 | `vue-server-renderer` supports stream rendering out of the box, for both the base renderer and the bundle renderer. All you need to do is use `renderToStream` instead of `renderToString`:
4 |
5 | ``` js
6 | const stream = renderer.renderToStream(context)
7 | ```
8 |
9 | The returned value is a [Node.js stream](https://nodejs.org/api/stream.html):
10 |
11 | ``` js
12 | let html = ''
13 |
14 | stream.on('data', data => {
15 | html += data.toString()
16 | })
17 |
18 | stream.on('end', () => {
19 | console.log(html) // render complete
20 | })
21 |
22 | stream.on('error', err => {
23 | // handle error...
24 | })
25 | ```
26 |
27 | ## Streaming Caveats
28 |
29 | In stream rendering mode, data is emitted as soon as possible when the renderer traverses the Virtual DOM tree. This means we can get an earlier "first chunk" and start sending it to the client faster.
30 |
31 | However, when the first data chunk is emitted, the child components may not even be instantiated yet, neither will their lifecycle hooks get called. This means if the child components need to attach data to the render context in their lifecycle hooks, these data will not be available when the stream starts. Since a lot of the context information (like head information or inlined critical CSS) needs to be appear before the application markup, we essentially have to wait until the stream to complete before we can start making use of these context data.
32 |
33 | It is therefore **NOT** recommended to use streaming mode if you rely on context data populated by component lifecycle hooks.
34 |
--------------------------------------------------------------------------------
/en/hydration.md:
--------------------------------------------------------------------------------
1 | # Client Side Hydration
2 |
3 | In `entry-client.js`, we are simply mounting the app with this line:
4 |
5 | ``` js
6 | // this assumes App.vue template root element has `id="app"`
7 | app.$mount('#app')
8 | ```
9 |
10 | 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.
11 |
12 | If you inspect the server-rendered output, you will notice that the app's root element has a special attribute:
13 |
14 | ``` js
15 |
16 | ```
17 |
18 | The `data-server-rendered` special attribute lets the client-side Vue know that the markup is rendered by the server and it should mount in hydration mode.
19 |
20 | In development mode, 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. **In production mode, this assertion is disabled for maximum performance.**
21 |
22 | ### Hydration Caveats
23 |
24 | One thing to be aware of when using SSR + client hydration is some special HTML structures that may be altered by the browser. For example, when you write this in a Vue template:
25 |
26 | ``` html
27 |
30 | ```
31 |
32 | The browser will automatically inject `
` inside ``, however, the virtual DOM generated by Vue does not contain ``, so it will cause a mismatch. To ensure correct matching, make sure to write valid HTML in your templates.
33 |
--------------------------------------------------------------------------------
/ru/streaming.md:
--------------------------------------------------------------------------------
1 | # Стриминг
2 |
3 | `vue-server-renderer` поддерживает потоковый рендеринг из коробки, как для базового рендерера, так и для рендерера сборки. Всё, что вам нужно сделать, это использовать `renderToStream` вместо `renderToString`:
4 |
5 | ``` js
6 | const stream = renderer.renderToStream(context)
7 | ```
8 |
9 | Возвращаемое значение это обычный [поток Node.js](https://nodejs.org/api/stream.html):
10 |
11 | ``` js
12 | let html = ''
13 |
14 | stream.on('data', data => {
15 | html += data.toString()
16 | })
17 |
18 | stream.on('end', () => {
19 | console.log(html) // рендер завершён
20 | })
21 |
22 | stream.on('error', err => {
23 | // обработка ошибок...
24 | })
25 | ```
26 |
27 | ## Ограничения по использованию стриминга
28 |
29 | В режиме рендеринга потока данные отдаются как можно быстрее пока рендерер проходит дерево виртуального DOM. Это означает, что мы можем получить раньше «первый кусок» и быстрее отправить его клиенту.
30 |
31 | Тем не менее, когда первый блок данных был отправлен, дочерние компоненты могут ещё даже не быть созданы, и не будут вызваны хуки их жизненного цикла. Это означает, что если дочерние компоненты должны присоединять данные к контексту рендера в их хуках жизненного цикла, то эти данные ещё не будут доступны когда поток начнётся. Поскольку большая часть контекстной информации (например, информация о заголовочных тегах или о встроенном критическом CSS) должна появиться перед разметкой приложения, мы по существу должны дождаться завершения потока, прежде чем сможем начать использовать эти данные контекста.
32 |
33 | Поэтому **НЕ РЕКОМЕНДУЕТСЯ** использовать режим потоковой передачи, если вы полагаетесь на данные контекста, заполняемые данными в хуках жизненного цикла компонентов.
34 |
--------------------------------------------------------------------------------
/ru/hydration.md:
--------------------------------------------------------------------------------
1 | # Гидратация клиентской части
2 |
3 | В файле `entry-client.js`, мы просто монтируем приложение такой строкой:
4 |
5 | ``` js
6 | // подразумеваем что шаблон в App.vue имеет корневой элемент с id="app"
7 | app.$mount('#app')
8 | ```
9 |
10 | Поскольку сервер уже отобразил разметку, мы, очевидно, не хотели бы её выбрасывать и заново создавать все элементы DOM. Вместо этого мы хотим «гидрировать» статическую разметку и сделать её интерактивной.
11 |
12 | Если вы исследовали вывод после серверного рендеринга, вы могли заметить, что у корневого элемента приложения есть специальный атрибут:
13 |
14 | ``` js
15 |
16 | ```
17 |
18 | Специальный атрибут `data-server-rendered` позволяет клиентской части Vue знать, что разметка отображается сервером, и он должен монтироваться в режиме гидратации.
19 |
20 | В режиме разработки Vue будет проверять, что виртуальное дерево DOM, сгенерированное на стороне клиента, совпадает с структурой DOM созданной на сервере. При нахождении несоответствия, он будет считать гидратацию неудачной, отказываться от существующего DOM и рендерить всё с нуля. **В режиме production, эта проверка отключена для достижения максимальной производительности.**
21 |
22 | ### Предостережения при гидратации
23 |
24 | Одна вещь, о которой нужно знать при использовании серверного рендеринга + гидратации на клиенте — некоторые специальные структуры HTML, которые могут быть изменены браузером. Например, когда вы пишете подобное в шаблоне Vue:
25 |
26 | ``` html
27 |
30 | ```
31 |
32 | Браузер будет автоматически добавлять `
` внутрь ``, в то время как виртуальный DOM генерируемый Vue не содержит ``, что вызовет несоответствие. Чтобы обеспечить правильное соответствие, обязательно пишите валидный HTML в ваших шаблонах.
33 |
--------------------------------------------------------------------------------
/en/head.md:
--------------------------------------------------------------------------------
1 | # Head Management
2 |
3 | Similar to asset injection, head management follows the same idea: we can dynamically attach data to the render `context` in a component's lifecycle, and then interpolate those data in `template`.
4 |
5 | > In version >=2.3.2, you can directly access the SSR context in a component as `this.$ssrContext`. In older versions you'd have to manually inject the SSR context by passing it to `createApp()` and expose it on the root instance's `$options` - child components can then access it via `this.$root.$options.ssrContext`.
6 |
7 | We can write a simple mixin to perform title management:
8 |
9 | ``` js
10 | // title-mixin.js
11 |
12 | function getTitle (vm) {
13 | // components can simply provide a `title` option
14 | // which can be either a string or a function
15 | const { title } = vm.$options
16 | if (title) {
17 | return typeof title === 'function'
18 | ? title.call(vm)
19 | : title
20 | }
21 | }
22 |
23 | const serverTitleMixin = {
24 | created () {
25 | const title = getTitle(this)
26 | if (title) {
27 | this.$ssrContext.title = title
28 | }
29 | }
30 | }
31 |
32 | const clientTitleMixin = {
33 | mounted () {
34 | const title = getTitle(this)
35 | if (title) {
36 | document.title = title
37 | }
38 | }
39 | }
40 |
41 | // VUE_ENV can be injected with webpack.DefinePlugin
42 | export default process.env.VUE_ENV === 'server'
43 | ? serverTitleMixin
44 | : clientTitleMixin
45 | ```
46 |
47 | Now, a route component can make use of this to control the document title:
48 |
49 | ``` js
50 | // Item.vue
51 | export default {
52 | mixins: [titleMixin],
53 | title () {
54 | return this.item.title
55 | }
56 |
57 | asyncData ({ store, route }) {
58 | return store.dispatch('fetchItem', route.params.id)
59 | },
60 |
61 | computed: {
62 | item () {
63 | return this.$store.state.items[this.$route.params.id]
64 | }
65 | }
66 | }
67 | ```
68 |
69 | And inside the `template` passed to bundle renderer:
70 |
71 | ``` html
72 |
73 |
74 | {{ title }}
75 |
76 |
77 | ...
78 |
79 |
80 | ```
81 |
82 | **Notes:**
83 |
84 | - Use double-mustache (HTML-escaped interpolation) to avoid XSS attacks.
85 |
86 | - You should provide a default title when creating the `context` object in case no component has set a title during render.
87 |
88 | ---
89 |
90 | Using the same strategy, you can easily expand this mixin into a generic head management utility.
91 |
--------------------------------------------------------------------------------
/ru/head.md:
--------------------------------------------------------------------------------
1 | # Управление заголовочными тегами (head)
2 |
3 | Аналогично внедрению ресурсов, управление заголовочными тегами следует той же идее: мы можем динамически присоединять данные к `context` рендерера в жизненном цикле компонента, а затем интерполировать эти данные в `template`.
4 |
5 | > С версии >=2.3.2 вы можете напрямую получать доступ к контексту SSR в компонентах через `this.$ssrContext`. В более ранних версиях вам потребуется вручную внедрять контекст SSR, передав его в `createApp()` и выставляя его на корневом экземплере `$options` — после чего, компоненты потомки смогут получить к нему доступ через `this.$root.$options.ssrContext`.
6 |
7 | Мы можем написать простую примесь для управления заголовком:
8 |
9 | ``` js
10 | // title-mixin.js
11 |
12 | function getTitle (vm) {
13 | // компоненты могут просто предоставлять опцию `title`,
14 | // которая может быть как строкой, так и функцией
15 | const { title } = vm.$options
16 | if (title) {
17 | return typeof title === 'function'
18 | ? title.call(vm)
19 | : title
20 | }
21 | }
22 |
23 | const serverTitleMixin = {
24 | created () {
25 | const title = getTitle(this)
26 | if (title) {
27 | this.$ssrContext.title = title
28 | }
29 | }
30 | }
31 |
32 | const clientTitleMixin = {
33 | mounted () {
34 | const title = getTitle(this)
35 | if (title) {
36 | document.title = title
37 | }
38 | }
39 | }
40 |
41 | // значение VUE_ENV будет определено плагином webpack.DefinePlugin
42 | export default process.env.VUE_ENV === 'server'
43 | ? serverTitleMixin
44 | : clientTitleMixin
45 | ```
46 |
47 | Теперь компонент маршрута сможет управлять заголовком документа:
48 |
49 | ``` js
50 | // Item.vue
51 | export default {
52 | mixins: [titleMixin],
53 | title () {
54 | return this.item.title
55 | }
56 |
57 | asyncData ({ store, route }) {
58 | return store.dispatch('fetchItem', route.params.id)
59 | },
60 |
61 | computed: {
62 | item () {
63 | return this.$store.state.items[this.$route.params.id]
64 | }
65 | }
66 | }
67 | ```
68 |
69 | И внутри `template`, переданного в рендерер сборки:
70 |
71 | ``` html
72 |
73 |
74 | {{ title }}
75 |
76 |
77 | ...
78 |
79 |
80 | ```
81 |
82 | **Примечания:**
83 |
84 | - Используйте двойные фигурные скобки (интерполяция экранированного HTML), чтобы избежать XSS-уязвимостей.
85 |
86 | - Вы должны указать заголовок по умолчанию при создании объекта `context` на случай, если ни один компонент не установит заголовок во время рендеринга.
87 |
88 | ---
89 |
90 | Используя ту же стратегию, вы можете легко расширять примесь в универсальную утилиту по управлению основными заголовочными тегами.
91 |
--------------------------------------------------------------------------------
/en/bundle-renderer.md:
--------------------------------------------------------------------------------
1 | # Introducing Bundle Renderer
2 |
3 | ## Problems with Basic SSR
4 |
5 | Up to this point, we have assumed that the bundled server-side code will be directly used by the server via `require`:
6 |
7 | ``` js
8 | const createApp = require('/path/to/built-server-bundle.js')
9 | ```
10 |
11 | This is straightforward, however whenever you edit your app source code, you would have to stop and restart the server. This hurts productivity quite a bit during development. In addition, Node.js doesn't support source maps natively.
12 |
13 | ## Enter BundleRenderer
14 |
15 | `vue-server-renderer` provides an API called `createBundleRenderer` to deal with this problem. With a custom webpack plugin, the server bundle is generated as a special JSON file that can be passed to the bundle renderer. Once the bundle renderer is created, usage is the same as the normal renderer, however the bundle renderer provides the following benefits:
16 |
17 | - Built-in source map support (with `devtool: 'source-map'` in webpack config)
18 |
19 | - Hot-reload during development and even deployment (by simply reading the updated bundle and re-creating the renderer instance)
20 |
21 | - Critical CSS injection (when using `*.vue` files): automatically inlines the CSS needed by components used during the render. See the [CSS](./css.md) section for more details.
22 |
23 | - Asset injection with [clientManifest](./api.md#clientmanifest): automatically infers the optimal preload and prefetch directives, and the code-split chunks needed for the initial render.
24 |
25 | ---
26 |
27 | We will discuss how to configure webpack to generate the build artifacts needed by the bundle renderer in the next section, but for now let's assume we already have what we need, and this is how to create a use a bundle renderer:
28 |
29 | ``` js
30 | const { createBundleRenderer } = require('vue-server-renderer')
31 |
32 | const renderer = createBundleRenderer(serverBundle, {
33 | runInNewContext: false, // recommended
34 | template, // (optional) page template
35 | clientManifest // (optional) client build manifest
36 | })
37 |
38 | // inside a server handler...
39 | server.get('*', (req, res) => {
40 | const context = { url: req.url }
41 | // No need to pass an app here because it is auto-created by
42 | // executing the bundle. Now our server is decoupled from our Vue app!
43 | renderer.renderToString(context, (err, html) => {
44 | // handle error...
45 | res.end(html)
46 | })
47 | })
48 | ```
49 |
50 | When `renderToString` is called on a bundle renderer, it will automatically execute the function exported by the bundle to create an app instance (passing `context` as the argument) , and then render it.
51 |
52 | Note it's recommended to set the `runInNewContext` option to `false` or `'once'`. See its [API reference](./api.md#runinnewcontext) for more details.
53 |
--------------------------------------------------------------------------------
/ru/bundle-renderer.md:
--------------------------------------------------------------------------------
1 | # Представляем Bundle Renderer
2 |
3 | ## Проблемы с обычным SSR
4 |
5 | До этого момента мы предполагали, что сборка с кодом серверной части будет напрямую использоваться сервером через `require`:
6 |
7 | ``` js
8 | const createApp = require('/path/to/built-server-bundle.js')
9 | ```
10 |
11 | Выглядит просто, однако всякий раз, когда вы редактируете исходный код вашего приложения, вам понадобится остановить и перезапустить сервер. Это очень плохо влияет на производительность во время разработки. К тому же, Node.js не поддерживает source maps нативно.
12 |
13 | ## Добавляем BundleRenderer
14 |
15 | `vue-server-renderer` предоставляет API, названное `createBundleRenderer` для решения этой проблемы. С помощью Webpack-плагина, серверная сборка создаётся как специальный JSON-файл, который может быть передан в рендерер. Как только рендерер создан, использование не будет отличаться от обычного рендерера, но появятся некоторые преимущества:
16 |
17 | - Встроенная поддержка source map (с помощью `devtool: 'source-map'` в конфигурации Webpack)
18 |
19 | - Горячая перезагрузка в процессе разработки и даже развёртывания (путём простого чтения обновлённого пакета и пересоздания экземпляра рендерера)
20 |
21 | - Внедрение критического CSS (при использовании `*.vue` файлов): автоматически встраивает CSS, необходимый компонентам во время рендеринга. Подробнее в разделе [CSS](./css.md).
22 |
23 | - Внедрение ресурсов с помощью [clientManifest](./api.md#clientmanifest): автоматически добавляет оптимальные директивы preload и prefetch, а также фрагменты кода, необходимые для первоначального рендеринга.
24 |
25 | ---
26 |
27 | Мы обсудим, как настроить Webpack для генерации необходимых частей для рендерера в следующем разделе, а сейчас давайте предположим, что у нас уже есть всё необходимое, и создадим рендерер:
28 |
29 | ``` js
30 | const { createBundleRenderer } = require('vue-server-renderer')
31 |
32 | const renderer = createBundleRenderer(serverBundle, {
33 | runInNewContext: false, // рекомендуется
34 | template, // (опционально) шаблон страницы
35 | clientManifest // (опционально) манифест клиентской сборки
36 | })
37 |
38 | // внутри обработчика сервера...
39 | server.get('*', (req, res) => {
40 | const context = { url: req.url }
41 | // Нет необходимости передавать приложение здесь, потому что оно автоматически создаётся
42 | // при выполнении сборки. Теперь наш сервер отделён от нашего приложения Vue!
43 | renderer.renderToString(context, (err, html) => {
44 | // обработка ошибок...
45 | res.end(html)
46 | })
47 | })
48 | ```
49 |
50 | Когда `renderToString` вызывается в рендерере, он автоматически выполнит функцию, экспортируемую сборкой для создания экземпляра приложения (передавая `context` в качестве аргумента), а затем рендерит его.
51 |
52 | Обратите внимание, что рекомендуется установить опцию `runInNewContext` в значение `false` или `'once'`. См. [справочник API](./api.md#runinnewcontext) для подробностей.
--------------------------------------------------------------------------------
/en/universal.md:
--------------------------------------------------------------------------------
1 | # Writing Universal Code
2 |
3 | Before going further, let's take a moment to discuss the constraints when writing "universal" code - that is, code that runs on both the server and the client. Due to use case and platform API differences, the behavior of our code will not be exactly the same when running in different environments. Here we will go over the key things you need to aware of.
4 |
5 | ## Data Reactivity on the Server
6 |
7 | In a client-only app, every user will be using a fresh instance of the app in their browser. For server-side rendering we want the same: each request should have a fresh, isolated app instance so that there is no cross-request state pollution.
8 |
9 | Because the actual rendering process needs to be deterministic, we will also be "pre-fetching" data on the server - this means our application state will be already resolved when we start rendering. This means data reactivity is unnecessary on the server, so it is disabled by default. Disabling data reactivity also avoids the performance cost of converting data into reactive objects.
10 |
11 | ## Component Lifecycle Hooks
12 |
13 | Since there are no dynamic updates, of all the lifecycle hooks, only `beforeCreate` and `created` will be called during SSR. This means any code inside other lifecycle hooks such as `beforeMount` or `mounted` will only be executed on the client.
14 |
15 | Another thing to note is that you should avoid code that produces global side effects in `beforeCreate` and `created`, for example setting up timers with `setInterval`. In client-side only code we may setup a timer and then tear it down in `beforeDestroy` or `destroyed`. However, because the destroy hooks will not be called during SSR, the timers will stay around forever. To avoid this, move your side-effect code into `beforeMount` or `mounted` instead.
16 |
17 | ## Access to Platform-Specific APIs
18 |
19 | Universal code cannot assume access to platform-specific APIs, so if your code directly uses browser-only globals like `window` or `document`, they will throw errors when executed in Node.js, and vice-versa.
20 |
21 | For tasks shared between server and client but use different platform APIs, it's recommended to wrap the platform-specific implementations inside a universal API, or use libraries that do this for you. For example, [axios](https://github.com/mzabriskie/axios) is an HTTP client that exposes the same API for both server and client.
22 |
23 | For browser-only APIs, the common approach is to lazily access them inside client-only lifecycle hooks.
24 |
25 | Note that if a 3rd party library is not written with universal usage in mind, it could be tricky to integrate it into an server-rendered app. You *might* be able to get it working by mocking some of the globals, but it would be hacky and may interfere with the environment detection code of other libraries.
26 |
27 | ## Custom Directives
28 |
29 | Most custom directives directly manipulate the DOM, and therefore will cause error during SSR. There are two ways to work around this:
30 |
31 | 1. Prefer using components as the abstraction mechanism and work at the Virtual-DOM level (e.g. using render functions) instead.
32 |
33 | 2. If you have a custom directive that cannot be easily replaced by components, you can provide a "server-side version" of it using the [`directives`](./api.md#directives) option when creating the server renderer.
34 |
--------------------------------------------------------------------------------
/ru/universal.md:
--------------------------------------------------------------------------------
1 | # Написание универсального кода
2 |
3 | Прежде чем идти дальше, давайте немного обсудим ограничения при написании «универсального» кода — кода, который выполняется как на сервере, так и на клиенте. Из-за различий в API платформы, поведение нашего кода будет отличаться при работе в разных средах выполнения. Здесь мы рассмотрим ключевые моменты, которые нужно вам нужно знать.
4 |
5 | ## Реактивность данных на сервере
6 |
7 | В приложении, которое работает только на клиенте, каждый пользователь использует свежий экземпляр приложения в своём браузере. Для рендеринга на стороне сервера мы хотим того же: каждый запрос должен иметь свежий, изолированный экземпляр приложения, чтобы не возникало загрязнения состояния при перекрёстных запросах.
8 |
9 | Поскольку фактический процесс рендеринга должен быть детерминированным, мы также будем «предзагружать» данные на сервере — это означает, что состояние приложения будет разрешённым, на момент начала рендеринга. А это означает, что на сервере реактивность данных не нужна, поэтому по умолчанию она отключена. Отключение реактивности данных также позволяет избежать уменьшения производительности из-за отсутствия необходимости преобразования данных в реактивные объекты.
10 |
11 | ## Хуки жизненного цикла компонента
12 |
13 | Так как динамических обновлений нет, из всех хуков жизненного цикла будут вызваны только `beforeCreate` и `created` во время серверного рендеринга (SSR). Это означает, что код внутри любых других хуков жизненного цикла, таких как `beforeMount` или `mounted`, будет выполняться только на клиенте.
14 |
15 | Стоит ещё отметить, что вам следует избегать кода, который производит глобальные побочные эффекты (side effects) в хуках `beforeCreate` и `created`, например устанавливая таймеры с помощью `setInterval`. В коде на стороне клиента мы можем установить таймер, а затем остановить его в `beforeDestroy` или `destroyed`. Но, поскольку хуки уничтожения не будут вызываться во время SSR, таймеры останутся навсегда. Чтобы избежать этого, переместите такой код в `beforeMount` или `mounted`.
16 |
17 | ## Доступ к специализированному API платформы
18 |
19 | Универсальный код не может использовать API специализированное для какой-то конкретной платформы (platform-specific APIs), потому что если ваш код будет использовать глобальные переменные браузеров `window` или `document`, то возникнут ошибки при выполнении в Node.js, и наоборот.
20 |
21 | Для задач, разделяемых между сервером и клиентом, но использующих разные API платформы, рекомендуется создавать обёртки платформо-специфичных реализаций в универсальное API, или использовать библиотеки, которые делают это за вас. Например, [axios](https://github.com/mzabriskie/axios) — это HTTP-клиент предоставляющий одинаковое API как для сервера так и для клиента.
22 |
23 | Для API только для браузеров общий подход — ленивый (lazy) доступ к ним, внутри хуков жизненного цикла только для клиентской стороны.
24 |
25 | Обратите внимание, что если сторонняя библиотека не была написана с расчётом на универсальное использование, её может быть сложно интегрировать в приложение с серверным рендерингом. Вы *могли бы* заставить её работать, например создавая моки некоторых глобальных переменных, но это будет грязным хаком и может помешать коду обнаружения окружения в других библиотеках.
26 |
27 | ## Пользовательские директивы
28 |
29 | Большинство пользовательских директив непосредственно манипулируют DOM, поэтому будут вызывать ошибки во время SSR. Существует два способа обойти это:
30 |
31 | 1. Предпочитайте использовать компоненты в качестве механизма абстракции и работайте на уровне виртуального DOM (например, используя render-функции).
32 |
33 | 2. Если у вас есть пользовательская директива, которую нельзя легко заменить компонентами, вы можете предоставить её «серверный вариант» с помощью опции [`directives`](./api.md#directives) при создании серверного рендерера.
34 |
--------------------------------------------------------------------------------
/ko/structure.md:
--------------------------------------------------------------------------------
1 | # 소스 코드 구조
2 |
3 | ## 상태를 보존하는 싱글톤을 피하세요
4 |
5 | 클라이언트 전용 코드를 작성할 때 코드가 항상 새로운 컨텍스트에서 구동된다는 것에 익숙할 것입니다. 그러나 Node.js 서버는 오랫동안 실행되는 프로세스입니다. 프로세스에 코드가 필요한 경우 한번 계산되어 메모리에 남아있게 됩니다. 즉, 싱글톤 객체를 만들면 들어오는 모든 요청간에 공유될 수 있습니다.
6 |
7 | 기본 예제에서 보듯이, 각 요청에 따라 **새로운 루트 Vue인스턴스를 생성**합니다. 이는 각 사용자가 브라우저에서 앱의 새로운 인스턴스를 사용하는 것과 유사합니다. 서로 다른 요청에서 공유된 인스턴스를 사용하면 상호 요청 상태 오염을 일으킬 수 있습니다.
8 |
9 | 따라서 앱 인스턴스를 직접 생성하는 대신 반복적으로 실행할 수 있는 팩토리 함수를 노출하고 각 요청에 따라 새로운 앱 인스턴스를 만들어야 합니다.
10 |
11 | ```js
12 | // app.js
13 | const Vue = require('vue')
14 | module.exports = function createApp (context) {
15 | return new Vue({
16 | data: {
17 | url: context.url
18 | },
19 | template: `방문한 URL은 : {{ url }}
`
20 | })
21 | }
22 | ```
23 |
24 | 이제 서버 측 코드는 아래와 같이 변경합니다.
25 |
26 | ```js
27 | // server.js
28 | const createApp = require('./app')
29 | server.get('*', (req, res) => {
30 | const context = { url: req.url }
31 | const app = createApp(context)
32 | renderer.renderToString(app, (err, html) => {
33 | // handle error...
34 | res.end(html)
35 | })
36 | })
37 | ```
38 |
39 | 동일한 규칙이 라우터, 스토어 및 이벤트 버스 인스턴스에도 적용됩니다. 모듈에서 직접 export하고 앱에서 import 하는 대신 `createApp`에 새 인스턴스를 만들고 루트 Vue인스턴스에서 이를 주입해야합니다.
40 |
41 | > 이러한 제약 조건은 번들 렌더러를 `{ runInNewContext: true }`와 함께 사용할 때 제거할 수 있지만 각 요청에 대해 새로운 vm context를 만들어야하기 때문에 성능에 상당한 비용이 발생합니다.
42 |
43 | ## 빌드 순서 소개
44 |
45 | 지금까지 클라이언트에 동일한 Vue 앱을 전달하는 방법을 다루지 않았습니다. 이를 위해 webpack을 사용해 Vue 앱을 번들링해야합니다. 사실, webpack을 사용해 서버에 Vue 앱을 번들링해야합니다. 이유는 아래와 같습니다.
46 |
47 | - 일반적으로 Vue 앱은 webpack과 `vue-loader`로 구성되어 있으며 `file-loader`를 통해 파일을 가져오는 것, `css-loader`를 통해 CSS를 가져오는 것과 같은 많은 webpack 관련 기능은 Node.js에서 직접 작동하지 않습니다.
48 | - Node.js 최신 버전은 ES2015를 완벽히 지원하지만 이전 버전의 브라우저에서 작동할 수 있도록 만들기 위해 클라이언트 측 코드를 변환해야합니다. 이 때문에 빌드 단계가 필요합니다.
49 |
50 | 따라서 webpack을 사용하여 클라이언트와 서버 모두를 번들로 제공할 것입니다. 서버 번들은 서버에서 필요하며 SSR에 사용되며, 클라이언트 번들은 정적 마크업을 위해 브라우저로 전송됩니다.
51 |
52 | 
53 |
54 | 이후 섹션에서 설정의 세부 사항을 논의 할 것입니다. 이제 빌드 설정을 이해한다고 가정하고 webpack을 사용하여 Vue 앱 코드를 작성할 수 있습니다.
55 |
56 | ## Webpack을 이용한 코드 구조
57 |
58 | webpack을 이용해 서버와 클라이언트 모두에서 애플리케이션을 처리하므로 대부분의 소스 코드를 모든 webpack 기반으로 작성할 수 있습니다. 범용적인 코드를 작성할 때 주의해야할 [몇가지 사항](./universal.md)들이 있습니다.
59 |
60 | 간단한 프로젝트는 아래와 같을 것 입니다.
61 |
62 | ```bash
63 | src
64 | ├── components
65 | │ ├── Foo.vue
66 | │ ├── Bar.vue
67 | │ └── Baz.vue
68 | ├── App.vue
69 | ├── app.js # universal entry
70 | ├── entry-client.js # runs in browser only
71 | └── entry-server.js # runs on server only
72 | ```
73 |
74 | ### `app.js `
75 |
76 | `app.js`는 앱의 범용적인 시작 지점입니다. 클라이언트 전용 애플리케이션에서는 이 파일에 루트 Vue 인스턴스를 만들고 DOM에 직접 마운트합니다. 그러나 SSR의 경우에는 책임이 클라이언트 전용 엔트리 파일로 옮겨갑니다.
77 |
78 | ```js
79 | import Vue from 'vue'
80 | import App from './App.vue'
81 | // export a factory function for creating fresh app, router and store
82 | // instances
83 | export function createApp () {
84 | const app = new Vue({
85 | // the root instance simply renders the App component.
86 | render: h => h(App)
87 | })
88 | return { app }
89 | }
90 | ```
91 |
92 | ### `entry-client.js`:
93 |
94 | 클라이언트 엔트리는 단순히 앱을 생성하고 DOM에 마운트하는 역할만 합니다.
95 |
96 | ```js
97 | import { createApp } from './app'
98 | // client-specific bootstrapping logic...
99 | const { app } = createApp()
100 | // this assumes App.vue template root element has `id="app"`
101 | app.$mount('#app')
102 | ```
103 |
104 | ### `entry-server.js`:
105 |
106 | 서버 항목은 각 렌더링마다 반복적으로 호출할 수있는 함수인 export default를 사용합니다. 현재 이 인스턴스는 앱 인스턴스를 생성하고 반환하는 것 이외에는 하지 않지만 나중에 서버 측 라우트 매칭 및 데이터 프리-페칭 로직(pre-fetching logic)을 다룹니다.
107 |
108 | ```js
109 | import { createApp } from './app'
110 | export default context => {
111 | const { app } = createApp()
112 | return app
113 | }
114 | ```
115 |
--------------------------------------------------------------------------------
/en/caching.md:
--------------------------------------------------------------------------------
1 | # Caching
2 |
3 | Although Vue's SSR is quite fast, it can't match the performance of pure string-based templating due to the cost of creating component instances and Virtual DOM nodes. In cases where SSR performance is critical, wisely leveraging caching strategies can greatly improve response time and reduce server load.
4 |
5 | ## Page-level Caching
6 |
7 | A server-rendered app in most cases relies on external data, so the content is dynamic by nature and cannot be cached for extended periods. However, if the content is not user-specific (i.e. for the same URL it always renders the same content for all users), we can leverage a strategy called [micro-caching](https://www.nginx.com/blog/benefits-of-microcaching-nginx/) to drastically improve our app's capability of handling high traffic.
8 |
9 | This is usually done at the Nginx layer, but we can also implement it in Node.js:
10 |
11 | ``` js
12 | const microCache = LRU({
13 | max: 100,
14 | maxAge: 1000 // Important: entries expires after 1 second.
15 | })
16 |
17 | const isCacheable = req => {
18 | // implement logic to check if the request is user-specific.
19 | // only non-user-specific pages are cache-able
20 | }
21 |
22 | server.get('*', (req, res) => {
23 | const cacheable = isCacheable(req)
24 | if (cacheable) {
25 | const hit = microCache.get(req.url)
26 | if (hit) {
27 | return res.end(hit)
28 | }
29 | }
30 |
31 | renderer.renderToString((err, html) => {
32 | res.end(html)
33 | if (cacheable) {
34 | microCache.set(req.url, html)
35 | }
36 | })
37 | })
38 | ```
39 |
40 | Since the content is cached for only one second, users will not see outdated content. However, this means the server only has to perform at most one full render per second for each cached page.
41 |
42 | ## Component-level Caching
43 |
44 | `vue-server-renderer` has built-in support for component-level caching. To enable it you need to provide a [cache implementation](./api.md#cache) when creating the renderer. Typical usage is passing in an [lru-cache](https://github.com/isaacs/node-lru-cache):
45 |
46 | ``` js
47 | const LRU = require('lru-cache')
48 |
49 | const renderer = createRenderer({
50 | cache: LRU({
51 | max: 10000,
52 | maxAge: ...
53 | })
54 | })
55 | ```
56 |
57 | You can then cache a component by implementing a `serverCacheKey` function:
58 |
59 | ``` js
60 | export default {
61 | name: 'item', // required
62 | props: ['item'],
63 | serverCacheKey: props => props.item.id,
64 | render (h) {
65 | return h('div', this.item.id)
66 | }
67 | }
68 | ```
69 |
70 | Note that cache-able component **must also define a unique "name" option**. With a unique name, the cache key is thus per-component: you don't need to worry about two components returning the same key.
71 |
72 | The key returned from `serverCacheKey` should contain sufficient information to represent the shape of the render result. The above is a good implementation if the render result is solely determined by `props.item.id`. However, if the item with the same id may change over time, or if render result also relies on another prop, then you need to modify your `getCacheKey` implementation to take those other variables into account.
73 |
74 | Returning a constant will cause the component to always be cached, which is good for purely static components.
75 |
76 | ### When to use component caching
77 |
78 | If the renderer hits a cache for a component during render, it will directly reuse the cached result for the entire sub tree. This means you should **NOT** cache a component when:
79 |
80 | - It has child components that may rely on global state.
81 | - It has child components that produces side effects on the render `context`.
82 |
83 | Component caching should therefore be applied carefully to address performance bottlenecks. In most cases, you shouldn't and don't need to cache single-instance components. The most common type of components that are suitable for caching are ones repeated in big `v-for` lists. Since these components are usually driven by objects in database collections, they can make use of a simple caching strategy: generate their cache keys using their unique id plus the last updated timestamp:
84 |
85 | ``` js
86 | serverCacheKey: props => props.item.id + '::' + props.item.last_updated
87 | ```
88 |
--------------------------------------------------------------------------------
/en/css.md:
--------------------------------------------------------------------------------
1 | # CSS Management
2 |
3 | The recommended way to manage CSS is to simply use `