基于 Vue SSR 的微架构在 FOLLOWME5.0 实践

2020年5月22日FOLLOWME5.0的第一个版本终于上线了,这也是公司内部基于 Genesis 上线的第二个项目。首页是老项目经历了最原始的那种 Vue SSR,后来在年初的时候,迁移到了 Nuxt.js 下,再到现在迁移到了 Genesis,可谓是一波三折。

首次实践

image

在2019年的上半年,我们在和 APP 混合开发的项目中,首次实践了模块化,它拥有了独立的 API、路由、状态和页面,并且是按需进行初始化的,但是它并不是完美的,它是基于路由的微模块,并且状态也是按需注入到全局的状态中的管理

无路由微模块

2019年的8月份,我开始介入到 web 端首页的开发,首先面对的就是基于 Vuex 的状态管理,所有的状态全部注入到全局的状态,交集到一起,随着业务的迭代,已经分不清楚哪些是需要的,哪些是可以被删除的,随着业务规模的膨胀,越来越难以维护。为了保证无入侵性,基于Tms.js抽象出来了一个支持微模块的状态库,这时候我们的微模块拥有了独立的 API、状态和页面。

image

image

我们5.0左导航就被抽离成一个独立运行的微模块,一个微模块由多个组件组合而成,它有自己内部状态管理。

大跃进

在2019年年底的时候,得知我们要升级5.0的版本,网站也将会大幅度的重构,在基于已有的微模块开发理念下,我们希望能够更近一步,解决一些以往项目架构的弊端。

5.0之前,我们有一个公共的导航栏,不管是 CSR、还是 SSR 的项目都需要它,每次导航栏发生变更之后,都需要重新打包十几个项目发布,这极大的消耗了我们的身心体力,我希望能做到一个页面可以由不同的 SSR 服务聚合而成,通过 API 的形式提供给另外一个服务使用。

这个大胆的想法诞生后,我们深入的研究了一下 Nuxt.js,期望它可以做到我们的需求,经过一番调研后,确认无法满足我的需求后,最终还是选择了造轮子。

Genesis

参考了一些社区 SSR 框架的实现,基本上都是封装好的框架,灵活性较低,而我们期望它是一个简单的 SSR 库,当成一个工具函数来使用,以便于能够支撑多实例运行。

在这里的理念下, 它没有像 Nuxt.js nuxt.config.js 这样直接读取一个配置开始运行,给你集成了各种各样的功能,它仅仅一个基础到不能再基础的渲染工具函数

import { SSR } from '@fmfe/genesis-core';

const ssr = new SSR();

const renderer = ssr.createRenderer();

renderer.render({ url: '/' });

当然了,在实际的业务中,我们还需要创建一个 HTTP 服务将我们的内容返回给用户。

如果要做到微服务,并且能被不同的服务之间调用,首先就需要服务的自身具备将渲染结果输出 JSON 的能力,然后第三方服务读取渲染结果,输出到 HTML 中

renderer.render({ url: '/', mode: 'ssr-json' });

我们巧妙的使用了 Vue 的 Renderer 选项 template,传入一个函数,执行完成后返回了一个 JSON 的渲染结果

const template: any = async (
    strHtml: string,
    ctx: Genesis.RenderContext
): Promise<Genesis.RenderData> => {
    const html = strHtml.replace(
        /^(<[A-z]([A-z]|[0-9])+)/,
        `$1 ${this._createRootNodeAttr(ctx)}`
    );
    const vueCtx: any = ctx;
    const resource = vueCtx.getPreloadFiles().map(
        (item): Genesis.RenderContextResource => {
            return {
                file: `${this.ssr.publicPath}${item.file}`,
                extension: item.extension
            };
        }
    );
    const { data } = ctx;
    if (html === '<!---->') {
        data.html += `<div ${this._createRootNodeAttr(ctx)}></div>`;
    } else {
        data.html += html;
    }
    data.script += vueCtx.renderScripts();
    data.style += vueCtx.renderStyles();
    data.resource = [...data.resource, ...resource];
    (ctx as any)._subs.forEach((fn: Function) => fn(ctx));
    (ctx as any)._subs = [];
    return ctx.data;
};

远程组件

考虑到不是所有的项目,都需要远程调用,所以远程调用组件,我们是以独立的包提供的。在拿到 SSR JSON 的渲染结果后,远程组件将会帮我们负责嵌入到该服务的页面中去。

<template>
    <remote-view :fetch="fetch" />
</template>

fetch 是一个异步的回调函数,你可以通过使用 axios 库来发送请求将 SSR 渲染的结果返回给 remote-view 组件。

renderer.render({ url: '/', mode: 'ssr-json' }).then((r) => {
    // 编写一个接口, 将 r.data 提供给 remote-view 组件访问
});

5.0的服务拆分

image

根据 UI 的呈现效果,我们将左导航和内容区拆分成不同的服务,其中内容区因为开发团队的不同、业务的不同,又拆分成不同的服务。
第一个版本我们拆分了左导航的 node-ssr-general 服务,首页和通知的 node-ssr-home 服务,以及信号的 node-ssr-signal 服务,每一个服务都是独立部署、独立开发、由不同的人进行维护,只是最终由 node-ssr-general 服务进行聚合。

未来我们的产品还将会进一步迭代,越来越多的服务都将会被集成进去这个大应用中。

内网域名

最初的 SSR 服务,尝试过使用 RPC 和 HTTP 获取接口数据进行渲染,后面经过权衡后,在5.0我们统一采用了在服务器配置 HOST 的方式,配置一个内网的域名,提供给 SSR 服务通过 HTTP 请求来获取数据

服务的加载策略

为了首屏可以更快的呈现给用户,所以在服务端远程组件走的是 SSR 的渲染,在客户端路由切换的时候,走 CSR 渲染,所以在首页切换到信号栏目时,会有一些加载过程明显的白屏。

后续可以通过 Service Worker 对所有服务的静态资源和 CSR 渲染时的 HTML 进行预加载,减少加载该服务内容时,出现白屏的几率

关于微前端

从理论上,Genesis 也可以同时做到 React 的支持,输出同样标准的 JSON 渲染结果、同样标准的应用创建和销毁逻辑。只是目前我们团队都是以 Vue 为主,所以这方面还需要团队有时间才能支持。

传送们

©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 204,053评论 6 478
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 85,527评论 2 381
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 150,779评论 0 337
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 54,685评论 1 276
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 63,699评论 5 366
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 48,609评论 1 281
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 37,989评论 3 396
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 36,654评论 0 258
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 40,890评论 1 298
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 35,634评论 2 321
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 37,716评论 1 330
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,394评论 4 319
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 38,976评论 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 29,950评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,191评论 1 260
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 44,849评论 2 349
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 42,458评论 2 342