3-4-01-搭建自己的 Server Side Render

2023-05-16

搭建自己的 Server Side Render

Vue 实例的服务端渲染

  • 使用 vue-server-renderer 插件完成 vue 实例的服务端渲染
  • 使用 express 创建一个 node 服务器
  • fs 模块读取 html 模板
const Vue = require('vue')
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: 'hahah'
    }
  })
  
  renderer.renderToString(app, {
    title: 'hello world!',
    meta: `
      <meta name="description" content="hello world">
    `
  }, (err, html) => {
    if (err) {
      return res.status(500).end('Internal Server Error.')
    } else {
      res.setHeader('Content-Type', 'text/html; charset=utf-8')
      res.end(html)
    }
  })
})

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

html 模板:

<!DOCTYPE html>
<html lang="en">
  <head>
    <meta charset="UTF-8" />
    <meta name="viewport" content="width=device-width, initial-scale=1.0" />
    {{{ meta }}}
    <title>{{ title }}</title>
  </head>
  <body>
    <!--vue-ssr-outlet-->
  </body>
</html>
  • 注意这里的占位符 <!--vue-ssr-outlet--> 两边不要有空格,否则渲染失败。
  • 如果要渲染 html 标签,需要使用三个大括号。
  • 解决页面乱码问题,最好的解决方式就是在请求头中添加 meta 标签,同时在响应头中设置 Content-type

构建配置

基本思路

服务端渲染就是将 Vue 实例渲染成纯静态的 HTML 字符串然后发送给客户端。所以,Vue 中的交互功能在客户端是不能实现的,比如说,@click 点击事件,v-model 数据双向绑定,这种的通过服务端渲染之后发送给客户端渲染的方式是无效的。因为服务端渲染完发送给客户端的是纯字符串,客户端负责完成的只是将这个 html 字符串进行渲染,至于 Vue 应用中的行为是没有的。

在这里插入图片描述

上图是 Vue Server Side Render 的源码结构

SSR 应用需要有两个 webpack 打包的入口一个是 Server entry 另一个是 Client entry ,分别打包生成 Server bundle 和 Client bundle 。

Server bundle 就是用于服务端渲染,如果想要服务端渲染的内容在客户端拥有动态交互能力就需要 Client bundle 用于客户端渲染,接管服务端渲染的内容激活成一个动态页面。这就是整个同构应用的实现流程。

源代码结构

同构应用的实现主要是分为两个部分,一部分是源代码的组织以及打包,另一部分就是将打包后的代码在服务端运行。

源码结构

入口文件完成之后接下来就是使用 webpack 打包,将服务端不能识别和运行的代码以及文件处理成可以在服务端直接运行的代码。

安装依赖

- 生产依赖

yarn add vue vue-server-renderer express cross-env
说明
vueVue.js 核心库
vue-server-rendererVue 服务端渲染工具
express基于 Node 的 Web 服务框架
cross-env通过 npm scripts 设置跨平台环境变量

- 开发依赖

yarn add --dev webpack webpack-cli webpack-merge webpack-node-externals @babel/core @babel/plugin-transform-runtime @babel/preset-env babel-loader css-loader url-loader file-loader rimraf vue-template-compiler friendly-errors-webpack-plugin
说明
webpackwebpack 核心包
webpack-cliwebpack 的命令行工具
webpack-mergewebpack 配置信息合并工具
webpack-node-externals排除 webpack 中的 Node 模块
@babel/corebabel 相关工具
@babel/plugin-transform-runtimebabel 相关工具
@babel/preset-envbabel 相关工具
babel-loaderbabel 相关工具
vue-loader处理 .vue 资源
vue-template-compiler处理 .vue 资源
css-loader处理 css 资源
url-loader处理图片资源
file-loader处理字体资源
rimraf基于 Node 封装的一个跨平台 rm -rf 工具 (通过它可以在命令行中做一些删除操作,打包之前清除原有的 dist)
friendly-errors-webpack-plugin友好的 webpack 错误提示

配置文件及打包命令

初始化 webpack 打包配置文件

# build
  # webpack.base.config.js # 公共配置
  # webpack.client.config.js # 客户端打包配置文件
  # webpack.server.config.js # 服务端打包配置文件

配置打包命令

"scripts": {
  "build:client": "cross-env NODE_ENV=production webpack --config build/webpack.client.config.js",
  "build:server": "cross-env NODE_ENV=production webpack --config build/webpack.server.config.js",
  "build": "rimraf dist && npm run build:client && npm run build:server"
},

