Coding With Fun
Home Docker Django Node.js Articles Python pip guide FAQ Policy

Vue.js 2.0 service-side rendering


May 07, 2021 Vue.js 2.0


Table of contents


Vue.js 2.0 service-side rendering

Need Service-side Rendering (SSR)?

Before we start rendering on the service side, let's see what it brings to us and when we need to use it.

SEO (Search Engine Optimization)

Google and Bing can index synchronized JavaScript apps well. S ynchronization is a key word here. If the app starts with a load animation and then the content is acquired through ajax, the crawlers won't wait for them to load.

This means that end-of-service rendering is important when search engine optimization is needed on pages that get content asynchronously.

The client's network is slow

Users may access the site from a distance when the network is slow - or through poor bandwidth. In these cases, minimize the number of page requests to ensure that users see the basic content as quickly as possible.

Webpack code can be split to avoid forcing users to download the entire single-page application, but this is far from the high performance of downloading a single pre-rendered HTML file.

The client runs on an old (or directly unavailable) JavaScript engine

For some parts of the world, computers may only be accessible by computers made in 1998. Vue can only run browsers above IE9, and you can also think of providing the basic content for older browsers - or fashionable hackers using Lynx on the command line.

Service-side rendering contrast Prerendering

If you're just using service-side rendering to improve the SEO of a small number of marketing pages (e.g. home page, about, contacts, etc.), you can replace it with pre-rendering. P re-rendering doesn't compile HTML as instantly as server rendering, and pre-rendering only builds a specific few static pages for a particular route at build time. The advantage is that the pre-rendering setup is simpler and keeps the front end a complete static station.

You can simply add pre-rendering with webpack through prerender-spa-plugin, which is widely used in Vue applications - in fact, the creator is also a member of Vue's core team.

Hello World

Get ready to experience service-side rendering in action. Service-side rendering (i.e. SSR) sounds complicated, but a simple Node script takes only 3 steps to do this:

// 步骤 1:创建一个Vue实例
var Vue = require('vue')
var app = new Vue({
  render: function (h) {
    return h('p', 'hello world')
  }
})
// 步骤 2: 创建一个渲染器
var renderer = require('vue-server-renderer').createRenderer()
// 步骤 3: 将 Vue实例 渲染成 HTML
renderer.renderToString(app, function (error, html) {
  if (error) throw error
  console.log(html)
  // => <p server-rendered="true">hello world</p>
})

It's not difficult. O f course, this example is simpler than most applications. We don't have to worry about:

  • A Web server
  • Streaming response
  • Component cache
  • The build process
  • Routing
  • Vuex state management

For the rest of this guide, we'll explore how these features work. Once you understand the underlying, we'll provide more details and further examples to help you resolve unexpected situations.

Simple end-of-service rendering with express web servers

Without a Web server, it's hard to say that it's server-side rendering, so let's complement it. We're going to build a very simple service-side rendering application with only ES5 and no other build steps or Vue plug-ins.

Launch an app to tell users how much time they spend on a page.

new Vue({
  template: '<div>你已经在这花了 {{ counter }} 秒。</div>',
  data: {
    counter: 0
  },
  created: function () {
    var vm = this
    setInterval(function () {
      vm.counter += 1
    }, 1000)
  }
})

To accommodate end-of-service rendering, we need to make some modifications so that it can be rendered in the browser and Node:

  • In the browser, add our app instance to the global context (window), and we can install it.
  • In Node, exporting a factory function allows us to create an app instance for each request.

Implementing this requires a bit of a template:

// assets/app.js
(function () { 'use strict'
  var createApp = function () {
    // ---------------------
    // 开始常用的应用代码
    // ---------------------
    // 主要的Vue实例必须返回,并且有一个根节点在id "app"上,这样客户端可以加载它。
    return new Vue({
      template: '<div id="app">你已经在这花了 {{ counter }} 秒。</div>',
      data: {
        counter: 0
      },
      created: function () {
        var vm = this
        setInterval(function () {
          vm.counter += 1
        }, 1000)
      }
    })
    // -------------------
    // 结束常用的应用代码
    // -------------------
  }
  if (typeof module !== 'undefined' && module.exports) {
    module.exports = createApp
  } else {
    this.app = createApp()
  }
}).call(this)

Now that you have the application code, add an html file.

<!-- index.html -->
<!DOCTYPE html>
<html>
<head>
  <title>My Vue App</title>
  <script src="/assets/vue.js"></script>
</head>
<body>
  <div id="app"></div>
  <script src="/assets/app.js"></script>
  <script>app.$mount('#app')</script>
</body>
</html>

Mainly referencing the app files we created earlier in the .js folder, as well as the vue .js files, we have a single-page application that we can run

Then, in order to achieve service-side rendering, you need to add a step on the service-side.

