1、微前端概論
1. 微前端概念
- 型別<iframe></iframe>一樣,只不過微前端是用fetch去請求js並渲染在指定的DOM容器。
· 跟技術棧無關,任何前端技術棧都可以接入。
· 多個應用結合在一起,可以一起執行,又可以單獨執行。
· 一個複雜龐大的專案拆成多個微應用,單獨開發、單獨部署、單獨測試,互不影響。
· 原理是通過在主應用引入每個子應用的入口檔案(main.js),進行解析,並指定渲染的容器
2. 什麼時候需要用到微前端
- 龐大的系統需要拆分給不同團隊去做時。
系統裡面有很多個模組,模組裡面又很多個子模組時。
2、微前端使用說明
qiankun(乾坤) 就是一款由螞蟻金服推出的比較成熟的微前端框架,基於 single-spa 進行二次開發,用於將 Web 應用由單一的單體應用轉變為多個小型前端應用聚合為一的應用。
基於 qiankun+vue2.0 技術棧實現的前端微應用架構,實現了動態路由主子應用以及子子應用之間的通訊,並做了簡單的自動化指令碼命令
1、微前端的相關文件
Qiankun: https://qiankun.umijs.org/zh/guide
2、關於專案依賴包 common
包內容簡介:
所有子應用都需要對主應用下發的資料進行接收及處理、如果資料修改則通知到其他應用以及對主應用下發的路由資料進行處理,因為這些邏輯完全一樣,因此將這些實現邏輯提取為一個 npm 包統一管理。
實現的功能:
在 vuex 中動態新增了 global 模組及 routes 模組;
- global 模組:封裝了全域性下發的資料,以及資料修改通知到其他應用;
- routes 模組:路由資料的封裝以及元件的匯入。
3、主專案中微前端的相關檔案說明
3、微前端子應用程式碼改造
a.修改package.json:
- name屬性為應用名。
- 設定header允許跨域請求。
b.修改vue.config.js的publicPath屬性應用名。
const packageName = require("./package.json").name; const Timestamp = new Date().getTime(); module.exports = { assetsDir: "./", publicPath: process.env.NODE_ENV === "production" ? "/sub/" : "/", outputDir: "sub", configureWebpack: { output: { library: `${packageName}-[name]`, libraryTarget: "umd", jsonpFunction: `webpackJsonp_${packageName}`, filename: "js/[name]." + Timestamp + ".js", chunkFilename: "js/[name]." + Timestamp + ".js", }, }, devServer: { port: 8012, // 在.env中VUE_APP_PORT=7788,與父應用的配置一致 headers: { "Access-Control-Allow-Origin": "*", // 主應用獲取子應用時跨域響應頭 }, }, };
設定唯一埠,在.env裡面設定埠號,這裡埠號沒有說必須要這裡設定,你也在其他地方設定,看你專案設計而定,但是埠號必須唯一,不跟已有應用發生衝突
c.在src下新建一個public-path.js檔案
(function () { if (window.__POWERED_BY_QIANKUN__) { if (process.env.NODE_ENV === "development") { // eslint-disable-next-line no-undef __webpack_public_path__ = `//localhost:${process.env.VUE_APP_PORT}/`; return; } // eslint-disable-next-line no-undef __webpack_public_path__ = window.__INJECTED_PUBLIC_PATH_BY_QIANKUN__; } })();
d.Src下新建common資料夾,新建index.js檔案(封裝用於接收主應用引數)
import Vue from 'vue' import validator from 'validator' Vue.prototype.$validator = validator function initGlobalState(store, props = {}) { registerGlobalModule(store, props) } function registerGlobalModule(store, props = {}) { // 是否傳入store及所傳入的store是否為一個vuex的例項 if (!store || !store.hasModule) { return } // 獲取初始化的state const initState = (props.getGlobalState && props.getGlobalState()) || { userInfo: {}, globalConfig: {} } // 將父應用的資料儲存到子應用中,名稱空間固定為global if (!store.hasModule('global')) { const globalModule = { namespaced: true, state: initState, actions: { // 子應用改變state並通知父應用 setGlobalState({ commit }, payload) { commit('setGlobalState', payload) commit('emitGlobalState', payload) }, // 初始化,只用於mount時同步父應用的資料 initGlobalState({ commit }, payload) { commit('setGlobalState', payload) } }, mutations: { setGlobalState(state, payload) { // eslint-disable-next-line state = Object.assign(state, payload); }, // 通知父應用 emitGlobalState(state) { if (props.setGlobalState) { props.setGlobalState(state) } } } } store.registerModule('global', globalModule) } else { // 每次mount時,都同步一次父應用資料 store.dispatch('global/initGlobalState', initState) } } export default { initGlobalState }
e.main.js改造
/* * @Author: your name * @Date: 2021-09-05 15:36:58 * @LastEditTime: 2021-10-28 10:16:46 * @LastEditors: Please set LastEditors * @Description: In User Settings Edit * @FilePath: \sub_notice\src\main.js */ import './public-path' import Vue from 'vue' import App from './App.vue' import router from './router' import store from './store' import common from './common/index' import ElementUI from 'element-ui' import iView from 'iview' import 'iview/dist/styles/iview.css'; import Utils from '@/utils/util' // 新字型圖示 有變動替換下方 import '@/assets/fontsNew/iconfont.css' import 'element-ui/lib/theme-chalk/index.css' import '@/utils/rem' Vue.config.productionTip = false let instance = null Vue.use(ElementUI) Vue.use(iView) Vue.prototype.$utils = Utils function render(props = {}) { const { container } = props instance = new Vue({ router, store, render: (h) => h(App) }).$mount(container ? container.querySelector('#app') : '#app') } console.log(router) if (!window.__POWERED_BY_QIANKUN__) { render(); } export async function bootstrap(props) { console.log('[vue] vue app bootstraped', props) } export async function mount(props) { console.log('[vue] props from main framework', props) common.initGlobalState(store, props) render(props) } export async function unmount() { instance.$destroy() instance.$el.innerHTML = '' instance = null }
f.路由攔截
路由攔截設計,當一起執行時,則交給主應用處理;當獨立執行時,則由執行的子應用處理,判斷是一起執行還是獨立執行可以通過window.POWERED_BY_QIANKUN__的值判斷。
路由模式採用hash,路由跳轉採用this.$router.push
g.獲取主應用引數
$store.state
h.修改公共引數
this.setGlobalState({ userInfo: { name: "xxxxxx" } });
5、微前端子應用接入示例
1、子應用的準備
先確保子應用能正常執行並且能訪問對應的選單路由。
2、配置選單
此係統中因無選單管理,相關選單需要在資料庫中配置。
a、新增選單
b、配置角色與選單對應關係
c、主應用中新增子應用選單路由
d、主應用訪問子應該選單(成功顯示)
3、主應用概述
技術棧:vue+vue-router+vuex+ivew/vant+axios 安裝 npm install 本地啟動 1、npm run serve 2、開啟http://localhost:8081/travel_guide/travel/(預設首頁) 環境變數與構建 環境變數:dev、test、prod 測試環境構建: run buildtest 生產環境構建: run buildprod PC元件庫iview 文件:https://www.iviewui.com 引入方式:src/plugins, 註冊引用,按需引入 路由配置 配置: src/router/routes 文件:https://router.vuejs.org/zh/ 資料儲存於請求 vuex+axios 文件 vuex https://vuex.vuejs.org/zh/guide/ axios https://www.kancloud.cn/yunye/axios/234845 配置 關於路由守衛,仿vue-admin
4、子應用概述
技術棧:vue+vue-router+vuex+ivew/vant+axios 安裝 npm install 本地啟動 1、npm run serve 2、開啟 http://localhost:8012/(預設首頁) 環境變數與構建 環境變數:dev、test、prod 測試環境構建: run buildtest 生產環境構建: run buildprod PC 元件庫 iview 文件:https://www.iviewui.com 引入方式:src/plugins, 註冊引用,按需引入 移動端元件庫 vant 文件:https://www.iviewui.com 路由配置 配置: src/router/routes 文件:https://router.vuejs.org/zh/ 首頁:http://localhost:8012/ 資料儲存於請求 vuex+axios 文件 vuex https://vuex.vuejs.org/zh/guide/ axios https://www.kancloud.cn/yunye/axios/234845