启动应用

运行 yarn build 构建应用,然后使用 node 执行 server.js 启动服务。这时候项目启动起来了,访问网页还会有问题就是交互功能依然不能正常使用,这是因为并没有把打包后的 dist 目录开放出来,请求 js 资源文件失败。解决方法就是开放 dist 文件,在服务器中将 dist 文件开放出来。

处理方法,在 server 挂载一个处理静态资源的中间件.

const server = express()

server.use('/dist', express.static('./dist'))

当访问 ‘/dist’ 开头的资源的时候,使用 express.static 中间件尝试去 dist 目录中查找对应的资源并返回。

然后应用就启动成功了,客户端的 vue 也能正常工作了。

解析渲染流程

服务端渲染

如何生成网页?

当客户端发起请求以后匹配到路由在服务端调用 renderToString() 方法将 Vue 实例完成渲染并返回给客户端。

Vue 实例的渲染关键就在服务端渲染的 serverBundle 中,这个 json 文件中包含了服务端渲染你的入口(entry),以及入口文件的代码(files),以及入口文件代码的 sourcemap (maps)

客户端渲染

如何接管服务端渲染的静态网页并激活为可以交互的应用?

显然,客户端需要将服务端返回的结果挂载到模板中并渲染。这些的关键就是 clientManifest ,它是客户端打包资源的构建清单,清单中描述了客户端构建出的资源相关的信息。

key说明
publicPath打包资源的出口(存放目录)
all客户端打包生成的所有资源文件
initialsercerrender 渲染的时候会把这个里面的资源自动的注入到 template 中
async存储异步资源的资源信息(比如在代码中加载的异步组件、异步的 js 模块等)
modules原始模块的依赖信息

详细解释以及注意事项,请看官方文档

开发模式下的自动化配置(含热更新)

前面完成的构建过程只能满足项目的生产模式下的构建和打包,如果是再开发模式下就需要每次更改项目代码之后都去手动的运行脚本重新构建,这种模式下的开发显然是不合理的。所以需要单独的配置开发模式的构建过程,使其可以自动地监听文件的变化,自动的构建项目。

实现开发模式构建的关键是 server.js 中的 renderer 方法,这个方法是通过接收服务端和客户端打包的结果使用 createBundleRenderer 方法创建出来的渲染函数。在生产模式下,只需要使用打包结果去创建就可以了,但是在开发模式下每次更新代码之后都需要重新打包生成最新的资源文件,并且用这个最新的资源文件生成最新的 renderer 方法进行渲染。

const fs = require('fs')
const path = require('path')
const chokidar = require('chokidar')
const webpack = require('webpack')
const devMiddleware = require('webpack-dev-middleware')
const hotMiddleware = require('webpack-hot-middleware')


const resolve = file => path.resolve(__dirname, file)

