使用VueGenesis从零开始前端微服务开发(一)开发

1 引言

提到微服务架构,我们会想到Java或者C#,但是真正的微服务应该如本文所述一样,每个服务拥有独立的展示层,独立的数据存储。相互之间绝对的无干扰。无论是前端还是后端。都是独立开发,独立部署,独立运行。

本文源代码:https://gitee.com/bitem/vue-genesis-micro-front-demo.git

微服务大致结构图可能是这样的:

或者这样的:

本文需要讲的是如何做一个如下图所示的微服务架构(这个图片是网上下载的,来源忘记了):

这个图可以看到每个API都有一个展示服务,即将前端拆分为一个个前端服务。每个前端服务拥有独立开发,独立部署,独立运行的能力,相互之间互不干扰,一个服务挂掉不会影响其他服务的正常运行。加上负载均衡配合,同一个服务还可以部署多个实例,形成分布式集群的解决方案。这里就不说怎么去负载均衡部署集群解决方案,只说明如何开始一个前端微服务的开发和部署。

2 工具说明

开发工具:VS Code(其他工具肯定也可以,只是我最近发现VS Code好牛逼的样子,才用VS Code)
开发语言:Vue(版本:2.7.13,为啥用特定版本后面会介绍,另外Vue Genesis只支持Vue2)
依赖框架:Vue Genesis(版本:1.0.13,为啥不用最新版2.x,因为新版本2.x官方都还没完成,暂时懒得去看)。Vue Genesis因为Followme 5.0 而诞生,官方文档和资料少的可怜。甚至连如何部署都没有,只能自己研究怎么部署,作者觉得部署太简单了不屑于写说明,但是对于小白来说,还是需要的。(使用Docker确实省了很多事情)。

3 准备开始

3.1 环境准备

确保你的VS Code安装好,并且安装了以下插件:

3.2 新建文件夹

新建项目根目录e-mes-ui
进入e-mes-ui,新建四个文件夹:e-mes-main、e-mes-home、e-mes-share、e-mes-about,分别为主服务(即入口服务或者叫聚合服务,主页服务、公共组件服务、关于我们服务)。

3.3 工作原理

章节4中主要说明部分重要的代码和配置,详细说明请直接查看源码。本节主要说明Vue Genesis的工作原理。摘抄自Vue Genesis官方文档。

在 Vue Genesis 中,核心的就是渲染器,它提供了最基础渲染能力,有了它,你可以实现微前端、微服务、远程组件、首屏渲染,甚至可以和 React、EJS 等配合使用。

它可以和怎样的你协作?
● 如果你是传统的后端渲染的,需要做SEO,但是你希望在部分布局,部分页面引入 Vue,那么 renderer.renderJson() 足以,将渲染结果传递给后端渲染的模板引擎中即可。
● 如果你是中后台系统,业务系统全部集中在一个项目,你希望可以按照业务进行服务的拆分,那么 <remote-view :fetch="fetch" /> 足以
● 如果你是 CSR 渲染的项目,那么 renderer.renderHtml({ mode: ‘csr-html’ }) 足以
● 如果你是 SSR 渲染的项目,那么 renderer.renderHtml({ mode: ‘ssr-html’ }) 足以

如果你想做微前端、微服务、那么渲染器 天生就具备了这样的能力,你可以把它当成一个工具函数使用,你可以通过 HTTP 、 RPC 等等各种协议访问到你的服务,然后使用它进行渲染。
官方GitHub地址:https://github.com/fmfe/genesis

4 开发

4.1 开发配置文件

文件目录如下,具体配置见源码(四个微服务项目的文件结构都是这样,少量区别下面已经做了说明)。

