Egg + Vue 服务端渲染工程化建设

在实现 egg + vue 服务端渲染工程化实现之前,我们先来看看前面两篇关于Webpack构建和Egg的文章:

  • Webpack工程化解决方案easywebpack 文章中我们提到了基于 Vue 构建的解决方案 easywebpack-vue. easywebpack-vue 支持纯前端模式和Node层构建,这为 Vue 服务端渲染提供了支持,我们只需要简单的配置关键的 entry 和 alias 就可以完成 Vue 前端渲染构建和 Node 层构建, 极大的简化了 Vue 服务端渲染构建的工作,可以让我们把中心放到 Vue 服务端渲染的实现上面。

  • Egg + Webpack 热更新实现 文章中我们通过 Egg 框架的 Message 通信机制实现了 Webpack 内存编译热更新实现插件 egg-webpack,保证 Node 层代码修改重启时,Webpack 编译实例依然存在, 为本地开发Node层代码修改和热更新提供了支持。

Vue 服务端(Node)渲染机制

从 Vue 的官方支持我们知道,Vue 是支持服务端渲染的,而且还提供了官方渲染插件 vue-server-renderer 提供了基于 JSBundle 或 JSON 文件渲染模式和流渲染模式。这里我们主要讲基于 JSBundle 的服务端渲染实现,流渲染模式目前在 Egg 框架里面与 Egg 部分插件有冲突(Header写入时机问题), 后续作为单独的研究课题。另外基于 Vue JSON 文件字符串构建渲染请移步 VueSSRPlugin 这种方案目前基于 Vue 官方的Plugin在构建上面只能构建单页面(生成一个json manfiest,多个会有冲突),完善的解决方案需要继续研究。

首先,我们来看看 vue-server-renderer 提供的 createBundleRenderer 和 renderToString 怎么把 JSBundle 编译成 HTML。
基于 vue-server-renderer 实现 JSBundle 主要代码如下:

const renderer = require('vue-server-renderer');
// filepath 为 Webpack 构建的服务端代码
const bundleRenderer = renderer.createBundleRenderer(filepath, renderOptions);
// data 为 Node端获取到的数据
const context = { state: data };
return new Promise((resolve, reject) => {
  bundleRenderer.renderToString(context, (err, html) => {
  if (err) {
    reject(err);
  } else {
    resolve(html);
  }
});

这里面仅仅简单考虑了编译,对于缓存,资源依赖都没有考虑。其实在做 Vue 服务端渲染时,关键的地方就在于这里,如何保证 Vue 渲染的速度,同时也要满足实际的项目需要。

缓存

  • 目前 createBundleRenderer 方法提供了 options 扩展参数,提供了 cache 的接口,支持组件级别缓存,我们这里再近一步支持页面缓存,也就是根据文件把 createBundleRenderer 缓存起来。

  • runInNewContext:默认情况下,对于每次渲染,bundle renderer 将创建一个新的 V8 上下文并重新执行整个 bundle。这具有一些好处 - 例如,应用程序代码与服务器进程隔离,我们无需担心文档中提到的状态单例问题。然而,这种模式有一些相当大的性能开销,因为重新创建上下文并执行整个 bundle 还是相当昂贵的,特别是当应用很大的时候。出于向后兼容的考虑,此选项默认为 true,但建议你尽可能使用 runInNewContext: false 或 runInNewContext: ‘once’(这段信息来自 Vue 官网:https://ssr.vuejs.org/zh/api.html#runinnewcontext)。从实际项目统计分析也印证了这里所说的性能开销问题:runInNewContext=false 能显著提高 render 速度,从线上实际统计来看,runInNewContext=false 能显著提高 render速度 3 倍以上(一个多模块的5屏的列表页面,runInNewContext = true 时的render时间平均在60-80ms,runInNewContext = false 时的render时间平均在20-30ms)。

基于以上两点, 我们实现了 egg-view-vue 插件, 提供了 Vue 渲染引擎。在 Egg 项目里面,我们可以通过 this.app.vue 拿到 Vue 渲染引擎的实例,然后就可以根据提供的方法进行 Vue 编译成 HTML。

  • egg-view-vue 暴露的 vue 实例
const Engine = require('../../lib/engine');
const VUE_ENGINE = Symbol('Application#vue');

module.exports = {

  get vue() {
    if (!this[VUE_ENGINE]) {
      this[VUE_ENGINE] = new Engine(this);
    }
    return this[VUE_ENGINE];
  },
};
  • Vue View Engine 设计实现
'use strict';
const Vue = require('vue');
const LRU = require('lru-cache');
const vueServerRenderer = require('vue-server-renderer');

class Engine {
  constructor(app) {
    this.app = app;
    this.config = app.config.vue;
    this.vueServerRenderer = vueServerRenderer;
    this.renderer = this.vueServerRenderer.createRenderer();
    this.renderOptions = this.config.renderOptions;

    if (this.config.cache === true) {
      this.bundleCache = LRU({
        max: 1000,
        maxAge: 1000 * 3600 * 24 * 7,
      });
    } else if (typeof this.config.cache === 'object') {
      
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值