module.exports = (server, callback) => {
  let ready
  const onReady = new Promise(r => ready = r)

  // 监视构建 -> 更新 renderer
  let template
  let serverBundle
  let clientManifest

  const update = () => {
    if (template && serverBundle && clientManifest) {
      callback(serverBundle, template, clientManifest)
      ready()
    }
  }

  // 监视构建 template -> 调用 update -> 更新 renderer 渲染器
  const templatePath = resolve('../index.template.html')
  template = fs.readFileSync(templatePath, 'utf-8')
  update()
  // fs.watch\fs.watchFile\第三方包 chokidar
  chokidar.watch(templatePath).on('change', () => {
    template = fs.readFileSync(templatePath, 'utf-8')
    update()
  })

  // 监视构建 serverBundle -> 调用 update -> 更新 renderer 渲染器
  const serverConfig = require('./webpack.server.config')
  const serverCompiler = webpack(serverConfig)
  const serverDevMiddleware = devMiddleware(serverCompiler, {
    logLevel: 'silent' // 关闭日志输出,由 FriendlyErrorsWebpackPlugin 处理
  })
  // 存在内存中
  // 编译结束的钩子函数
  serverCompiler.hooks.done.tap('server', () => {
    serverBundle = JSON.parse(
      // 在 devMiddleware 的文件系统中读取文件
      serverDevMiddleware.fileSystem.readFileSync(resolve('../dist/vue-ssr-server-bundle.json', 'utf-8'))
    )
    update()
  })
  // 存在disk中
  // serverCompiler.watch({}, (err, states) => {
  //   if (err) throw err
  //   if (states.hasErrors()) return
  //   serverBundle = JSON.parse(
  //   // 这里不能用 require 去加载 json 文件,因为 require 是由缓存的。
  //     fs.readFileSync(resolve('../dist/vue-ssr-server-bundle.json', 'utf-8')) // 默认是 buffer
  //   )
    
  //   console.log(serverBundle)
  //   update()
  // })

  // 监视构建 clientManifest -> 调用 update -> 更新 renderer 渲染器
  const clientConfig = require('./webpack.client.config')
  clientConfig.plugins.push(new webpack.HotModuleReplacementPlugin())
  clientConfig.entry.app = [
    'webpack-hot-middleware/client?quiet=true&reload=true', // 和服务端交互处理热更新的一个客户端脚本
    clientConfig.entry.app
  ]
  clientConfig.output.filename = '[name].js' // 热更新模式下确保一致的 hash
  const clientCompiler = webpack(clientConfig)
  const clientDevMiddleware = devMiddleware(clientCompiler, {
    publicPath: clientConfig.output.publicPath,
    logLevel: 'silent' // 关闭日志输出,由 FriendlyErrorsWebpackPlugin 处理
  })
  // 存在内存中
  clientCompiler.hooks.done.tap('client', () => {
    clientManifest = JSON.parse(
      clientDevMiddleware.fileSystem.readFileSync(resolve('../dist/vue-ssr-client-manifest.json', 'utf-8'))
    )
    update()
  })
  server.use(hotMiddleware(clientCompiler, {
    log: false //关闭它本身的日志输出
  }))

  // 将 clientDevMiddleware 挂载到 Express 服务中,提供对其内部内存中数据的访问
  server.use(clientDevMiddleware)

  return onReady
}
本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

