前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
工具
TVP
发布

Vue SSR

作者头像
用户3258338
发布2019-07-30 16:15:26
4K0
发布2019-07-30 16:15:26
举报

习惯了没有诗的日子,连夏夜的晚风,都当做是你给的惊喜~

各位宝宝,好久不见,最近还好吗?你那里下雨了吗?我这里下雨了,雨声很好听~

speak is cheap ! 开始看正文吧~

一、什么是SSR(服务端渲染)?

vue.js是构建客户端应用程序的框架,在默认情况下,在浏览器输出Vue组件,进行生成DOM和操作DOM。Vue SSR 就是实现将组件渲染为服务器端的HTML字符串,将他们直接发送给浏览器,最后将这些静态标记“激活”为客户端可应用的应用程序。

二、为什么使用SSR

1. 更快的内容到达时间(time-to-content)

之前我们用vue-cli搭建的单页面(SPA)的应用,在我们第一次请求时,服务端返回我们的是一个HTML,外链式的引入了js和css。

代码语言:javascript
复制
<!DOCTYPE html>
<html>

<head>
  <meta charset="utf-8">
  <link rel="shortcut icon" type=image/x-icon href=static/favicon.ico>
  <link href=/static/css/app.XXX.css rel=stylesheet>
  <script type=text/javascript src=/static/js/manifest.XXX.js></script>
  <script type=text/javascript src=/static/js/vendor.XXX.js></script>
  <script type=text/javascript src=/static/js/app.XXX.js></script>
  <meta name="viewport" content="width=device-width,initial-scale=1.0">
  <title>我是title</title>
</head>
<body>
  <div id="app"></div>
</body>
</html>

这样的静态项目要等待js下载完成,执行去渲染DOM。如果页面中的数据需要去服务器请求,则得等到请求返回之后才能渲染出用户想要的页面。然而SSR就不同了,SRR的过程是:

用户访问某页面 -->

服务端接到请求-->

请求数据(此时如果是数据来源于同一个服务器,那就更快了)-->

根据数据渲染出一个HTML字符串直接返回给客户端

所以用户能够更快的看到完整的渲染页面。

2. 更好的SEO

通常情况下SPA应用,要进行异步请求然后展示数据。。但是Google能够很好的进行同步的Javascript应用程序进行索引,它不会等待你的数据回来在进行抓取页面内容。SRR 因为是同步返回整个页面的HTML字符串,是包含程序重要信息的完成页面,so,SSR相比于SPA应用来说能够有利于网站的SEO。

三、核心

Vue SSR核心用代码展示如下:

代码语言:javascript
复制
const Vue = require('vue')
const server = require('express')()
const renderer = require('vue-server-renderer').createRenderer()
// 客户端发出任何请求
server.get('*', (req, res) => {
// 创建一个vue实例
  const app = new Vue({
    data: {
      url: req.url
    },
    template: `<div>访问的 URL 是:{{ url }}</div>`
  })
 // 传入Vue实例 app 和一个回调函数
  renderer.renderToString(app, (err, html) => {
    if (err) {
      res.status(500).end('Internal Server Error')
      return
    }
    res.end(`
      <!DOCTYPE html>
      <html lang="en">
        <head><title>Hello</title></head>
        <body>${html}</body>
      </html>
    `)
  })
})

server.listen(8080)

四、生命周期

只有beforeCreate和created 会在服务端渲染过程中被调用。其他的生命周期钩子函数中的代码,只会在客户端执行。

所以在以上两个生命周期中?写有副作用的代码!比如在纯客户端应用程序中,我们可能会在beforeCreate或者created中设置定时器,然后在beforeDestroy或者destroyed时将其销毁。但是在SSR期间并不会调用销毁钩子函数,所以timer就会一直在。所以为了避免这种情况,可以将副作用代码移到beforMounted 或mounted中。

四、避免创建单例

Node.js服务器是一个长期运行的程序。当我们的代码进入该进程时,他将进行一次取值并留存在内存中。这意味着如果创建一个单例对象,他将在每个传入的请求之间共享。

所以,我们为每个请求创建一个新的根Vue实例。这与每个客户在自己的浏览器中使用新应用程序类似。如果我们在多个请求之间使用一个共享的实例,很容易导致交叉请求状态污染(cross-request state pollution).

所以我们应该暴露一个可以重复执行的工厂函数,为每个请求创建一个新的应用程序实例

代码语言:javascript
复制
// app.js
const Vue = require('vue')

