• vue微前端qiankun框架学习到项目实战


    微前端架构

    一、什么是微前端架构

           微前端是一种多个团队通过独立发布功能的方式来共同构建现代化web应用的技术手段及方法策略。
           微前端借鉴了微服务的架构理念,将一个庞大的前端应用才分为多个独立灵活的小型应用,每个应用都可以独立开发,独立运行,独立部署,再将这些小型应用联合为一个完整的应用。微前端既可以将多个项目融合为一,又可以减少项目之间的耦合,提升项目扩展性,相比一整块的前端仓库,微前端架构下的前端仓库倾向于更小更灵活。

    二、特性

    1. 技术栈无关,主框架不限制介入应用的技术栈,子应用可以自主选中技术栈
      2.独立开发/部署 各个团队之间仓库独立,单独部署,互不依赖
      3.增量升级 当一个应用庞大之后,技术升级或重构相当的麻烦,而微应用具备渐进式升级的特性
      4.基座下的子应用之间运行时互不依赖,有独立的状态管理
      5.提升效率 应用越庞大,越难以维护,协作效率低下,微应用可以很好的拆分,提升效率

    三、有那几种技术去实现微前端架构呢?

    • iframe

    也是最早最熟悉的解决方案,就是通过iframe独立运行一个项目,非常简单,无须任何改造,天然沙箱【完美隔离JS,css,都是独立运行的环境】,不限制使用,页面上可以放多个iframe来组合业务,当然也是逃不过技术的两面性,又有点就会有缺点,无法保持路由状态,刷新后路由状态丢失(这点也是完全不能解决,可以将路由)

    • single-spa

    single-spa是最早的微前端框架,可以兼容很多技术栈。
    single-spa首先在基座中注册所有子应用的路由,当URL改变时就会去进行匹配,匹配到哪个子应用就会去加载对应的那个子应用。相对于iframe的实现方案,single-spa中基座和各个子应用之间共享着一个全局上下文,并且不存在URL不同步和UI不同步的情况,但是single-spa没有实现js隔离和css隔离,需要修改大量的配置,包括基座和子应用的,不能开箱即用

    • qiankun

    qiankun是阿里开源的一个微前端的框架,在阿里内部已经经过一批线上应用的充分检验及打磨了,所以可以放心使用。
    基于single-spa封装的,提供了更加开箱即用的API,技术栈无关,任意技术栈的应用均可使用/接入,不论是
    React/Vue/Angular/JQuery 还是其他等框架。 HTML
    Entry的方式接入,像使用iframe一样简单,实现了single-spa不具备的样式隔离和js隔离
    资源预加载,在浏览器空闲时间预加载未打开的微应用资源,加速微应用打开速度。

    四、Qiankun用到的API介绍

    registerMicroApps(apps,lifeCycles?): 自动加载模块,一次性写好配置,直接传入,然后调用start(),qiankun会自动监听UR了变化调用对应用的暴露的生命周期函数。
    start(opts?): 配合registerMicroApps使用,当调用registerMicroApps后,运行启动。
    loadMicroApp(app,configuration?) :手动加载模块,需要自己监听URL并手动加载模块。
    addGlobalUncaughtErrorHandler(handler)/removeGlobalUncaughtErrorHandler(handler): 添加/移除监听应用加载错误。
    initGlobalState(state) 初始化全局共享状态,类似于vuex,返回两个方法。
    setGlobalState(state) 设置全局状态。
    onGlobalStateChange((newState,oldState)=>{}) 监听全局状态变化。

    五、qiankun注册子应用参数说明

    qiankun框架注册子组件参数说明
    例如:
    qiankun框架注册子应用

    六、qiankun框架适合项目结构

    qiankun适用项目类型

    | -- 主体文件
         | -- .git
         | -- common // 公共模板
         | -- main // 主应用
              | -- package.json
         | -- aps // aps应用
              | -- package.json
         | -- wms // wms应用
              | -- package.json
         | -- 基础设置 // 基础设置应用
              | -- package.json
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11

    七、路由设计

    项目是有一个登录页的,但是登录页不加载子应用,只有通过登录成功后,跳到第一个页面,才进行加载子应用的。
    qiankun中路由的设置

    八、一起运行和独立运行

    • 一起运行

    公用菜单模块,公用页头,公用页底部模块,只有内容改变,登陆成功后内容页面跳转到对应的子应用页面的内容中。登陆成功后默认跳转到启动页面,通过全局路由守卫进行判断,判断跳转到这个路由,根据获取路由表数据,再跳入到路由表的第一个路由,如果路由表没有数据,则代表这个用户没有菜单,那就没有访问权限,直接调回路由页面。
    注意:主应用登录成功后,把路由存到全局状态中,除了主应用addRoute添加路由外。处理路由可以通过路由守卫获取所有菜单后,然后通过判断前缀,把相应的子应用通过apps配置的props传递进去。
    还可以在每个子应用第一次运行的时候,在全局路由守卫判断是否是第一次运行的,直接获取全局状态里的路由表,循环判断是否属于当前子应用的路由再addRoute进去。

    在这里插入图片描述

    • 独立运行

    是指应用独立运行,运行后登录页,基础模块,包括菜单,注销,还能正常使用,这个时候就需要吧登录页,菜单,App三个模块迁到common模块,通过引入的方式,然后根据window.__PROWERED_BY_QIANKUN__判断当前运行环境是否独立运行做相对应的逻辑处理,window.PROWERED_BY_QIANKUN===true为一起运行,window.PROWERED_BY_QIANKUN===false为独立运行。
    qiankun框架一起运行

    九、基于qiankun的一个Demo实例

    (1). Demo文件结构

    基于qiankun的项目结构

    (2). 主应用配置,基座

    基座app-main采用的是vue-cli3搭建,它用来导航渲染和登陆的下发,为子应用提供一个挂载的容器div,基座应该保持简洁,不做过多的业务操作,qiankun只要在基座中引入,再main.js中注册子应用即可。

    • 引入 registerMicroApps,start,setDefaultMountApp
    import { registerMicroApps,start,setDefaultMountApp } from 'qiankun'
    
    • 1
    1. 注册子应用
    // 1. 注册子应用
    registerMicroApps([
      {
        name:"open-platform-admin",
        entry:"//localhost:81/",  //子应用页面访问入口
        container:"#subapp-container", //子应用渲染的出口
        activeRule:"/open-platform-admin", // 路径匹配规则, // 路径匹配规则
        sandbox:{
          strictStyleIsolation: true, // 开启样式隔离
          routerBase: '/open-platform-admin',
        }
      },
    ])
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 默认打开子应用
    setDefaultMountApp("/")  //默认打开子应用
    
    • 1
    • 启动微前端架构
    start()
    
    • 1
    1. 在 App.vue中,需要声明 main.js配置的子应用挂载div(注意id一定要一致),以及基座布局相关的,我当前使用的是layout,所以配置再AppMain.vue中,这样,基座就算配置完成了。项目启动后,子应用将会挂载到

      中。
      主应用挂载子应用

    2. 子应用配置
      在根目录下创建一个子应用,open-admin-web,子应用最好与在基座主应用main.js中配置的名称一致,这样可以直接使用package.json中的name作为output。
      vue.config.js,devServer的端口改为与主营用配置的一致,且加上跨域headers和output配置。
      qiankun子应用配置vue.config.js
      qiankun子应用配置vue.config.js
      新增src/public-path.js

      	if (window.__POWERED_BY_QIANKUN__) {
      	  __webpack_public_path__ = window.__INJECTED_PUBLIC_PATH_BY_QIANKUN__;
      	}
      
      • 1
      • 2
      • 3

      qiankun子应用配置
      src/router/index.js改为只暴露routes, new Router改到 main.js中声明,并改造main.js,并引入src下创建的public-path.js,改写render,添加生命周期函数,最终结果如下⬇

    import './public-path';
    import Vue from 'vue'
    import Cookies from 'js-cookie'
    import Element from 'element-ui'
    import './assets/styles/element-variables.scss'
    // import 'ant-design-vue/dist/antd.css';
    import "ant-design-vue/dist/antd.less"
    import '@/assets/styles/index.scss' // global css
    import '@/assets/styles/ruoyi.scss' // ruoyi css
    import App from './App'
    import store from './store'
    import router from './router'
    import directive from './directive' // directive
    import plugins from './plugins' // plugins
    import { download } from '@/utils/request'
    
    import './assets/icons' // icon
    import './permission' // permission control
    import { getDicts } from "@/api/system/dict/data";
    import { getConfigKey } from "@/api/system/config";
    import { parseTime, resetForm, addDateRange, selectDictLabel, selectDictLabels, handleTree } from "@/utils/ruoyi";
    // 分页组件
    import Pagination from "@/components/Pagination";
    // 自定义表格工具组件
    import RightToolbar from "@/components/RightToolbar"
    // 富文本组件
    import Editor from "@/components/Editor"
    // 文件上传组件
    import FileUpload from "@/components/FileUpload"
    // 图片上传组件
    import ImageUpload from "@/components/ImageUpload"
    // 图片预览组件
    import ImagePreview from "@/components/ImagePreview"
    // 字典标签组件
    import DictTag from '@/components/DictTag'
    // 头部标签组件
    import VueMeta from 'vue-meta'
    // 字典数据组件
    import DictData from '@/components/DictData'
    
    //局部使用antDesign-vue中的tree组件
    import { Tree } from 'ant-design-vue';
    import { Table } from 'ant-design-vue';
    import { Icon } from 'ant-design-vue';
    
    // 全局方法挂载
    Vue.prototype.getDicts = getDicts
    Vue.prototype.getConfigKey = getConfigKey
    Vue.prototype.parseTime = parseTime
    Vue.prototype.resetForm = resetForm
    Vue.prototype.addDateRange = addDateRange
    Vue.prototype.selectDictLabel = selectDictLabel
    Vue.prototype.selectDictLabels = selectDictLabels
    Vue.prototype.download = download
    Vue.prototype.handleTree = handleTree
    
    
    // 全局组件挂载
    Vue.component('DictTag', DictTag)
    Vue.component('Pagination', Pagination)
    Vue.component('RightToolbar', RightToolbar)
    Vue.component('Editor', Editor)
    Vue.component('FileUpload', FileUpload)
    Vue.component('ImageUpload', ImageUpload)
    Vue.component('ImagePreview', ImagePreview)
    Vue.component('ATree', Tree)
    Vue.component('ATable', Table)
    Vue.component('AIcon', Icon)
    
    Vue.use(directive)
    Vue.use(plugins)
    Vue.use(VueMeta)
    DictData.install()
    
    /**
     * If you don't want to use mock-server
     * you want to use MockJs for mock api
     * you can execute: mockXHR()
     *
     * Currently MockJs will be used in the production environment,
     * please remove it before going online! ! !
     */
    
    Vue.use(Element, {
      size: Cookies.get('size') || 'medium' // set element-ui default size
    })
    let instance = null
    
    async function render(props={}){
      const { container } = props;
      instance = new Vue({
          router,
          store,
          render: h => h(App)
        }).$mount(
          container
          ?
          container.querySelector('#app')  //渲染到主应用的入口
          :'#app' //独立运行的时候
        ) 
    }
    // 在被qiankun引用时 修改运行时的 `publicPath`
    if (window.__POWERED_BY_QIANKUN__) { 
      __webpack_public_path__ = window.__INJECTED_PUBLIC_PATH_BY_QIANKUN__;
    }
    //如果独立运行的时候,判断是否是独立运行
    if (!window.__POWERED_BY_QIANKUN__) {
      render();
    }
    /**
     * 子应用建议使用qiankun的规则来接入不需要安装任何依赖,
     * 只需要再三个入口到二u三个必须的钩子函数给qiankun主应用使用
     * 钩子函数必须返回promise(启动的时候调用)
     */
    export async function bootstrap() {
      console.log('[vue] vue app bootstraped');
    }
    export async function mount(props) {
      // console.log('[vue] props from main framework333333', props);
      render(props);
    }
    /**
     * 可选生命周期钩子,仅使用 loadMicroApp 方式加载微应用时生效
     */
    export async function update(props) { 
      console.log('update props', props);
    }
    export async function unmount() {
      instance.$destroy();
      instance.$el.innerHTML = '';
      instance = null;
    }
    export default instance;
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45
    • 46
    • 47
    • 48
    • 49
    • 50
    • 51
    • 52
    • 53
    • 54
    • 55
    • 56
    • 57
    • 58
    • 59
    • 60
    • 61
    • 62
    • 63
    • 64
    • 65
    • 66
    • 67
    • 68
    • 69
    • 70
    • 71
    • 72
    • 73
    • 74
    • 75
    • 76
    • 77
    • 78
    • 79
    • 80
    • 81
    • 82
    • 83
    • 84
    • 85
    • 86
    • 87
    • 88
    • 89
    • 90
    • 91
    • 92
    • 93
    • 94
    • 95
    • 96
    • 97
    • 98
    • 99
    • 100
    • 101
    • 102
    • 103
    • 104
    • 105
    • 106
    • 107
    • 108
    • 109
    • 110
    • 111
    • 112
    • 113
    • 114
    • 115
    • 116
    • 117
    • 118
    • 119
    • 120
    • 121
    • 122
    • 123
    • 124
    • 125
    • 126
    • 127
    • 128
    • 129
    • 130
    • 131
    • 132
    • 133
    1. 配置基座路由
     {
        path: '/open-platform-admin',
        component: Layout,
        redirect: '/open-platform-admin/user',
        name: 'open-platform-admin',
        meta: { title: '用户管理', icon: 'el-icon-notebook-2' },
        children: [
          {
            path: 'user/user',
            name: 'user',
            // component: () => import('@/views/article/index'),
            meta: { title: '用户管理', icon: 'el-icon-notebook-1' }
          },
        ]
      },
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    1. Demo效
      qiankun框架Deno
      十、使用官方提供的props实现父子应用通讯问题
      父组件main.js,注册子应用的时候直接把父组件的sotre都传递给子组件
    // 1. 注册微应用
    registerMicroApps([
      {
        name: 'son',
        entry: '//localhost:8999/', // 子应用页面访问入口
        container: '#subapp-container', // 子应用渲染的出口
        activeRule: '/open-platform-admin', // 路径匹配规则
        sandbox: {
          strictStyleIsolation: true, // 开启样式隔离
          routerBase: '/open-platform-admin'
        },
        props:{sharedStore:store}
      }
    ])
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14

    子组件判断当前是否是微服务打开状态,接收父组件传递过来的props

    
    async function render(props={}){
      // console.log('micro-app-test-vue')
      const { container } = props;
      instance = new Vue({
          router,
          store,
          render: h => h(App),
          beforeCreate(){
            if (window.__POWERED_BY_QIANKUN__) {
              store.state.user = props.sharedStore.state.user
            }
          }
        }).$mount(
          container
          ?
          container.querySelector('#app')  //渲染到主应用的入口
          :'#app' //独立运行的时候
        ) 
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20

    基座登陆后,将token传递给子应用,由于父子应用都共享一个store实例,所以父应用做修改,子应用也做相应的修改。
    在父组件和子组件中打印用户名和token
    qiankun微前端状态共享

    部署中遇到的问题

    基座,子应用都有属于自己的base基础路径,基座访问子应用的时候出现404问题。

    假如基座的基础路径为 “hsk-admin”,子应用的基础路径为 “business-module”,再后端部署不使用端口号的情况下,基座访问子应用的时候会出现地址为:http://192.168.10.205/hsk-admin/business-module/tenant/tenant,就会出现/hsk-admin/business-module/再子应用中找不到该路由,报404的错误
    方法:加载子应用的时候将及做的base传递给子应用,子应用拿到后根据当前是否是qiankun环境进行子路由的base设置。

    404问题

    访问子应用的时候,子应用没有hsk-admin/business-module/路径就会出现
    前端微服务

    基座路由配置:

    import Vue from 'vue'
    import Router from 'vue-router'
    
    Vue.use(Router)
    
    /* Layout */
    import Layout from '@/layout'
    
    /**
     * Note: 路由配置项
     *
     * hidden: true                     // 当设置 true 的时候该路由不会再侧边栏出现 如401,login等页面,或者如一些编辑页面/edit/1
     * alwaysShow: true                 // 当你一个路由下面的 children 声明的路由大于1个时,自动会变成嵌套的模式--如组件页面
     *                                  // 只有一个时,会将那个子路由当做根路由显示在侧边栏--如引导页面
     *                                  // 若你想不管路由下面的 children 声明的个数都显示你的根路由
     *                                  // 你可以设置 alwaysShow: true,这样它就会忽略之前定义的规则,一直显示根路由
     * redirect: noRedirect             // 当设置 noRedirect 的时候该路由在面包屑导航中不可被点击
     * name:'router-name'               // 设定路由的名字,一定要填写不然使用时会出现各种问题
     * query: '{"id": 1, "name": "ry"}' // 访问路由的默认传递参数
     * roles: ['admin', 'common']       // 访问路由的角色权限
     * permissions: ['a:a:a', 'b:b:b']  // 访问路由的菜单权限
     * meta : {
        noCache: true                   // 如果设置为true,则不会被  缓存(默认 false)
        title: 'title'                  // 设置该路由在侧边栏和面包屑中展示的名字
        icon: 'svg-name'                // 设置该路由的图标,对应路径src/assets/icons/svg
        breadcrumb: false               // 如果设置为false,则不会在breadcrumb面包屑中显示
        activeMenu: '/system/user'      // 当路由设置了该属性,则会高亮相对应的侧边栏。
      }
     */
    // 公共路由
    export const constantRoutes = [
      {
        path: '/business-module',
        component: Layout,
        name: 'business-module',
        meta: { title: '业务系统', icon: 'tool' },
        children: [
          {
            path: 'tenant/tenant',
            name: 'tenant',
            meta: { title: '租户管理', noCache:true,icon: 'tool' }
          },
          {
            path: 'applicationManagement/applicationManagement',
            name: 'applicationManagement',
            meta: { title: '应用设置', noCache:true,icon: 'tool' }
          },
        ]
      },
    ]
    // 动态路由,基于用户权限动态去加载
    export const dynamicRoutes = [
      
    ]
    
    // 防止连续点击多次路由报错
    let routerPush = Router.prototype.push;
    let routerReplace = Router.prototype.replace;
    // push
    Router.prototype.push = function push(location) {
      return routerPush.call(this, location).catch(err => err)
    }
    // replace
    Router.prototype.replace = function push(location) {
      return routerReplace.call(this, location).catch(err => err)
    }
    
    export default new Router({
      mode: 'history', // 去掉url中的#
      base: 'hsk-admin',
      scrollBehavior: () => ({ y: 0 }),
      routes: constantRoutes
    })
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45
    • 46
    • 47
    • 48
    • 49
    • 50
    • 51
    • 52
    • 53
    • 54
    • 55
    • 56
    • 57
    • 58
    • 59
    • 60
    • 61
    • 62
    • 63
    • 64
    • 65
    • 66
    • 67
    • 68
    • 69
    • 70
    • 71
    • 72
    • 73

    基座vue.config.js

     publicPath: process.env.NODE_ENV === 'production' ? '/' : '/hsk-admin',
    
    • 1

    基座注册子应用配置

    registerMicroApps([
      {
        name: 'son',
        entry: '//localhost:8999/business-module/', // 子应用页面访问入口
        container: '#subapp-container', // 子应用渲染的出口
        activeRule: '/hsk-admin/business-module', // 路径匹配规则
        sandbox: {
         strictStyleIsolation: true, // 开启样式隔离
        },
        props:{sharedStore:store,baseName:'/hsk-admin/business-module'}
      },
    ])
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12

    hsk-admin为基座的基础路径,business-module为子应用的基础路径,如果在基座中访问必须设置子应用的路由base为/hsk-admin/business-module/

    子应用的main.js配置

    import './public-path';
    
    import Vue from 'vue'
    
    import Cookies from 'js-cookie'
    
    import Element from 'element-ui'
    import './assets/styles/element-variables.scss'
    
    // import 'ant-design-vue/dist/antd.css';
    
    import "ant-design-vue/dist/antd.less"
    
    import '@/assets/styles/index.scss' // global css
    import '@/assets/styles/ruoyi.scss' // ruoyi css
    import App from './App'
    import store from './store'
    import router, {constantRoutes} from './router'
    import directive from './directive' // directive
    import plugins from './plugins' // plugins
    //引入hsk组件
    import hskui from "hsk-ui"
    //引入hsk方法
    import { hskMsgbox } from 'hsk-ui/commonUtils'
    import { download } from '@/utils/request'
    
    import './assets/icons' // icon
    import './permission' // permission control
    import { getDicts } from "@/api/system/dict/data";
    import { getConfigKey } from "@/api/system/config";
    import { parseTime, resetForm, addDateRange, selectDictLabel, selectDictLabels, handleTree } from "@/utils/ruoyi";
    // 分页组件
    import Pagination from "@/components/Pagination";
    // Vue.prototype.hskMsgbox = hskui.hskMsgbox.hskMsgbox
    // 自定义表格工具组件
    import RightToolbar from "@/components/RightToolbar"
    // 富文本组件
    import Editor from "@/components/Editor"
    // 文件上传组件
    import FileUpload from "@/components/FileUpload"
    // 图片上传组件
    import ImageUpload from "@/components/ImageUpload"
    // 图片预览组件
    import ImagePreview from "@/components/ImagePreview"
    // 字典标签组件
    import DictTag from '@/components/DictTag'
    // 头部标签组件
    import VueMeta from 'vue-meta'
    // 字典数据组件
    import DictData from '@/components/DictData'
    // import action from '../src/action'
    //局部使用antDesign-vue中的tree组件
    import { Tree } from 'ant-design-vue';
    import { Table } from 'ant-design-vue';
    import { Icon } from 'ant-design-vue';
    import Router from "vue-router";
    // 全局方法挂载
    Vue.prototype.hskMsgbox = hskMsgbox
    Vue.prototype.getDicts = getDicts
    Vue.prototype.getConfigKey = getConfigKey
    Vue.prototype.parseTime = parseTime
    Vue.prototype.resetForm = resetForm
    Vue.prototype.addDateRange = addDateRange
    Vue.prototype.selectDictLabel = selectDictLabel
    Vue.prototype.selectDictLabels = selectDictLabels
    Vue.prototype.download = download
    Vue.prototype.handleTree = handleTree
    
    
    // 全局组件挂载
    Vue.component('DictTag', DictTag)
    Vue.component('Pagination', Pagination)
    Vue.component('RightToolbar', RightToolbar)
    Vue.component('Editor', Editor)
    Vue.component('FileUpload', FileUpload)
    Vue.component('ImageUpload', ImageUpload)
    Vue.component('ImagePreview', ImagePreview)
    Vue.component('ATree', Tree)
    Vue.component('ATable', Table)
    Vue.component('AIcon', Icon)
    
    Vue.use(directive)
    Vue.use(plugins)
    Vue.use(VueMeta)
    DictData.install()
    
    /**
     * If you don't want to use mock-server
     * you want to use MockJs for mock api
     * you can execute: mockXHR()
     *
     * Currently MockJs will be used in the production environment,
     * please remove it before going online! ! !
     */
    
    Vue.use(Element, {
      size: Cookies.get('size') || 'medium' // set element-ui default size
    })
    Vue.use(hskui)
    let instance = null
    Cookies.set("client_id","admin")
    async function render(props={}){
      const { container } = props;
      instance = new Vue({
          router,
          store,
          render: h => h(App),
          beforeCreate(){
            if (window.__POWERED_BY_QIANKUN__) {
              store.state.user = props.sharedStore.state.user
            }
          }
        }).$mount(
          container
          ?
          container.querySelector('#app')  //渲染到主应用的入口
          :'#app' //独立运行的时候
        )
    }
    
    // 在被qiankun引用时 修改运行时的 `publicPath`
    if (window.__POWERED_BY_QIANKUN__) {
      __webpack_public_path__ = window.__INJECTED_PUBLIC_PATH_BY_QIANKUN__;
    }
    //如果独立运行的时候,判断是否是独立运行
    if (!window.__POWERED_BY_QIANKUN__) {
      render();
    }
    
    /**
     * 子应用建议使用qiankun的规则来接入不需要安装任何依赖,
     * 只需要再三个入口到二u三个必须的钩子函数给qiankun主应用使用
     * 钩子函数必须返回promise(启动的时候调用)
     */
    export async function bootstrap() {
      // console.log('[vue] vue app bootstraped');
    }
    // 从生命周期 mount 中获取通信方法,props默认会有onGlobalStateChange和setGlobalState两个api
    export async function mount(props) {
      // console.log('乾坤子应用容器加载完成,开始渲染 child',props)
      if (window.__POWERED_BY_QIANKUN__) {
        if(router.options.base !== props.baseName){
          const { container } = props;
          // 获取容器元素,用于后续操作或设置环境变量等
          let rootRoute = new Router({
            mode: 'history', // 去掉url中的#
            base: props.baseName,
            scrollBehavior: () => ({y: 0}),
            routes: constantRoutes
          })
    
          instance =  new Vue({
            router:rootRoute,
            store,
            render: h => h(App),
            beforeCreate(){
              if (window.__POWERED_BY_QIANKUN__) {
                store.state.user = props.sharedStore.state.user
              }
            }
          }).$mount(
            container
              ?
              container.querySelector('#app')  //渲染到主应用的入口
              :'#app' //独立运行的时候
          )
        } else {
          render(props);
        }
      } else {
        render(props);
      }
    }
    /**
     * 可选生命周期钩子,仅使用 loadMicroApp 方式加载微应用时生效
     */
    export async function update(props) {
    }
    
    export async function unmount() {
      instance.$destroy();
      instance.$el.innerHTML = '';
      instance = null;
    }
    
    export default instance;
    
    
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45
    • 46
    • 47
    • 48
    • 49
    • 50
    • 51
    • 52
    • 53
    • 54
    • 55
    • 56
    • 57
    • 58
    • 59
    • 60
    • 61
    • 62
    • 63
    • 64
    • 65
    • 66
    • 67
    • 68
    • 69
    • 70
    • 71
    • 72
    • 73
    • 74
    • 75
    • 76
    • 77
    • 78
    • 79
    • 80
    • 81
    • 82
    • 83
    • 84
    • 85
    • 86
    • 87
    • 88
    • 89
    • 90
    • 91
    • 92
    • 93
    • 94
    • 95
    • 96
    • 97
    • 98
    • 99
    • 100
    • 101
    • 102
    • 103
    • 104
    • 105
    • 106
    • 107
    • 108
    • 109
    • 110
    • 111
    • 112
    • 113
    • 114
    • 115
    • 116
    • 117
    • 118
    • 119
    • 120
    • 121
    • 122
    • 123
    • 124
    • 125
    • 126
    • 127
    • 128
    • 129
    • 130
    • 131
    • 132
    • 133
    • 134
    • 135
    • 136
    • 137
    • 138
    • 139
    • 140
    • 141
    • 142
    • 143
    • 144
    • 145
    • 146
    • 147
    • 148
    • 149
    • 150
    • 151
    • 152
    • 153
    • 154
    • 155
    • 156
    • 157
    • 158
    • 159
    • 160
    • 161
    • 162
    • 163
    • 164
    • 165
    • 166
    • 167
    • 168
    • 169
    • 170
    • 171
    • 172
    • 173
    • 174
    • 175
    • 176
    • 177
    • 178
    • 179
    • 180
    • 181
    • 182
    • 183
    • 184
    • 185
    • 186
    • 187
    • 188

    子应用配置其中主要代码:

    export async function mount(props) {
      // console.log('乾坤子应用容器加载完成,开始渲染 child',props)
      if (window.__POWERED_BY_QIANKUN__) {
        if(router.options.base !== props.baseName){
          const { container } = props;
          // 获取容器元素,用于后续操作或设置环境变量等
          let rootRoute = new Router({
            mode: 'history', // 去掉url中的#
            base: props.baseName,
            scrollBehavior: () => ({y: 0}),
            routes: constantRoutes
          })
    
          instance =  new Vue({
            router:rootRoute,
            store,
            render: h => h(App),
            beforeCreate(){
              if (window.__POWERED_BY_QIANKUN__) {
                store.state.user = props.sharedStore.state.user
              }
            }
          }).$mount(
            container
              ?
              container.querySelector('#app')  //渲染到主应用的入口
              :'#app' //独立运行的时候
          )
        } else {
          render(props);
        }
      } else {
        render(props);
      }
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35

    如果当前属于qiankun模块,则不执行render,自己手动创建vue router进行更改即可。

    子应用的vue.config.js

      publicPath: process.env.NODE_ENV === "production" ? "/" : "/system-module",
    
    • 1

    部署完成效果:

    vue+乾坤部署效果

  • 相关阅读:
    分享一下商城小程序怎么设置分销功能
    力扣算法题:34、在排序数组中查找元素的第一个和最后一个位置.java版
    TOWE雷达光敏感应开关,让生活更智能、更安全
    RabbitMQ 利用DelayExchange插件实现延迟队列
    [实时流基础 flink] 窗口
    stdlib.h
    openpnp - 程序发布包的制作
    根据店铺ID或店铺昵称或店铺链接获取阿里巴巴店铺所有商品数据接口|阿里巴巴店铺整店商品数据接口|阿里巴巴API接口
    [附源码]计算机毕业设计springboot疫情防控平台
    前端框架模板
  • 原文地址:https://blog.csdn.net/qq_42696432/article/details/133172593