3-4-01-搭建自己的 Server Side Render 的相关文章

  • Symfony 命令中的 RenderView 用法

    如何在 symfony 命令内 而不是在控制器内 使用 this gt renderView 我对 renderView 功能很陌生 但是我必须设置什么才能在命令中使用它 预先感谢您的问候 您的命令类必须扩展ContainerAwareCo
  • python创建DDE服务器并不断发送数据

    我正在尝试用 python 编写一个 DDE 服务器 它需要将不断变化的字符串发送到作为 DDE 客户端连接的程序 连接到 DDE 服务器的程序使用以下 DDE 设置来连接 服务 Orbitron 主题 Tracking 项目 Tracki
  • 用于发送字符串数据的简单 rpyc 客户端和服务器

    我正在使用 rpyc 在 python 中编写一个程序 我的目标是创建一个简单的服务器 它接受来自客户端的数据字节 字符串 我对 python 和 rpyc 都很陌生 这是我的 server py 代码 from rpyc utils se
  • MongoError:拓扑已关闭,尽管已建立数据库连接,仍请连接

    我正在编写一个 Web 应用程序 它使用异步数据库请求作为 api 的一部分 目前 我有一个异步快速路由 等待异步函数返回函数 这两个函数都返回布尔值并且都查询数据库 第一个可以正常工作 但第二个却不能 这是 MongoClient 设置
  • Paramiko 服务器:向客户端发出 stdout 已关闭的信号

    尝试在中实现测试服务器paramiko 无需修改客户端进行测试 我偶然发现了如何关闭的问题stdout流 使 stdout read 不会永远挂起 而不会在客户端的级别太低 到目前为止 我已经能够通过以下方式传达已完成的命令 简单文本输出到
  • Ruby on Rails:如何将文件呈现为纯文本(没有任何 HTML)

    如何在 Rails 中将文件渲染为纯文本 文本 不渲染任何 HTML 我试过了 render file path to file layout false and render file path to file content type
  • Tomcat 和 TomEE、TomEE 和 TomEE Plus 之间有什么区别

    我想在服务器中部署 EJB Ear 但我对选择服务器感到非常困惑tomcat TomEE and TomEE Plus 两者有什么区别Tomcat and TomEE 其中有哪些新功能TomEE and TomEE Plus 在什么情况下才
  • Mandrill:渲染模板时条件合并标签不起作用

    我尝试过条件合并标签 即类似 如果 MY NUM 0 发送电子邮件时它按预期工作 但是 当我尝试渲染模板时 给定相同的 global merge vars 和 merge vars IF 合并标记块中的所有内容都保持空白 我得到了渲染的 h
  • 通过PHP从网站上传文件到Amazon EC2服务器

    我有一个网站 bedatify com 我想创建一个页面 人们可以在其中将图像上传到我的亚马逊 EC2 服务器 我检查了类似的问题 例如无法在 Amazon EC2 上上传文件 php https stackoverflow com que
  • 我使用 jsp/jstl/spring 动态生成 css 和 js。如何将此结果放置在头部的链接标记中。不在头部的脚本标签中

    我在jsp中生成css 输出是 现在我在 jsp 中使用以下代码调用此代码 它有效并且输出是这样的
  • 无法在服务器端 NodeJS 启用 CORS

    我无法启用CORS在服务器端 我的前端和后端服务器有不同的端口 服务器端的实现方式如下 http createServer function req res Here you can create your data response in
  • 仅打印 drupal field_view_field 值

    我使用下面的代码将节点字段打印到特定区域 效果很好 但有一个例子 我只想打印没有标签的字段值 看起来应该很容易 但我遇到了一些麻烦 我很感激任何帮助 因为我对 drupal 还很陌生 谢谢 field view value 需要一个 dis
  • 为什么turn服务器不支持tcp连接?

    我是 WebRTC 新手 我需要为我的 webrtc 应用程序配置我自己的 Turn 服务器 我使用以下命令安装了我的转弯服务器 apt get install coturn 我只需要通过 tcp 运行转变服务器 它不必使用 UDP 进行任
  • Laravel Vue js spa 应用程序

    1 我想知道为什么人们使用两台服务器用 laravel 制作 vuejs SPA 我想我们可以用另一种方式 制定这样的路线 Route get any function return view index gt where any 并让 v
  • IIS 如何识别请求的是哪个站点?

    如果我在一台服务器上托管多个站点 并且 dns 服务器将不同的域名解析到同一地址 这是服务器的名称 那么 IIS 如何知道最终请求的是哪个站点 因此 客户端输入我的 1 站点地址 gt myrandomsite mydomain com 然
  • .htaccess 主要用于 localhost

    我在本地主机上使用 wamp server 虚拟主机名为www xyz com我有一个通过 git 设置的部署服务 它将代码部署到服务器上www xyz in 这是我的 htaccess 代码 RewriteEngine on Rewrit
  • PHP 和 ADODB 连接失败

    我的任务是迁移服务器 这包括移动我没有构建的应用程序 其中一些具有 ADODB connection 我似乎无法在新服务器上修复它 我只得到空白屏幕 我已经对 ADODB connection 与 PHP 进行了相当广泛的研究 但找不到明确
  • Node.js Socket.IO 无法完全工作

    我运行一个侦听端口 5000 的独立服务器 当我通过 Netcat 或 Telnet 连接到它时 该应用程序不会在终端上打印任何内容 但在 Netcat Telnet 应用程序屏幕上 它显示连接已建立 var io require sock
  • Nuxt.js 使用 https 调用服务器端 API 的问题

    当我使用 HTTP 时 我遇到 nuxt 服务器端 API 调用问题S 在客户端 一切都很好 当我通过链接在客户端切换页面时 API 可以工作 但是当我按下 Ctrl f5 并且数据将在服务器端预取时 实际上没有 API 调用 也没有提供数
  • 自动备份远程托管服务器的最佳实践

    我正在尝试设置一个用于团队笔记的服务器 我想知道自动备份其数据 又称我的笔记 的最佳方法是什么 目前我计划在 docker 镜像中运行服务器 docker 镜像将由托管服务 例如 Google 托管 我找到了一个适合我的需求的免费托管服务