module.exports = function createApp (context) {
  return new Vue({
    data: {
      url: context.url
    },
    template: `<div>访问的 URL 是:{{ url }}</div>`
  })
}

现在我们的服务器代码现在变成:

代码语言:javascript
复制
// server.js
const createApp = require('./app')

server.get('*', (req, res) => {
  const context = { url: req.url }
  const app = createApp(context)
  renderer.renderToString(app, (err, html) => {
    // 处理错误……
    res.end(html)
  })
})

同样的规则也是用于router、store 、event bus 实例。

四、构建步骤

对于客户端应用程序和服务端应用程序我们都需要webpack打包成响应的环境能够是别的程序语言。

Vue SSR打包结果就是生成用于服务端渲染的’服务器‘bundle’,和发送给浏览器的‘客户端bundle’,用户混合静态标记。如图:

五、使用webpack的源码结构

一个基本项目可能像这样:

代码语言:javascript
复制
src
├── components
│   ├── Foo.vue
│   ├── Bar.vue
│   └── Baz.vue
├── App.vue
├── app.js # 通用 entry(universal entry)
├── entry-client.js # 仅运行于浏览器
└── entry-server.js # 仅运行于服务器

app.js : 导出一个工厂函数

代码语言:javascript
复制
import Vue from 'vue'
import App from './App.vue'

// 导出一个工厂函数,用于创建新的
// 应用程序、router 和 store 实例
export function createApp () {
  const app = new Vue({
    // 根实例简单的渲染应用程序组件。
    render: h => h(App)
  })
  return { app }
}

entry-client.js 创建应用程序

代码语言:javascript
复制
import { createApp } from './app'

// 客户端特定引导逻辑……

const { app } = createApp()

// 这里假定 App.vue 模板中根元素具有 `id="app"`
app.$mount('#app')

entry-server.js 导出函数

代码语言:javascript
复制
import { createApp } from './app'

export default context => {
  const { app } = createApp()
  return app
}

五、客户端激活

客户端激活:Vue在浏览器端接管由服务器端发送的静态HTML,使其变为有Vue管理的动态DOM的过程;

在entry-client.js中,挂载应用程序

代码语言:javascript
复制
// 这里假定 App.vue template 根元素的 `id="app"`
app.$mount('#app')

五、构建配置

我们之前已经了解到我们需要对服务端和客户端两端分别进行打包,所以建议将配置分为三个文件:base ,client, server。base包含两个环境共享的配置。

5.1 服务端配置

代码语言:javascript
复制
const merge = require('webpack-merge')
const nodeExternals = require('webpack-node-externals')
const baseConfig = require('./webpack.base.config.js')
const VueSSRServerPlugin = require('vue-server-renderer/server-plugin')

module.exports = merge(baseConfig, {
  // 将 entry 指向应用程序的 server entry 文件
  entry: '/path/to/entry-server.js',

  // 这允许 webpack 以 Node 适用方式(Node-appropriate fashion)处理动态导入(dynamic import),
  // 并且还会在编译 Vue 组件时,
  // 告知 `vue-loader` 输送面向服务器代码(server-oriented code)。
  target: 'node',

  // 对 bundle renderer 提供 source map 支持
  devtool: 'source-map',

  // 此处告知 server bundle 使用 Node 风格导出模块(Node-style exports)
  output: {
    libraryTarget: 'commonjs2'
  },

  // https://webpack.js.org/configuration/externals/#function
  // https://github.com/liady/webpack-node-externals
  // 外置化应用程序依赖模块。可以使服务器构建速度更快,
  // 并生成较小的 bundle 文件。
  externals: nodeExternals({
    // 不要外置化 webpack 需要处理的依赖模块。
    // 你可以在这里添加更多的文件类型。例如,未处理 *.vue 原始文件,
    // 你还应该将修改 `global`(例如 polyfill)的依赖模块列入白名单
    whitelist: /\.css$/
  }),

  // 这是将服务器的整个输出
  // 构建为单个 JSON 文件的插件。
  // 默认文件名为 `vue-ssr-server-bundle.json`
  plugins: [
    new VueSSRServerPlugin()
  ]
})

emmm,说了这么多,发现自己配置Vue SSR的整个项目真的是够复杂,SO,推荐各位宝宝使用Nuxt.js。。但是知道这些原理会对Nuxt.js帮助巨大的哦!!!

愿我们有能力不向生活缴械投降---Lin

本文参与 腾讯云自媒体分享计划,分享自微信公众号。
原始发表:2019-07-28,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 女程序员的日常 微信公众号,前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体分享计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档