項(xiàng)目源碼地址:
https://github.com/wuxiaohuaer/webpack5-vue-admin
一泉唁、什么是微前端
微前端是一個比較宏觀的概念鹅龄,他的核心就是獨(dú)立,開發(fā)獨(dú)立游两、部署獨(dú)立砾层,比較適合大的團(tuán)隊(duì)來進(jìn)行重量級項(xiàng)目開發(fā)。
從Micro Frontends 官網(wǎng)可以了解到贱案,微前端概念是從微服務(wù)概念擴(kuò)展而來的肛炮,摒棄大型單體方式,將前端整體分解為小而簡單的塊宝踪,這些塊可以獨(dú)立開發(fā)侨糟、測試和部署,同時仍然聚合為一個產(chǎn)品出現(xiàn)在客戶面前瘩燥★踔兀可以理解微前端是一種將多個可獨(dú)立交付的小型前端應(yīng)用聚合為一個整體的架構(gòu)風(fēng)格。
二厉膀、為什么要用微前端溶耘?
一個公司有多個類似的項(xiàng)目,大家可能會共用一個dialog組件服鹅,那我們可以封裝起來凳兵,以便其他的項(xiàng)目一起使用。
這個時候大家會有一個疑問企软,直接打包放在npm上不就完了庐扫,為什么要用微前端這么復(fù)雜的東西?
我們把公用的組件打包以后上傳到npm包管理器上仗哨,確實(shí)可以讓公司的其他項(xiàng)目一起使用形庭,但是會有兩個弊端。
1厌漂、程序繁瑣
開發(fā)三個管理后臺應(yīng)用項(xiàng)目萨醒,將相同的業(yè)務(wù)子模塊抽離成npm包方式,這時候桩卵,如果要更新該業(yè)務(wù)子模塊的邏輯時验靡,那么需要做以下的流程操作:
更新npm包版本
更新A管理系統(tǒng)應(yīng)用的npm包版本
發(fā)布部署A管理系統(tǒng)應(yīng)用
對B和C管理系統(tǒng)應(yīng)用循環(huán)2和3步驟
因?yàn)殡m然相對是獨(dú)立的倍宾,有了npm這么一個中間商,但是要改一個組件胜嗓,所有的項(xiàng)目都要摸一遍高职。
如果我們使用微服務(wù),就可以把公用的組件全部放到一個容器應(yīng)用當(dāng)中辞州,專門用來放組件怔锌,需要更新的時候只要重新部署這個容器應(yīng)用,其他項(xiàng)目刷新就能得到最新的模塊变过。
2埃元、構(gòu)建速度慢
如果項(xiàng)目當(dāng)中引用了n個組件,除了需要從npm上更新以外媚狰,構(gòu)建部署的時候岛杀,也是需要全部打包一遍的,開發(fā)體驗(yàn)就會越來越差崭孤。
而微服務(wù)并不需要本地構(gòu)建這些子模塊的代碼类嗤,從而減小了構(gòu)建體積,提高了開發(fā)效率辨宠。
三遗锣、微前端實(shí)現(xiàn)方案
目前業(yè)內(nèi)最火的微前端解決方案應(yīng)該是螞蟻團(tuán)隊(duì)維護(hù)的qiankun:
https://qiankun.umijs.org/zh/guide
有興趣可以去官網(wǎng)了解
本身沒有開發(fā)過非常大量級的系統(tǒng),所以對微服務(wù)沒有太大的興趣嗤形。前段時間正好在學(xué)webpack5精偿,發(fā)現(xiàn)webpack5有一個聯(lián)邦模塊功能(mf),對于微前端的公共依賴加載是比較好的解決方案赋兵。
鑒于mf的能力笔咽,我們可以完全實(shí)現(xiàn)一個去中心化的應(yīng)用部署群:每個應(yīng)用是單獨(dú)部署在各自的服務(wù)器,每個應(yīng)用都可以引用其他應(yīng)用霹期,也能被其他應(yīng)用所引用拓轻,即每個應(yīng)用可以充當(dāng)host的角色,亦可以作為remote出現(xiàn)经伙,無中心應(yīng)用的概念。
目前基于mf勿锅,比較成熟的微前端架構(gòu)是YY團(tuán)隊(duì)的EMP微前端方案
https://github.com/efoxTeam/emp
作為一個vue狗帕膜,就基于最新的vue3.0+webpack5實(shí)現(xiàn)一個基礎(chǔ)的微服務(wù)。
四溢十、搭建環(huán)境
1垮刹、創(chuàng)建文件夾
// 創(chuàng)建文件夾
mkdir hand-vue3-project && cd hand-vue3-project
// 初始化項(xiàng)目
npm init -y
2、安裝依賴
yarn add webpack webpack-cli -D
3张弛、創(chuàng)建文件
在根目錄下創(chuàng)建src文件夾荒典、index.html和webpack.config.js酪劫,src文件夾里面創(chuàng)建main.js
4、配置webpack.config.js
// webpack.config.js
const path = require('path')
module.exports = {
mode: 'development', // 環(huán)境模式
entry: path.resolve(__dirname, './src/main.js'), // 打包入口
output: {
path: path.resolve(__dirname, 'dist'), // 打包出口
filename: 'js/[name].js' // 打包完的靜態(tài)資源文件名
}
}
在package.json 的 scripts 屬性加上:
"dev": "webpack --config ./webpack.config.js"
先做一些基礎(chǔ)的打包配置寺董,后面引入vue再加
5覆糟、運(yùn)行環(huán)境
在main.js打印一下
console.log('hello,world!')
運(yùn)行命令:
yarn dev
這個時候就可以看到dist文件夾下有個打包好的js文件
五、引入vue3.0
公司項(xiàng)目不敢用vue3.x遮咖,也只能在demo上騷一騷了
安裝依賴
yarn add vue@next -S
@next -S才能下載到最新的vue版本
yarn add html-webpack-plugin -D
將 index.html 作為模板滩字,打出到 dist 文件夾
yarn add vue-loader@next
解析和轉(zhuǎn)換 .vue 文件,提取出其中的邏輯代碼 script御吞、樣式代碼 style麦箍、以及 HTML 模版 template
yarn add @vue/compiler-sfc
Vue 2.x 時代,需要 vue-template-compiler 插件處理 .vue 內(nèi)容為 ast 陶珠, Vue 3.x 則變成 @vue/compiler-sfc 挟裂。
yarn add vue-style-loader css-loader
配置項(xiàng)目
1、webpack.config.js
const path = require('path')
const HtmlWebpackPlugin = require('html-webpack-plugin')
// 最新的 vue-loader 中揍诽,VueLoaderPlugin 插件的位置有所改變
const { VueLoaderPlugin } = require('vue-loader/dist/index')
module.exports = {
mode: 'development',
entry: path.resolve(__dirname, './src/main.js'),
output: {
path: path.resolve(__dirname, 'dist'),
filename: 'js/[name].js'
},
module: {
rules: [
{
test: /\.vue$/,
use: [
'vue-loader'
]
},
{
test: /\.css$/,
use: [
'style-loader',
'css-loader'
]
}
]
},
plugins: [
new HtmlWebpackPlugin({
template: path.resolve(__dirname, './index.html'),
filename: 'index.html',
title: '微前端框架1'
}),
// 添加 VueLoaderPlugin 插件
new VueLoaderPlugin()
]
}
2诀蓉、src下的文件
main.js
import { createApp } from 'vue' // Vue 3.x 引入 vue 的形式
import App from './app.vue' // 引入 APP 頁面組建
const app = createApp(App) // 通過 createApp 初始化 app
app.mount('#root') // 將頁面掛載到 root 節(jié)點(diǎn)
新建的app.vue
<template>
<div>距離2021年歐洲杯還有?</div>
</template>
<script>
export default {
}
</script>
這個時候運(yùn)行yarn dev寝姿,dist文件夾下就會包出來一個index.html交排,打開就能看到效果,但是每次都要打包打開太麻煩
引入WDS
yarn add webpack-dev-server -D
webpack.config.js
devServer: {
contentBase: path.resolve(__dirname, './dist'),
port: 8080,
publicPath: '/'
}
package.json
"dev": "webpack serve --progress --config ./webpack.config.js"
運(yùn)行yarn dev
六饵筑、使用聯(lián)邦模塊實(shí)現(xiàn)微服務(wù)
剛才那個項(xiàng)目我們給他稱之為項(xiàng)目A埃篓,現(xiàn)在需要創(chuàng)建項(xiàng)目A的某個公用組件,并導(dǎo)出
1根资、新建一個組件
mountDown.vue
<template>
<div>{{ sum }}天</div>
</template>
<script>
import { defineComponent, onMounted, ref, computed } from 'vue'
export default {
setup() {
let sum = computed(() => parseInt((new Date('2021-06-13'.replace(/-/g, '/')).getTime() - new Date()) / (1000*3600*24)));
return {
sum
}
}
}
</script>
2架专、導(dǎo)出組件
webpack.config.js
const ModuleFederationPlugin = require('webpack/lib/container/ModuleFederationPlugin');
new ModuleFederationPlugin({
name: "A", // 暴露出去的模塊名
filename: "remoteEntry.js", // 構(gòu)建出來的文件名
exposes: {
'./countDown': './src/components/countDown.vue' // 暴露出去。key玄帕,要寫相對路徑
}
})
3部脚、B項(xiàng)目引入
webpack.config.js
const ModuleFederationPlugin = require('webpack/lib/container/ModuleFederationPlugin');
new ModuleFederationPlugin({
name: "B", // 暴露出去的模塊名
filename: "remoteEntry.js", // 構(gòu)建出來的文件名
remotes: {
A: 'A@http://localhost:8080/remoteEntry.js' // 引用
}
})
app.vue
<template>
<div>距離2021年歐洲杯還有?</div>
<countDown/>
</template>
import { defineAsyncComponent } from 'vue'
const countDown = defineAsyncComponent(() =>
import('A/countDown')
)
export default {
components: { countDown },
setup() {
return {
}
}
}
在A項(xiàng)目上改代碼裤纹,B項(xiàng)目也會及時更新委刘,不同應(yīng)用中有相同的組件,就不需要復(fù)制粘貼相同的代碼到每一個應(yīng)用的代碼中鹰椒,解決了跨應(yīng)用代碼共享的問題锡移。
七、基本概念和配置
一些基本概念
? 使用Module Federation 時漆际, 每個應(yīng)用塊都是一個獨(dú)立的構(gòu)建淆珊,這些構(gòu)建都將被編譯為 容器。
? 被應(yīng)用的容器奸汇,被稱為 remote
? 引用者施符,被稱為 host
? 暴露出去被使用的模塊往声,稱為remote模塊
一個容器 , 使用戳吝、暴露浩销,是雙向的。一個項(xiàng)目可以引用別的項(xiàng)目的組件骨坑,也可以將自己的組件暴露給別的項(xiàng)目用撼嗓。
原理
感覺原理,就是利用了jsonp欢唾。根據(jù)模塊創(chuàng)建一個全局變量且警,根據(jù)全局變量來獲取不同組件的源代碼。
參考鏈接
https://juejin.cn/post/6921161482663100423#heading-2
https://www.yuque.com/violet-coyxa/ib3u7d/etzwyg