搭建Vue 服务端渲染项目,了解SSR

372 阅读2分钟

一.什么是SSR

在浏览器请求页面输入URL的时候,组件将渲染为服务器端的 HTML 字符串,将它们直接发送到浏览器,最后将这些静态标记"激活"为客户端上完全可交互的应用程序

  • 怎么渲染成HTML字符串?【renderTostring()】

  • 怎么把HTML字符串激活?【app.$mount('#app')】

二.为什么使用SSR

1.传统的服务端渲染

使用的是后端模版渲染【如jsp或php】使用模板引擎将模板与数据拼接成完整的HTML

缺点:

  • a-前后端职责不清楚
  • b-前后端代码杂糅在一起
  • c-项目难以管理维护

优点:

  • a-减少白屏的时间
  • b-seo友好(html带有页面需要的内容)

2.客户端渲染 CSR

用js来渲染页面大部分内容,spa单页面应用(如:react,vue)在客户端请求时,服务端不做任何处理,将前端打包好后生成的HTML反给客户端

补:Vue组件渲染流程

组件声明-组件创建-渲染

组件声明:Vue的全局静态方法Vue.component进行注册,组件内部利用components属性进行注册,组件是可复用的 Vue 实例【Vue.component继承Vue】

组件创建:组件注册之后就会在模板里进行声明使用,模板里声明使用的组件最后回被vue-loader编译返回一个渲染函数render

  • a-调用createElement创建节点
  • b-自定义你组件调用【createComponent-installComponentHooks-componentVNodeHooks源码的大体调用步骤,也是创建节点】

渲染:$mount-patch-createEle 【递归的将组件里的vnode渲染成正式DOM】

问题:

  • a-首屏加载慢
  • b-SEO不友好
  • c-客户端渲染至少发起三次请求【第一次请求页面,第二次请求页面里面的js,第三次动态数据请求】
  • d-需要重复渲染【发送请求获取后台数据一般在mounted,页面已经渲染一次,获取到数据,更新视图,页面又需要再渲染一次】

优点:

  • a-前后端代码解藕
  • b-可以实现局部刷新【两端分别请求】

3.同构渲染 SSR

相同代码在服务端运行一遍,在达浏览器又运行一遍【服务端渲染完成页面结构,客户端渲染绑定事件】

问题:

  • a-开发条件首先【客户端和服务端渲染在写法上的区别容易造成bug】
  • b-项目构建和部署【依赖于node环境】
  • c-更多的服务器负载

优点:

  • a-更好的SEO
  • b-更快的加载速度
  • c-前后端代码解藕

三.Vue SSR实战

在此之前应该先了解Vue SSR指南ssr.vuejs.org/zh/

截图.png 上图是Vue官方的SSR原理介绍图片。从这张图片,我们可以知道:我们需要通过Webpack打包生成两份bundle文件:

  • Client Bundle,给浏览器用。和纯Vue前端项目Bundle类似
  • Server Bundle,供服务端SSR使用,一个json文件

1.初始化项目

mkdir vue-ssr
cd vue-ssr
npm init -y 
npm i vue vue-server-renderer

2.创建server.js(在根目录下)

// 第 1 步:创建一个 Vue 实例
const Vue = require('vue')
const app = new Vue({
  template: `
    <div id="app">
      <h1>{{ message }}</h1>
    </div>
  `,
  data: {
    message: 'MESSAGE'
  }
})

// 第 2 步:创建一个 renderer
const renderer = require('vue-server-renderer').createRenderer()

// 第 3 步:将 Vue 实例渲染为 HTML
renderer.renderToString(app, (err, html) => {
  if (err) throw err
  console.log(html)
  // => <div id="app" data-server-rendered="true"><h1>时光海</h1></div>
})

与服务器集成

npm run express

当前server.js

const express = require('express')
const server = express()

server.get('/', (req, res) => {
  const app = new Vue({
    template: `
      <div id="app">
        <h1>{{ message }}</h1>
      </div>
    `,
    data: {
      message: 'MESSAGE'
    }
  })
  const renderer = require('vue-server-renderer').createRenderer()
  
  renderer.renderToString(app, (err, html) => {
    if (err) {
      res.status(500).end('Internal Server Error.')
    }
    // 解决编码问题
    res.setHeader('Content-Type', 'text/html; charset=utf-8')
    res.end(html)
  })
})

server.listen(3000, () => {
  console.log('server runing at port 3000.')
})

启动服务器

nodemon server.js

4.创建模版index.template.html

<!DOCTYPE html>
<html lang="en">

<head>
  <title>Hello</title>
  <meta charset="UTF-8">
</head>

<body>
  <!-- html字符串注入 -->
  <!--vue-ssr-outlet-->
</body>

</html>

再模板中使用外部的数据

当前的server.js

const express = require('express')
const fs = require('fs')

const renderer = require('vue-server-renderer').createRenderer({
  template: fs.readFileSync('./index.template.html',
  'utf-8')
})
const server = express()

server.get('/', (req, res) => {
  const app = new Vue({
    template: `
      <div id="app">
        <h1>{{ message }}</h1>
      </div>
    `,
    data: {
      message: 'MESSAGE'
    }
  })
  
  renderer.renderToString(app, {
    title: 'VUE_SSR',
    meta: `
      <meta name="description" content="MESSAGE">
    `
    },  (err, html) => {
      if (err) {
        res.status(500).end('Internal Server Error.')
      }
      // 解决编码问题
      res.setHeader('Content-Type', 'text/html; charset=utf-8')
      res.end(html)
    }
  )
})

server.listen(3000, () => {
  console.log('server runing at port 3000.')
})

当前index.template.html

<!DOCTYPE html>
<html lang="en">

<head>
  <title>{{ title }}</title>
  <meta charset="UTF-8">
  {{{ meta }}}
</head>

<body>
  <!-- html字符串注入 -->
  <!--vue-ssr-outlet-->
</body>

</html>

构建同构渲染 源码查看github.com/EdaDuan/vue…

在demo中有注释 可以直接pull下来运行