├── .vscode                               vscode配置文件
├── src                                   源代码目录
│   ├── component                           组件目录
│   |   └── ***.vue                           组件
│   ├── view                                页面目录
│   |   └── ***.vue                           页面
│   ├── app.vue                             页面入口文件
│   ├── container.vue                       子应用容器(子应用无该文件)
│   ├── entry-client.ts                     客户端入口文件
│   ├── entry-server.ts                     服务端入口文件
│   ├── router.ts                           路由配置文件
│   └── shims-vue.d.ts                      .vue文件的TS声明
├── startup                               编译执行脚本目录
│   ├── genesis.ts                            启动执行脚本
│   ├── genesis.build.ts                      生产构建脚本
│   ├── genesis.dev.ts                        开发环境启动脚本
│   └── genesis.prod.ts                       生产环境启动脚本
├── .editorconfig                         编辑器配置
├── .eslintignore                         eslint忽略配置
├── .eslintrc.js                          eslint配置
├── .gitignore                            git忽略配置
├── .stylelintignore                      stylelint忽略配置
├── package.json                          包配置文件
├── README.md                             说明文档
├── stylelint.config.js                   stylelint配置
└── tsconfig.json                         TS的配置
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26

4.2 依赖特殊说明

以下三个包需要指定相同的版本号:
yarn add vue@2.7.13 vue-template-compiler@2.7.13 vue-server-renderer@2.7.13

4.3 主应用源码

入口 app.vue
应用入口,只需要一个router-view即可。
登录页 login.vue
登录页面,按照vue和typescript语法开发即可,不做说明。
框架页 default.vue
该文件中需要有个“router-view”标签接收二级路由的组件容器。如下图所示。

远程组件容器 container.vue
该文件中的remote-view为genesis框架中的组件,用来进行远程组件调用。具体参见源码。
客户端入口脚本 entry-client.ts

import { ClientOptions } from '@fmfe/genesis-core';
import { createClientApp } from '@fmfe/genesis-app';
import { createRouter } from './router';
import Vue from 'vue';
import App from './app.vue';

import Antd from 'ant-design-vue'
import 'ant-design-vue/dist/antd.css'
Vue.use(Antd)

export default async (clientOptions: ClientOptions): Promise<Vue> => {
    return createClientApp({
        App,
        clientOptions,
        vueOptions: {
            router: createRouter()
        }
    });
};
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19

服务的入口脚本 entry-server.ts

import { RenderContext } from '@fmfe/genesis-core';
import { createServerApp } from '@fmfe/genesis-app';
import { createRouter } from './router';
import Vue from 'vue';
import App from './app.vue';

export default async (renderContext: RenderContext): Promise<Vue> => {
    return createServerApp({
        App,
        renderContext,
        vueOptions: {
            router: createRouter()
        }
    });
};
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15

路由脚本 router.ts
其中每个路由中的ssrname表示该路由对应哪个服务。

import { Router } from '@fmfe/genesis-app';
import Container from './container.vue';
import Default from './view/default.vue';
import Login from './view/login.vue'

export const createRouter = () => {
    return new Router({
        mode: 'history',
        routes: [{
            path: '/',
            meta: {
                title:'E-MES',
                ssrname: 'e-mes-main'
            },
            redirect: '/login'
        }, {
            path: '/login',
            meta: {
                title:'E-MES',
                ssrname: 'e-mes-main'
            },
            component:Login
        }, {
            path: '/default',
            meta: {
                title:'E-MES',
                ssrname: 'e-mes-main'
            },
            component: Default,
            redirect: '/home',
            children: [{
                path: '/home',
                meta: {
                    title:'系统',
                    ssrname: 'e-mes-home'
                },
                component: Container
            }, {
                path: '/about/*',
                meta: {
                    ssrname: 'e-mes-about'
                },
                component: Container
            }, {
                path: '/share/*',
                meta: {
                    ssrname: 'e-mes-share'
                },
                component: Container
            }]
            // Default页的二级路由 END
        }]
        // 一级路由 END
    });
};

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56

4.3 子应用源码

子应用没有特殊要求,按照正常路由开发即可