webpack5(Module Federation)+vue3.0實現(xiàn)微前端

項目源碼地址:

https://github.com/wuxiaohuaer/webpack5-vue-admin

一、什么是微前端

微前端是一個比較宏觀的概念,他的核心就是獨立,開發(fā)獨立、部署獨立,比較適合大的團(tuán)隊來進(jìn)行重量級項目開發(fā)。

從Micro Frontends 官網(wǎng)可以了解到,微前端概念是從微服務(wù)概念擴(kuò)展而來的,摒棄大型單體方式,將前端整體分解為小而簡單的塊,這些塊可以獨立開發(fā)、測試和部署,同時仍然聚合為一個產(chǎn)品出現(xiàn)在客戶面前。可以理解微前端是一種將多個可獨立交付的小型前端應(yīng)用聚合為一個整體的架構(gòu)風(fēng)格。

二、為什么要用微前端?

一個公司有多個類似的項目,大家可能會共用一個dialog組件,那我們可以封裝起來,以便其他的項目一起使用。

這個時候大家會有一個疑問,直接打包放在npm上不就完了,為什么要用微前端這么復(fù)雜的東西?
我們把公用的組件打包以后上傳到npm包管理器上,確實可以讓公司的其他項目一起使用,但是會有兩個弊端。

1、程序繁瑣

開發(fā)三個管理后臺應(yī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步驟

因為雖然相對是獨立的,有了npm這么一個中間商,但是要改一個組件,所有的項目都要摸一遍。

如果我們使用微服務(wù),就可以把公用的組件全部放到一個容器應(yīng)用當(dāng)中,專門用來放組件,需要更新的時候只要重新部署這個容器應(yīng)用,其他項目刷新就能得到最新的模塊。

2、構(gòu)建速度慢

如果項目當(dāng)中引用了n個組件,除了需要從npm上更新以外,構(gòu)建部署的時候,也是需要全部打包一遍的,開發(fā)體驗就會越來越差。

而微服務(wù)并不需要本地構(gòu)建這些子模塊的代碼,從而減小了構(gòu)建體積,提高了開發(fā)效率。

三、微前端實現(xiàn)方案

目前業(yè)內(nèi)最火的微前端解決方案應(yīng)該是螞蟻團(tuán)隊維護(hù)的qiankun:
https://qiankun.umijs.org/zh/guide

有興趣可以去官網(wǎng)了解

本身沒有開發(fā)過非常大量級的系統(tǒng),所以對微服務(wù)沒有太大的興趣。前段時間正好在學(xué)webpack5,發(fā)現(xiàn)webpack5有一個聯(lián)邦模塊功能(mf),對于微前端的公共依賴加載是比較好的解決方案。

鑒于mf的能力,我們可以完全實現(xiàn)一個去中心化的應(yīng)用部署群:每個應(yīng)用是單獨部署在各自的服務(wù)器,每個應(yīng)用都可以引用其他應(yīng)用,也能被其他應(yīng)用所引用,即每個應(yīng)用可以充當(dāng)host的角色,亦可以作為remote出現(xiàn),無中心應(yīng)用的概念。

目前基于mf,比較成熟的微前端架構(gòu)是YY團(tuán)隊的EMP微前端方案

https://github.com/efoxTeam/emp

作為一個vue狗,就基于最新的vue3.0+webpack5實現(xiàn)一個基礎(chǔ)的微服務(wù)。

四、搭建環(huán)境

1、創(chuàng)建文件夾

// 創(chuàng)建文件夾

mkdir hand-vue3-project && cd hand-vue3-project

// 初始化項目
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、運行環(huán)境

在main.js打印一下

console.log('hello,world!')

運行命令:

yarn dev

這個時候就可以看到dist文件夾下有個打包好的js文件

五、引入vue3.0

公司項目不敢用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

配置項目

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é)點

新建的app.vue

<template>
  <div>距離2021年歐洲杯還有?</div>
</template>

<script>
export default {
  
}
</script>

這個時候運行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"

運行yarn dev

六、使用聯(lián)邦模塊實現(xiàn)微服務(wù)

剛才那個項目我們給他稱之為項目A,現(xiàn)在需要創(chuà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項目引入

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項目上改代碼,B項目也會及時更新,不同應(yīng)用中有相同的組件,就不需要復(fù)制粘貼相同的代碼到每一個應(yīng)用的代碼中,解決了跨應(yīng)用代碼共享的問題。

七、基本概念和配置

一些基本概念

? 使用Module Federation 時, 每個應(yīng)用塊都是一個獨立的構(gòu)建,這些構(gòu)建都將被編譯為 容器。
? 被應(yīng)用的容器,被稱為 remote
? 引用者,被稱為 host
? 暴露出去被使用的模塊,稱為remote模塊

一個容器 , 使用、暴露,是雙向的。一個項目可以引用別的項目的組件,也可以將自己的組件暴露給別的項目用。

原理

感覺原理,就是利用了jsonp。根據(jù)模塊創(chuàng)建一個全局變量,根據(jù)全局變量來獲取不同組件的源代碼。

參考鏈接

https://juejin.cn/post/6921161482663100423#heading-2

https://www.yuque.com/violet-coyxa/ib3u7d/etzwyg
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
平臺聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點,簡書系信息發(fā)布平臺,僅提供信息存儲服務(wù)。

推薦閱讀更多精彩內(nèi)容