快速開(kāi)始
# 拉取代碼
git clone git@github.com:fmfe/vue-genesis-micro.git
# 進(jìn)入項(xiàng)目目錄
cd vue-genesis-micro
# 安裝依賴
npm install
# 開(kāi)發(fā)環(huán)境啟動(dòng)
npm run dev
# 打包生產(chǎn)環(huán)境代碼
npm run build
# 生產(chǎn)環(huán)境運(yùn)行
npm run start
微服務(wù)是什么逛裤?
微服務(wù)是一個(gè)新興的軟件架構(gòu)瘩绒,就是把一個(gè)大型的單個(gè)應(yīng)用程序和服務(wù)拆分為數(shù)十個(gè)的支持微服務(wù)。
為什么需要微服務(wù)带族?
隨著業(yè)務(wù)的發(fā)展锁荔,項(xiàng)目規(guī)模越來(lái)越大,給編譯打包蝙砌、合并的代碼沖突帶來(lái)了巨大的挑戰(zhàn)阳堕,而服務(wù)的拆分可以獲得更快的編譯打包跋理,獨(dú)立部署、增量更新恬总、不同的團(tuán)隊(duì)只需要負(fù)責(zé)自己的服務(wù)前普、更好的支持多端,在大型的項(xiàng)目中壹堰,使用微服務(wù)架構(gòu)可以獲得極大的收益拭卿。
微服務(wù)和微前端的區(qū)別
目前社區(qū)的微前端解決方案,基本上都是基于客戶端去進(jìn)行聚合的思路贱纠,而本項(xiàng)目卻是完全基于后端微服務(wù)的概念而誕生的峻厚,頁(yè)面的聚合既可以在服務(wù)端完成,也可以在客戶端完成谆焊,一切取決于需求惠桃。
項(xiàng)目介紹
本項(xiàng)目基于 Vue 的Genesis開(kāi)發(fā),一個(gè)編寫了三個(gè)例子:
-
ssr-common
公共的頁(yè)面導(dǎo)航 -
ssr-home
首頁(yè) -
ssr-about
關(guān)于我們
在學(xué)習(xí)完成本項(xiàng)目后懊渡,你可以搭建屬于自己的微服務(wù)架構(gòu)刽射,并且深入的了解到遠(yuǎn)程組件它是怎么工作的军拟。至此剃执,你可以做到一個(gè)大型應(yīng)用的微服務(wù)拆分下。
關(guān)于 Genesis
Genesis是在FOLLOWME5.0升級(jí)而誕生的一個(gè)項(xiàng)目懈息,它解決了以往架構(gòu)的很多弊端肾档,例如:
- 公共組件庫(kù)更新,導(dǎo)致同時(shí)十幾個(gè)項(xiàng)目要編譯發(fā)布更新
- 頁(yè)面和頁(yè)面之間的切換辫继,需要刷新整頁(yè)怒见,無(wú)法做到無(wú)刷新跳轉(zhuǎn)
- 數(shù)百個(gè)頁(yè)面,如果全部寫到一個(gè)大的項(xiàng)目中做SSR渲染姑宽,只要其中一個(gè)地方出現(xiàn)BUG遣耍,就有可能導(dǎo)致整個(gè)服務(wù)掛掉或者不穩(wěn)定,發(fā)生內(nèi)存泄漏的問(wèn)題時(shí)炮车,也更加難以排查
- 大量的項(xiàng)目是基于CSR渲染舵变,在國(guó)際化和SEO方面,導(dǎo)致 index.html 頁(yè)面的標(biāo)題瘦穆、關(guān)鍵詞和描述比較難做到國(guó)際化
- 不同的團(tuán)隊(duì)纪隙,互相交叉開(kāi)發(fā)一個(gè)項(xiàng)目,合并代碼時(shí)扛或,很容易產(chǎn)生各種沖突绵咱,服務(wù)的拆分后,大大的減少了代碼沖突
渲染接口
服務(wù)的拆分后熙兔,那么服務(wù)和服務(wù)之間的調(diào)用是必不可少的悲伶,這里提出了一個(gè)渲染接口的概念艾恼,它可能是這樣子的
// 下面的接口,你可能需要做Nginx反向代理麸锉,來(lái)做到下面的接口
// /api/ssr-服務(wù)名稱/render?url=渲染地址&mode=渲染模式&routerMode=路由模式
const renderModes = ['ssr-html', 'ssr-json', 'csr-html', 'csr-json'];
/**
* 提供一個(gè)API允許外部渲染
*/
app.use('/api/render', (req, res, next) => {
// 獲取渲染的地址
const url = decodeURIComponent(String(req.query.renderUrl));
// 獲取路由渲染的模式
const routerMode =
['abstract', 'history'].indexOf(String(req.query.routerMode)) > -1
? req.query.routerMode
: 'history';
// 渲染默認(rèn)
const mode: any =
renderModes.indexOf(String(req.query.renderMode)) > -1
? String(req.query.renderMode)
: 'ssr-json';
renderer
.render({
url,
mode,
state: {
routerMode
}
})
.then((r) => {
res.send(r.data);
})
.catch(next);
});
這樣第三方的服務(wù)蒂萎,就可以隨意的調(diào)用這個(gè)服務(wù)的渲染結(jié)果,傳遞需要渲染的地方渲染淮椰,比如Vue五慈、React、或者其它的EJS模板引擎等等主穗。本項(xiàng)目會(huì)使用渲染接口泻拦,來(lái)傳遞給遠(yuǎn)程組件進(jìn)行渲染。
遠(yuǎn)程組件
當(dāng)你需要調(diào)用其它服務(wù)的頁(yè)面渲染時(shí)忽媒,你請(qǐng)求渲染接口争拐,拿到渲染的結(jié)果,傳遞給遠(yuǎn)程組件晦雨,它就會(huì)負(fù)責(zé)幫你渲染該服務(wù)的內(nèi)容架曹。
<template>
<div>
<remote-view
v-for="name in names"
v-show="ssrname === name"
:key="name"
:clientFetch="() => clientFetch(name)"
:serverFetch="() => serverFetch(name)"
></remote-view>
</div>
</template>
<script lang="ts">
import Vue from 'vue';
import { RemoteView } from '@fmfe/genesis-remote';
import axios from 'axios';
interface Data {
names: string[];
}
interface Methods {
clientFetch: (ssrname: string) => Promise<void>;
serverFetch: (ssrname: string) => Promise<void>;
}
interface Computed {
ssrname: string;
}
export default Vue.extend<Data, Methods, Computed>({
name: 'container',
components: {
RemoteView
},
data() {
return {
names: []
};
},
computed: {
ssrname() {
return this.$route.meta.ssrname;
}
},
watch: {
ssrname() {
if (this.names.indexOf(this.ssrname) > -1) return;
this.names.push(this.ssrname);
}
},
created() {
this.names.push(this.ssrname);
},
methods: {
/**
* 客戶端遠(yuǎn)程調(diào)用時(shí),走 CSR 渲染
*/
async clientFetch(ssrname: string) {
const renderUrl = encodeURIComponent(this.$route.fullPath);
const res = await axios.get(
`http://localhost:3000/api/${ssrname}/render`,
{
params: {
routerMode: 'history',
renderMode: 'csr-json',
renderUrl
}
}
);
if (res.status === 200) {
return res.data;
}
return null;
},
/**
* 服務(wù)端遠(yuǎn)程調(diào)用時(shí)闹瞧,走 SSR渲染
*/
async serverFetch(ssrname: string) {
const renderUrl = encodeURIComponent(this.$route.fullPath);
const res = await axios.get(
`http://localhost:3000/api/${ssrname}/render`,
{
params: {
routerMode: 'history',
renderMode: 'ssr-json',
renderUrl
}
}
);
if (res.status === 200) {
return res.data;
}
return null;
}
}
});
</script>
目錄說(shuō)明
.
├── .vscode
│ ├── settings.json vscode的配置
├── examples 服務(wù)拆分的例子
│ ├── ssr-about 關(guān)于我們服務(wù)
│ | ├── src Vue源碼目錄
│ | | ├── views 頁(yè)面目錄
│ | | | ├── about-help.vue 幫助中心頁(yè)面
│ | | | └── about-us.vue 關(guān)于我們頁(yè)面
│ | | ├── app.vue 頁(yè)面入口文件绑雄,公共導(dǎo)航
│ | | ├── entry-client.ts 客戶端入口文件
│ | | ├── entry-server.ts 服務(wù)端入口文件
│ | | ├── router.ts 路由配置文件
│ | | └── shims-vue.d.ts .vue文件的TS聲明
│ | ├── genesis.build.ts 當(dāng)前服務(wù)生產(chǎn)環(huán)境構(gòu)建入口
│ | ├── genesis.dev.ts 當(dāng)前服務(wù)開(kāi)發(fā)環(huán)境入口
│ | ├── genesis.prod.ts 當(dāng)前服務(wù)生產(chǎn)環(huán)境入口
│ | └── genesis.ts 當(dāng)前服務(wù)通用的服務(wù)端邏輯
│ ├── ssr-common 基礎(chǔ)的頁(yè)面聚合服務(wù),包含公共導(dǎo)航
│ | ├── src Vue源碼目錄
│ | | ├── views 頁(yè)面目錄
│ | | | ├── about-help.vue 幫助中心頁(yè)面
│ | | | └── about-us.vue 關(guān)于我們頁(yè)面
│ | | ├── app.vue 頁(yè)面入口文件奥邮,公共導(dǎo)航
│ | | ├── container.vue 子應(yīng)用的容器
│ | | ├── entry-client.ts 客戶端入口文件
│ | | ├── entry-server.ts 服務(wù)端入口文件
│ | | ├── router.ts 路由配置文件
│ | | └── shims-vue.d.ts .vue文件的TS聲明
│ | ├── genesis.build.ts 當(dāng)前服務(wù)生產(chǎn)環(huán)境構(gòu)建入口
│ | ├── genesis.dev.ts 當(dāng)前服務(wù)開(kāi)發(fā)環(huán)境入口
│ | ├── genesis.prod.ts 當(dāng)前服務(wù)生產(chǎn)環(huán)境入口
│ | └── genesis.ts 當(dāng)前服務(wù)通用的服務(wù)端邏輯
│ ├── ssr-home 首頁(yè)的服務(wù)
│ | ├── src Vue源碼目錄
│ | | ├── views 頁(yè)面目錄
│ | | | └── home.vue 首頁(yè)頁(yè)面
│ | | ├── app.vue 頁(yè)面入口文件万牺,公共導(dǎo)航
│ | | ├── container.vue 子應(yīng)用的容器
│ | | ├── entry-client.ts 客戶端入口文件
│ | | ├── entry-server.ts 服務(wù)端入口文件
│ | | ├── router.ts 路由配置文件
│ | | └── shims-vue.d.ts .vue文件的TS聲明
│ | ├── genesis.build.ts 當(dāng)前服務(wù)生產(chǎn)環(huán)境構(gòu)建入口
│ | ├── genesis.dev.ts 當(dāng)前服務(wù)開(kāi)發(fā)環(huán)境入口
│ | ├── genesis.prod.ts 當(dāng)前服務(wù)生產(chǎn)環(huán)境入口
│ | └── genesis.ts 當(dāng)前服務(wù)通用的服務(wù)端邏輯
| ├── .editorconfig 編輯器配置
| ├── .eslintignore eslint忽略配置
| ├── .eslintrc.js eslint配置
| ├── .gitignore git忽略配置
| ├── .stylelintignore stylelint忽略配置
| ├── genesis.build.ts 所有服務(wù)生產(chǎn)構(gòu)建
| ├── genesis.dev.ts 所有服務(wù)開(kāi)發(fā)環(huán)境啟動(dòng)
| ├── genesis.prod.ts 所有服務(wù)生產(chǎn)環(huán)境入口
| ├── stylelint.config.js stylelint配置
| └── tsconfig.json TS的配置
│
└── package.json
注意:本項(xiàng)目是為了演示,才把幾個(gè)服務(wù)全部放到一個(gè)倉(cāng)庫(kù)中洽腺,在實(shí)際的應(yīng)用中脚粟,每一個(gè)服務(wù),都應(yīng)該放到獨(dú)立的git倉(cāng)庫(kù)中蘸朋。
最后
- 本項(xiàng)目源碼地址:vue-genesis-micro
- Genesis官方倉(cāng)庫(kù)
- Genesis文檔地址
- 基于 Vue SSR 的微架構(gòu)在 FOLLOWME5.0 實(shí)踐
-
基于 Vue CSR 的微前端實(shí)現(xiàn)方案
如果你對(duì)微前端核无、和微服務(wù)這一塊感興趣,歡迎 Star 藕坯,也可以在留言區(qū)我和互動(dòng)团南!