// server.js
'use strict'
var fs = require('fs')
var path = require('path')
// 定义全局的Vue为了服务端的app.js
global.Vue = require('vue')
// 获取HTML布局
var layout = fs.readFileSync('./index.html', 'utf8')
// 创建一个渲染器
var renderer = require('vue-server-renderer').createRenderer()
// 创建一个Express服务器
var express = require('express')
var server = express()
// 部署静态文件夹为 "assets"文件夹
server.use('/assets', express.static(
  path.resolve(__dirname, 'assets')
))
// 处理所有的Get请求
server.get('*', function (request, response) {
  // 渲染我们的Vue应用为一个字符串
  renderer.renderToString(
    // 创建一个应用实例
    require('./assets/app')(),
    // 处理渲染结果
    function (error, html) {
      // 如果渲染时发生了错误
      if (error) {
        // 打印错误到控制台
        console.error(error)
        // 告诉客户端错误
        return response
          .status(500)
          .send('Server Error')
      }
      // 发送布局和HTML文件
      response.send(layout.replace('<div id="app"></div>', html))
    }
  )
})
// 监听5000端口
server.listen(5000, function (error) {
  if (error) throw error
  console.log('Server is running at localhost:5000')
})

That's it. ple, clone down the depth experiment. O nce it's running locally, you can confirm that the service selection rendering is really running by right-clicking select page resources (or something like that) on the page. You can see it in body:

<div id="app" server-rendered="true">You have been here for 0 seconds.</div>

Replace:

<div id="app"></div>

Streaming response

Vue also supports streaming, giving preference to Web servers that support streaming. A llows THEML side generation to typically write to the corresponding stream, rather than writing all at the last time. The result is faster requests for services with no drawbacks!

In order for the previous section to apply the code to streaming, you can simply replace server.get (',...') For the following code:

// 拆分布局成两段HTML
var layoutSections = layout.split('<div id="app"></div>')
var preAppHTML = layoutSections[0]
var postAppHTML = layoutSections[1]
// 处理所有的Get请求
server.get('*', function (request, response) {
  // 渲染我们的Vue实例作为流
  var stream = renderer.renderToStream(require('./assets/app')())
  // 将预先的HTML写入响应
  response.write(preAppHTML)
  // 每当新的块被渲染
  stream.on('data', function (chunk) {
    // 将块写入响应
    response.write(chunk)
  })
  // 当所有的块被渲染完成
  stream.on('end', function () {
    // 将post-app HTML写入响应
    response.end(postAppHTML)
  })
  // 当渲染时发生错误
  stream.on('error', function (error) {
    // 打印错误到控制台
    console.error(error)
    // 告诉客服端发生了错误
    return response
      .status(500)
      .send('Server Error')
  })
})

It's no more complicated than the previous version, and it's not even a new concept for you. We did:

  1. Establish a flow
  2. Write HTML before applying the response
  3. HTML write responses are applied when available
  4. Html is written at the end of the response
  5. Handle any errors

Component cache

Vue's service-side rendering is very fast by default, but you can further improve performance by caching rendered components. T his is considered an advanced feature, but if the wrong component is cached (or the correct component has the wrong contents), an app rendering error will result. In particular:

Components that contain sub-components should not be cached depending on global states, such as those from vuex. I f you do this, the sub-components (in fact, the entire sub-tree) will also be cached. So pay special attention to situations with slots fragments or sub-components.

Set up

In addition to warning situations, we can cache components in the following way.

First, you need to provide the renderer with a cached object. Here's a simple example using lru-cache

var createRenderer = require('vue-server-renderer').createRenderer
var lru = require('lru-cache')
var renderer = createRenderer({
  cache: lru(1000)
})

This caches up to 1000 individual renderings. For configurations that are further cached into the content, look at the lru-cache settings

Then for the components you want to cache, you can provide them with:

  • A unique name
  • A serverCacheKey function that returns a unique component scope

For example:

Vue.component({
  name: 'list-item',
  template: '<li>{{ item.name }}</li>',
  props: ['item'],
  serverCacheKey: function (props) {
    return props.item.type + '::' + props.item.id
  }
})

The ideal component for caching

Any pure component can be securely cached - this is guaranteed to pass the same data to any component to produce the same HTML. Examples of these scenarios include:

  • Static components (e.g. always try the same HTML, so the serverCacheKey function can be returned true)
  • List components (when there are a large number of lists, caching them can improve performance)
  • Universal UI components (e.g. buttons, alerts, etc. - at least they get data through props instead of slots or sub-components)

Build procedures, routing, and Vuex state management

It's time to understand the basic concepts behind service-side rendering. However, the build process, routing, and Vuex each have their own considerations.

To truly master service-side rendering in complex applications, we recommend a deep understanding of the following resources: