美文网首页
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