美文网首页
vue3 主应用

vue3 主应用

作者: sweetBoy_9126 | 来源:发表于2023-02-23 16:21 被阅读0次

    main

    • main.ts
    import { createApp } from "vue"
    import App from "./App"
    import router from "./router"
    import "@/micro"
    import { store } from "@/store"
    
    createApp(App).use(router).use(store).mount("#app")
    
    • micro/app.ts
    import actions from "@/shared/action"
    import { store } from "@/store"
    
    const getActiveRule = (hash: string) => (location: { hash: string }) =>
      location.hash.startsWith(hash)
    
    const microApps: {
      name: string
      entry: string
      activeRule: string
    }[] = [
      {
        name: "subapp",
        entry: "http://127.0.0.1:8082",
        activeRule: "#/subapp",
      },
    ]
    
    export const apps = microApps.map((item) => {
      return {
        ...item,
        container: "#subapp", // 子应用挂载的div
        activeRule: getActiveRule(item.activeRule),
        props: {
          routerBase: item.activeRule, // 下发基础路由
          // getGlobalState: actions.getGlobalState, // 下发getGlobalState方法
          store,
        },
      }
    })
    
    • micro/index.ts
    import {
      addGlobalUncaughtErrorHandler,
      registerMicroApps,
      start,
    } from "qiankun"
    import Nprogress from "nprogress"
    import "nprogress/nprogress.css"
    import { ElLoading } from "element-plus"
    import { apps } from "./app"
    let loadingInstance: ReturnType<typeof ElLoading.service>
    
    registerMicroApps(apps, {
      beforeLoad: (app) => {
        Nprogress.start()
        loadingInstance = ElLoading.service()
        console.log("before load", app.name)
        return Promise.resolve()
      },
      afterMount: (app) => {
        Nprogress.done()
        loadingInstance.close()
        console.log("after mount", app.name)
        return Promise.resolve()
      },
    })
    
    addGlobalUncaughtErrorHandler(() => {
      loadingInstance.close()
      console.log("子应用加载失败,请检查应用是否可运行")
    })
    
    export { start }
    
    • router/index.ts
    import { createRouter, createWebHashHistory, RouteRecordRaw } from 'vue-router';
    
    export const routes: RouteRecordRaw[] = [
      // 基座应用自己的路由
      {
        path: '/login',
        component: () => import('../views/Login'),
      },
      {
        path: "",
        component: () => import('../views/Layout'),
        redirect: "/home",
        children: [
          {
            path: "/home",
            name: "首页",
            component: () => import('../views/Home'),
          },
          {
            path: "/subapp",
            component: () => import('../views/SubApp'),
            name: "子应用",
            children: [
              {
                path: "/subapp/child1",
                name: "child1",
                // 这里的二级菜单组件是没有意义的,但是 ts 必须要传一个 component,所以随便写一个就行,为了解决 ts bao'cuo
                component: import("../views/Portal")
              },
              {
                path: "/subapp/child2",
                name: "child2",
                component: import("../views/Portal")
              }
            ]
          }
        ]
      },
    ];
    
    const router = createRouter({
      history: createWebHashHistory(),
      routes,
    });
    
    export default router;
    
    • store/index.ts
    import actions from "@/shared/action"
    import { createStore } from "vuex"
    
    export const store = createStore({
      state() {
        return {
          token: "111",
          name: "",
        }
      },
      mutations: {
        setToken(state, token) {
          state.token = token
          actions.setGlobalState({ ...state, token })
          actions.offGlobalStateChange()
        },
        setName(state, name) {
          state.name = name
        },
      },
    })
    
    • App.tsx
    import { defineComponent } from "vue"
    import "element-plus/dist/index.css"
    import "./App.less"
    import { useStore } from "vuex"
    const App = defineComponent({
      setup: (props, context) => {
        const store = useStore()
    
        return () => (
          <>
            <div>token: {store.state.token}</div>
            <router-view />
          </>
        )
      },
    })
    export default App
    

    这里需要注意如果你的子应用是二级菜单那么你的容器 id,必须在根 Layout 组件或者一级路由组件里声明,而且容器id和子应用调用必须写在同一个组件里,否则打包后无效,因为我们可能有多个子应用,所以我直接在 Layout 里写,这样就避免我每次都要写一遍容器id和调用一次start

    • views/Layout
    import { defineComponent, onMounted } from "vue"
    import { ElContainer, ElAside, ElMain, ElHeader } from "element-plus"
    import s from "./index.module.less"
    import { SlideBar } from "../../components/SliderBar"
    import { start } from "qiankun"
    import { store } from "@/store"
    const Layout = defineComponent({
      setup: (props, context) => {
        const handleClick = () => {
          store.commit("setToken", "2222")
        }
        onMounted(() => {
          if (!window.qiankunStarted) {
            window.qiankunStarted = true
            start({
              sandbox: {
                experimentalStyleIsolation: true,
              },
            })
          }
        })
        return () => (
          <div class={[s.layoutWrapper, "common-layout"]} onClick={handleClick}>
            <ElContainer>
              <ElHeader class={s.header}>header</ElHeader>
              <ElContainer>
                <ElAside width='240px' class={s.aside}>
                  <SlideBar />
                </ElAside>
                <ElMain class={s.main}>
                    <div id="subapp" />
                  <router-view />
                </ElMain>
              </ElContainer>
            </ElContainer>
          </div>
        )
      },
    })
    export default Layout
    
    • SlideBar/index.tsx
    import { ElMenu, ElMenuItem, ElSubMenu } from 'element-plus';
    import { defineComponent } from 'vue';
    import { RouteRecordRaw } from 'vue-router';
    import { routes } from '../../router';
    import s from './index.module.less'
    export const SlideBar = defineComponent({
      setup: (props, context) => {
        const filterRoutes = routes.filter((route)=> route.path === "")[0]?.children
    
        const renderItem = (route: RouteRecordRaw) => {
          return (
            <>
              {route.path ? (
                <ElMenuItem index={route.path}>{route.name}</ElMenuItem>
              ) : null} 
            </>
          )
        }
    
        return () => (
          <div class={s.slideBarWrapper}>
            <ElMenu
              activeTextColor='#ffd04b' 
              backgroundColor='#545c64'
              class={"el-menu-vertical-demo"}
              textColor="#fff"
              router
            >
              {
                filterRoutes!.map(route => (
                  <>
                    {
                      route.children && !!route.children.length ? (
                        <ElSubMenu index={route.path}
                          v-slots={
                            {
                              title: () => (<span>{route.name}</span>)
                            }
                          } 
                        >
                            {
                              route.children.map(child => renderItem(child))
                            }
                          </ElSubMenu>
                      ) : renderItem(route)
                    } 
                  </>
                ))
              }
            </ElMenu>
          </div>
        )
      }
    });
    
    • views/Portal/index.tsx
    import { start } from "@/micro"
    import { defineComponent, onMounted } from "vue"
    
    const Portal = defineComponent({
      setup: (props, context) => {
        return () => <div  />
      },
    })
    export default Portal
    
    • views/SubApp/index.tsx
    import { start } from 'qiankun';
    import { defineComponent, onMounted } from 'vue';
    const SubApp = defineComponent({
      setup: (props, context) => {
        return () => (
          <>
          <router-view />
          </>
        )
      }
    });
    export default SubApp;
    
    • vite.config.ts
    import { defineConfig } from "vite"
    import vue from "@vitejs/plugin-vue"
    import vueJsx from "@vitejs/plugin-vue-jsx"
    import { resolve } from "path"
    
    // https://vitejs.dev/config/
    export default defineConfig({
      plugins: [
        vue(),
        vueJsx({
          transformOn: true,
          mergeProps: true,
        }),
      ],
      resolve: {
        extensions: [".ts", ".tsx", ".json", ".js", ".vue"],
        alias: {
          "@": resolve("src"),
        },
      },
    })
    

    子应用

    • main.ts
    import { createPinia } from "pinia"
    import {
      renderWithQiankun,
      qiankunWindow,
      QiankunProps,
    } from "vite-plugin-qiankun/dist/helper"
    import { createApp, App as VueApp } from "vue"
    import App from "./App"
    import { router } from "./router"
    import { useUserStore } from "./store/user"
    
    let app: VueApp<Element>
    
    const render = (props: QiankunProps | { container: string }) => {
      const { container } = props
      app = createApp(App)
      app
        .use(router)
        .use(createPinia())
        .mount(
          container
            ? container instanceof HTMLElement
              ? container.querySelector("#app")!
              : container
            : document.getElementById("app")!
        )
    }
    
    if (!qiankunWindow.__POWERED_BY_QIANKUN__) {
      render({ container: "#app" })
    } else {
      renderWithQiankun({
        mount(props) {
          console.log(props, "sss")
          const { store: parentStore, onGlobalStateChange, setGlobalState } = props
          console.log(1)
          onGlobalStateChange((state, prev) => {
            console.log(state, prev)
            store.setToken(state.token)
          })
          console.log("--mount")
          render(props)
          const store = useUserStore()
          store.setToken(parentStore.state.token)
        },
        bootstrap() {
          console.log("--bootstrap")
        },
        update() {
          console.log("--update")
        },
        unmount() {
          console.log("--unmount")
          app?.unmount()
        },
      })
    }
    
    • vite.config.ts
    iimport vueJsx from "@vitejs/plugin-vue-jsx"
    import { defineConfig, loadEnv } from "vite"
    import { resolve } from "path"
    import vue from "@vitejs/plugin-vue"
    import qiankun from "vite-plugin-qiankun"
    
    // https://vitejs.dev/config/
    export default defineConfig(({ mode }) => {
      // base: "http://localhost:3002/",
      const root = process.cwd()
      const env = loadEnv(mode, root)
      const { VITE_PUBLIC_PATH, VITE_DROP_CONSOLE } = env
      return {
        base: mode !== "production" ? "/" : "http://127.0.0.1:8082/", // 生产环境需要指定域名
        server: {
          port: 8082,
          cors: true,
        },
        resolve: {
          extensions: [".ts", ".tsx", ".json", ".js", ".vue"],
          alias: {
            "@": resolve("src"),
          },
        },
        plugins: [
          vue(),
          vueJsx({
            transformOn: true,
            mergeProps: true,
          }),
          qiankun("subapp", {
            useDevMode: true,
          }),
        ],
      }
    })
    
    • router
    import { qiankunWindow } from "vite-plugin-qiankun/dist/helper"
    import { createRouter, createWebHashHistory, RouteRecordRaw } from "vue-router"
    
    const routes: RouteRecordRaw[] = [
      {
        path: "/subApp",
        component: () => import("@/views/Layout"),
        name: "Layout",
        children: [
          {
            path: "a",
            component: () => import("@/views/A"),
          },
        ],
      },
    ]
    
    export const router = createRouter({
      history: createWebHashHistory(
        qiankunWindow.__POWERED_BY_QIANKUN__ ? "/subapp" : "/"
      ),
      routes,
    })
    

    相关文章

      网友评论

          本文标题:vue3 主应用

          本文链接:https://www.haomeiwen.com/subject/rhqtldtx.html