不同網站對商家做o2o的政策阿里seo排名優(yōu)化軟件
目錄
什么是微前端
目前現(xiàn)有的微前端
好處
使用
?子應用的頁面在主應用里顯示?
什么是微前端
微前端是一種多個團隊通過獨立發(fā)布功能的方式來共同構建現(xiàn)代化 web 應用的技術手段及方法策略。
?
我的理解就是將一個大型的前端應用拆分成多個模塊,每個微前端模塊可以由不同的團隊進行管理,并可以自主選擇框架,并且有自己的倉庫,可以獨立部署上線。
目前現(xiàn)有的微前端方案
iframe
通過iframe實現(xiàn)的話就是每個子應用通過iframe標簽來嵌入到父應用中,iframe具有天然的隔離屬性,各個子應用之間以及子應用和父應用之間都可以做到互不影響。
但是iframe也有很多缺點:
- url不同步,如果刷新頁面,iframe中的頁面的路由會丟失。
- 全局上下文完全隔離,內存變量不共享。
- UI不同步,比如iframe中的頁面如果有帶遮罩層的彈窗組件,則遮罩就不能覆蓋整個瀏覽器,只能在iframe中生效。
- 慢。每次子應用進入都是一次瀏覽器上下文重建、資源重新加載的過程
single-spa
single-spa是最早的微前端框架,可以兼容很多技術棧。
single-spa首先在基座中注冊所有子應用的路由,當URL改變時就會去進行匹配,匹配到哪個子應用就會去加載對應的那個子應用。
相對于iframe的實現(xiàn)方案,single-spa中基座和各個子應用之間共享著一個全局上下文,并且不存在URL不同步和UI不同步的情況,但是single-spa也有以下的缺點:
- 沒有實現(xiàn)js隔離和css隔離
- 需要修改大量的配置,包括基座和子應用的,不能開箱即用
qiankun
基于single-spa二次開發(fā),封裝了開箱即用的api
資源預加載,在瀏覽器空閑時間預加載未打開的微應用資源,加速微應用打開速度。
實現(xiàn)了樣式隔離
基于qiankun的微前端實戰(zhàn)
準備兩個空項目
- qiankun-base 主應用
- qiankun-child vue 子應用
創(chuàng)建基座項目qiankun-base和qiankun-child-vue
創(chuàng)建一個vue3+vite+tsx項目詳情見?創(chuàng)建一個vue3+vite+ts項目
vue子應用 qiankun-child-vue
修改.env
VITE_APP_NAME = qiankun-child-vue
修改根節(jié)點掛載id
index.html
<!DOCTYPE html>
<html lang="en"><head><meta charset="UTF-8" /><link rel="icon" type="image/svg+xml" href="/vite.svg" /><meta name="viewport" content="width=device-width, initial-scale=1.0" /><title>qiankun-child-vue</title></head><body><div id="qiankun-child-vue"></div><script type="module" src="/src/main.ts"></script></body>
</html>main.tsimport { createApp } from "vue";
import "./style.css";
import App from "./App.vue";
import router from "./router";
import antv from "ant-design-vue";
const app = createApp(App);
app.use(router).use(antv).mount("#qiankun-child-vue");
配置子應用菜單?
/views/index.tsx
import { defineComponent, h, reactive, ref } from "vue";
import { Menu, SubMenu, MenuItem, ItemType } from "ant-design-vue";
import "./index.css";
import { RouterView, useRouter } from "vue-router";
// 展平數組
const flattenMenu = (list) => {const res: any = [];if (!list) return;list.forEach((item) => {res.push(item);if (item.children) res.push(...flattenMenu(item.children));});return res;
};
const getMenuKey = (menuList, key) => {const allList = flattenMenu(JSON.parse(JSON.stringify(menuList)));const cur = allList.find((item) => item.key == key);return cur ? cur : {};
};export default defineComponent({setup() {const router = useRouter();const menuList = ref([{key: "1",label: "子應用菜單",url: "/qiankun-child-vue",children: [{ label: "設置", key: "2", url: "/qiankun-child-vue/setting" },],},]);// 找到點擊的菜單對象const handleMenuSelect = (params) => {const menu = getMenuKey(menuList.value, params.key);router.push(menu.url);};return () => (<a-layout class="layout"><a-layout-sider><MenuonSelect={handleMenuSelect}style="height:100%"mode="inline"items={menuList.value}></Menu></a-layout-sider><a-layout-content><RouterView></RouterView></a-layout-content></a-layout>);},
});
新建/views/setting.tsx
import { defineComponent, ref } from "vue";export default defineComponent({setup() {return () => <div>設置</div>;},
});
配置路由?/router/index.ts
import { createRouter, createWebHashHistory } from "vue-router";
import Index from "@/views/index";
const router = createRouter({history: createWebHashHistory(),routes: [{path: "/",component: Index,children: [{path: "/qiankun-child-vue/setting",name: "setting",component: () => import("@/views/setting"),},],},],
});export default router;
基本的頁面就搭建好了?
基座 qiankun-base
同樣在/views/index.tsx 寫好基本的菜單
配置路由?/router/index.ts
import { createRouter, createWebHashHistory } from "vue-router";
import Index from "@/views";const router = createRouter({history: createWebHashHistory(),routes: [{path: "/:afterUser(.*)", // 正則匹配url 跳轉不會報錯component: Index,},],
});export default router;
配置vite.config.js? ?根路徑base
import { defineConfig } from "vite";
import vue from "@vitejs/plugin-vue";
import vueJsx from "@vitejs/plugin-vue-jsx";
import { resolve } from "path";
export const pathResolve = (dir: string) => resolve(process.cwd(), ".", dir);export default defineConfig(({ mode }) => {return {base: "/qiankun-base/",plugins: [vue(), vueJsx()],server: {host: "0.0.0.0",port: 1000,},resolve: {alias: {"@": pathResolve("src"),},},};
});
修改dom根節(jié)點 和主應用一樣改為子應用的項目名稱 改這個是為了主應用和子應用的掛載在根節(jié)點的id不會重復,如果一樣的話會導致主應用頁面渲染不出來子應用(這里不在展示細節(jié))
qiankun配置步驟(上面還沒開始)
主應用qiankun-base中下載qiankun
yarn add qiankun
在main.ts中開啟?
注意:在掛載之前開啟
import { createApp } from "vue";
import "./style.css";
import App from "./App.vue";
import { start } from "qiankun";
import router from "./router";
import antv from "ant-design-vue";
start({sandbox: {// strictStyleIsolation: true, // 開啟嚴格的樣式隔離模式experimentalStyleIsolation: true, // 開啟后所有樣式都會加上一個類名 .app-main {} ===> div[data-qiankun-react16] .app-main {}},singular: false, // 單一時間只渲染一個微應用,默認為true
});
createApp(App).use(router).use(antv).mount("#qiankun-base");
vue子應用 qiankun-child-vue
下載vite-plugin-qiankun插件
yarn add vite-plugin-qiankun
配置vite.config.js 使用vite-plugin-qiankun
import { defineConfig, loadEnv } from "vite";
import vue from "@vitejs/plugin-vue";
import vueJsx from "@vitejs/plugin-vue-jsx";
import qiankun from "vite-plugin-qiankun";
import { resolve } from "path";export const pathResolve = (dir: string) => resolve(process.cwd(), ".", dir);export default defineConfig(({ mode }) => {const env = loadEnv(mode, process.cwd());return {base: mode == "production" ? `/${env.VITE_APP_NAME}/` : "",plugins: [vue(),vueJsx(),qiankun(env.VITE_APP_NAME, { useDevMode: true }),],server: {host: "0.0.0.0",port: 2000,},resolve: {alias: {"@": pathResolve("src"),},},};
});
修改main.ts 判斷是在主應用還是子應用中
import "./style.css";
import App from "./App.vue";
import router from "./router";
import antv from "ant-design-vue";
import { render } from "@/hooks/microApp";render(App, "#qiankun-child-vue", (app, props) => {app.use(router).use(antv);
});
/hooks/microApp.ts
import renderWithQiankun, {QiankunProps,qiankunWindow,
} from "vite-plugin-qiankun/dist/helper";
import { App, Component, createApp } from "vue";const isMicroApp = qiankunWindow.__POWERED_BY_QIANKUN__;
export const render = (AppRoot: Component,domId,configApp: (app: App, props?: QiankunProps) => any
) => {let app: App;const _render = (props: QiankunProps = {}) => {const { container } = props;const root: string | Element = container? container.querySelector(domId)!: domId; // 避免 id 重復導致微應用掛載失敗app = createApp(AppRoot);// 回調配置app的函數 讓調用的地方 可以使用appconfigApp(app, props);app.mount(root);};const initQiankun = () => {renderWithQiankun({bootstrap() {// console.log("微應用:bootstrap");},mount(props) {// 獲取主應用傳入數據// console.log("微應用:mount", props);_render(props);},unmount(props) {// console.log("微應用:unmount", props);app.unmount();},update(props) {// console.log("微應用:update", props);},});};isMicroApp ? initQiankun() : _render();
};
在views/index.tsx 增加判斷邏輯 是在主應用中還是在子應用中
import { qiankunWindow } from "vite-plugin-qiankun/dist/helper";return () => {
// 判斷如果在主應用中if (qiankunWindow.__POWERED_BY_QIANKUN__) {return <router-view></router-view>;}
// 在子應用中return (<a-layout class="layout"><a-layout-sider><MenuonSelect={handleMenuSelect}style="height:100%"mode="inline"items={menuList.value}></Menu></a-layout-sider><a-layout-content><RouterView></RouterView></a-layout-content></a-layout>);};
易錯點
1.主應用和子應用掛載在根節(jié)點的domid是同一個
2.主應用配置路徑和子應用路徑不一致