随机推荐

  • 如何对日志文件进行二分查找?开源文件二分查找工具『timecat』介绍

    这篇文章是我从自己的博客搬运过来的 转载请注明 xff1a 吹水小镇 reetsee com 原文链接地址 xff1a http blog reetsee com archives 502 要获得更好的阅读体验 xff0c 欢迎大家到这篇文
  • 阿里的电话面试是神马感觉

    感觉就是被问了个稀巴烂 xff0c 估计到不了下一轮 问了神马呢 xff0c 问了我的项目 xff0c 我描述了一阵子之后 xff0c 当他问到使用人数的时候 xff0c 我说是内部使用 没有发布 xff0c 只是一件比赛的作品的时候 xf
  • 找出带环单向链表的环入口(交点)

    其实这个问题已经被问烂了 xff0c 但是之前没有想透 xff0c 今天算是解决得差不多 找环的入口这个问题 xff0c 其实是建立在另外一个问题之上的 判断单向链表是否有环 土方法很多 xff0c 但是比较好的目前就那么一个 xff1a
  • 关于我最近看的一本书——大名鼎鼎的APUE

    APUE xff0c Know as Unix环境高级编程 xff0c 我每天都在用自己的绳命去看 xff0c 每天都燃烧自己去看 什么样的书 xff0c 一看就是上乘之中的珍稀之品 xff1f 这本 不同于不少机械工业出版社的大部头 xf
  • 复杂网络笔记-R语言

    最近学习了下复杂网络相关的东西 xff0c 总结了部分基础的理论 xff0c 与使用R语言igraph包 xff0c 总结如下 xff0c 还需要继续深入 xff0c 目前只学了皮毛 复杂网络的复杂性 1 结构复杂性 网络连接结构看上去错综
  • 写博客加分不

    写第一篇博客 xff0c 就看看加分不 xff0c 这个网站分很重要 xff0c 不然下不了东西
  • 谨以此文献给才毕业一两年的朋友

    谨以此文献给才毕业一两年的朋友 选自同事信件 谨以此文献给才毕业一两年的朋友我们终于进入了这个社会 从此结束了被学校老师看管的生涯 xff0c 结束了做父母乖宝贝的日子 xff0c 也结束从父母兄长那里拿钱的幸福时光 我们从家里搬了出来 x
  • layui实际项目使用过程中遇到的兼容性问题

    layui实践兼容 本文记录自己在layui的实际使用过程中遇到的一些兼容性问题 xff0c 烂笔头 gt gt gt 大脑 layui在vue项目中不能自动渲染的问题 下载layui源码到本地 xff0c 在vue的项目中引用 xff0c
  • Vue从零开始01——Vue双向绑定原理和MVVM

    Vue是一个主张较弱的渐进式框架 xff0c 什么是主张弱的渐进式框架呢 xff1f 主张弱和渐进式说的都是 xff0c 可以灵活的选取你需要用的东西和不需要用的东西 xff0c 需要用的就引进来 xff0c 不需要用的就不引 xff0c
  • layui数据表格复选框自动选中部分选项

    layui数据表格复选框自动选中部分选项 layui官方文档中给出了数据表格复选框全选字段 xff0c 如下 xff0c 但是部分选中没有配置项 部分选中的方法 xff1a 利用异步数据接口的参数 xff1a 实现代码 xff1a layu
  • Js代码收藏大全

    1 nc ntextmenu 61 34 window event returnvalue 61 false 34 将彻底屏蔽鼠标右键 lt table border nc ntextmenu 61 return false gt lt t
  • jenkins(linux)远程构建windows项目(超详细)

    1 背景 公司开发的资产管理探针需要在linux unix windows上分别部署 xff0c 使用自动化构建jenkins工具远程部署linux平台容易实现 xff0c windows比较折腾 xff0c 现将个人经验分享 2 环境 I
  • jq实现点击一个按钮,触发另一个点击事件

    jq实现点击一个按钮 xff0c 触发另一个点击事件 span class token function span span class token punctuation span span class token string 34 a
  • Promise.race()异步超时处理

    span class token keyword function span span class token function timeoutPromise span span class token punctuation span p
  • Ztree点击树节点选中前面的复选框

    span class token keyword var span setting span class token operator 61 span span class token punctuation span check span
  • 关于vue-cli项目搭建完成之后的yarn serve启动指令

    在项目的配置文件package json里面 xff0c 声明了两条指令的快捷指令 xff0c 当执行yarn serve的时候就相当于执行了vue cli service serve这条指令
  • Vue项目url中的BASE_URL解析

    在Vue中遇到很多url都用到了 lt 61 BASE URL gt 这个东西 span class token operator lt span span class token operator 61 span span class t
  • VSCode 中 js 文件类型注释报错的问题解决

    在阅读 Vue js 源码的时候遇见的一个问题 xff0c 本来可以忽略的玩意 xff0c 但是报错的波浪线 xff0c 如鲠在喉实在受不了 xff0c 解决之 报错说明 Type annotations can only be used
  • 3-3-07-nuxtjs案例realworld-nuxtjs

    realworld nuxtjs 项目地址 xff1a https gitee com dingxd9702 realworld nuxtjs 创建项目 mkdir realworld nuxtjsyarn init yyarn add n
  • 3-4-01-搭建自己的 Server Side Render

    搭建自己的 Server Side Render Vue 实例的服务端渲染 使用 vue server renderer 插件完成 vue 实例的服务端渲染使用 express 创建一个 node 服务器fs 模块读取 html 模板 sp