基础框架-文档中心基础框架-文档中心
使用指南
公共组件
开发测试
  • 微服务框架
  • Vue3框架
  • 项目实践
更新日志
  • V3.3.0
  • V3.2.6
  • V3.2.5
  • V3.2.4
  • V3.1.0
  • V2.2.x
  • V2.1.0
  • V2.0.0
  • V1.2.1
  • V1.1.1
使用指南
公共组件
开发测试
  • 微服务框架
  • Vue3框架
  • 项目实践
更新日志
  • V3.3.0
  • V3.2.6
  • V3.2.5
  • V3.2.4
  • V3.1.0
  • V2.2.x
  • V2.1.0
  • V2.0.0
  • V1.2.1
  • V1.1.1
  • 后端组件

    • waf-license-产品证书授权
    • waf-calcite-动态数据管理
    • 消息中心组件
    • 调度任务组件
    • xxl-job 安装使用
    • API服务
    • 全文检索
  • 前端组件

    • 分页组件
    • 数据字典组件
    • 业务字典组件
    • 人员选择组件
    • 组织机构选择组件
    • 文件上传组件
    • 第三方应用集成

第三方应用集成

第三方应用集成主要分为两种,一种是基于自身框架,能够把控源码的,可以采用qiankun框架进行微前端的集成,还有一种是采用IFrame方式集成。

1、微前端集成

在前端应用进行多模板拆分后,最终集成在一个框架中运行时,这里采用qiankun进行集成,建议>=waf-parent:2.2.0版本,之前版本只是做为集成,没有具体项目进行实践。

qiankun 是一个基于 single-spa 的微前端实现库,旨在帮助大家能更简单、无痛的构建一个生产可用微前端架构系统。

1.1、菜单配置

基于qiankun集成的子应用,菜单配置需要注意组件的地址:layouts/QiankunLayout,路由地址需要根据qiankun注册时activeRule规则来配置,这里的路由地址一般就采用子应用的路由 base+path的方式配置。

微应用系统: 从数据字典MicroApp(微应用)中获取,微应用的编码也在该字典项配置

微应用组件: 组件的路径地址(vue文件在子应用的路径)

数据字典微应用添加需要集成的微应用编码,如: waf-micro-web (微应用示例)编码需要与应用是setting.js中microAppCode值保持一致

1683942456940

菜单配置:

访问地址:/waf-micro-web/microDemo (这里需要添加微应用的上下文/waf-micro-web,后面自定义不重复就是)

微应用系统:选择数据字典配置的微应用

微应用组件:admin/SystemLog (微应用中views目录下的文件路径)

1683942375296

这里假设两个前端应用:waf-web(主应用)、waf-micro-web(微应用)应用注册微应用配置

waf-web(主应用)

waf-micro-web(微应用)

1.2、waf-web(主应用)改造

1.2.1、env环境变量配置

# 1、.env.development (测试环境配置全路径)
# 主数据-qiankun应用入口
VUE_APP_QIANKUN_MDMWEB_ENTRY = '//localhost:8020/waf-micro-web/'

# 2、.env.production (正式环境只配置应用上下文,host由window.location.host获取)
# 主数据-qiankun应用入口
VUE_APP_QIANKUN_MDMWEB_ENTRY = '/waf-micro-web/'

1.2.2、AppMain.vue 配置

文件路径:src/components/layouts/components/AppMain.vue

// src/components/layouts/components/AppMain.vue
import { registerMicroApps, start } from 'qiankun'
import { getMicroApps } from '@/utils'

mounted: function() {
    // 注册微应用(需要集成的模块)
    registerMicroApps(getMicroApps(), {
        beforeLoad: (app) => {
            this.loading = true
        },
        afterMount: (app) => {
            this.loading = false
        }
    })
    // 禁用预加载   strictStyleIsolation: false  去掉严格模式
    start({ prefetch: false })
}

// utils/index.js
export function getMicroApps() {
  const microApps = [
    {
        name: 'waf-micro-web',
        entry: buildEntry(process.env.VUE_APP_QIANKUN_APIWEB_ENTRY),
        container: '#subapp-container',
        activeRule: '/waf-web/waf-micro-web/',
        props: { store: store }
    }
  ]
  return microApps
}

function buildEntry(appUrl) {
  if (process.env.NODE_ENV === 'development') {
    return appUrl
  } else {
    return `//${window.location.host}${appUrl}`
  }
}

1.2.3、vue.config.js配置

**注意:**这里只注册微应用的后端代理,前端不要进行代理,否则微应用的刷新会被proxy代理

proxy: {
  '/waf-framework/': {
    // 目标 API 地址
    target: 'http://localhost:8081/',
    // target: 'http://d.wiseda.cn/',
    // 如果要代理 websockets
    ws: true,
    // 将主机标头的原点更改为目标URL
    changeOrigin: true // 是否跨域
  },
  '/xxx-server/': {
    // 目标 API 地址
    target: 'http://localhost:8850/',
    // 如果要代理 websockets
    ws: true,
    // 将主机标头的原点更改为目标URL
    changeOrigin: true // 是否跨域
  }
}

1.3、waf-micro-web(微应用)改造

微应用的配置相对于主应用要复杂一些,主要是暴露钩子函数,生成新的路由。

1.3.1、utils\public-path.js

新增文件,用于注入qiankun路径

if (window.__POWERED_BY_QIANKUN__) {
  // eslint-disable-next-line no-undef
  __webpack_public_path__ = window.__INJECTED_PUBLIC_PATH_BY_QIANKUN__
}

1.3.2、.env

qiankun集成时,路由的base属性

# qiankun 微应用集成地址
VUE_APP_QIANKUN_PUBLICPATH = '/waf-web/waf-micro-web/'

# 超时登录地址(由主应用进行重新登录)
VUE_APP_LOGIN_URL='/waf-web/'

1.3.3、utils\request.js

子应用超时后,在生产环境,统一转向主应用处理

// 延迟2秒后跳转到登录页面
 setTimeout(() => {
  store.dispatch('user/resetToken').then(() => {
  // 生产环境,统一由配置地址进行登录
     if (process.env.NODE_ENV === 'production') {
       window.location.href = process.env.VUE_APP_LOGIN_URL
      } else {
       router.push(`/login`).catch(err => {err})
 }

1.3.4、router\index.js

修改base代码行,window.__POWERED_BY_QIANKUN__如果为true,则采用qiankun的路径,否则采用应用配置的路径(用于单独登录)

const createRouter = () => new Router({
  base: window.__POWERED_BY_QIANKUN__ ? process.env.VUE_APP_QIANKUN_PUBLICPATH : process.env.VUE_APP_PUBLICPATH,
  mode: 'history', // require service support
  scrollBehavior: () => ({ y: 0 }),
  routes: constantRoutes
})

1.3.5、store\modules\user.js

在actions中添加loadUserInfo,用于qiankun调用mount(props)钩子函数时,初始化用户信息

const actions = {
  // qiankun微应用集成初始化用户
  loadUserInfo({ commit }, storeData) {
    return new Promise((resolve) => {
      commit('SET_USER_INFO', storeData.userInfo)
      commit('SET_TOKEN', storeData.token)
      resolve()
    })
  },
......
}    

1.3.6、permission.js

在router.beforeEach中添加if条件判断,如果是qiankun调用,直接next(),权限路由将在mount中处理

router.beforeEach(async(to, from, next) => {
  // start progress bar
  NProgress.start()
  if (window.__POWERED_BY_QIANKUN__) {
    next()
  } else {
......
}

1.3.7、settings.js

在setting文件中添加microAppCode属性,指定当前微应用编码,与数据字典微应用配置的数据项保持一致

注意:

  • ls的前端统一改为waf. (如果其他项目,只需要保持主应用,各子应用一致就可以了,这样token可以在同域下传递)

  • 该编码与菜单配置中指定的微应用系统保持一致。

// ls的前端统一改为`dmm.`
storageOptions: {
  namespace: 'waf.'
    
/**
* 微应用编码(用于qiankun集成的过滤出本系统的菜单生成路由)
*/
microAppCode: 'waf-micro-web'

1.3.8、main.js

在import中删除babel-polyfill 引用 import 'babel-polyfill',该引用在主应用中已经引用,多次引用会报错

导出bootstrap() 、mount(props) 、unmount()三个钩子函数

import '@/utils/public-path'
import router, { resetRouter } from './router'
import { microAppCode } from '@/settings'
// 删除babel-polyfill 引用

let instance = null

function render(props = {}) {
  const { container } = props
  instance = new Vue({
    router,
    store,
    render: h => h(App)
  }).$mount(container ? container.querySelector('#app') : '#app')
}

// 如果是独立运行window.__POWERED_BY_QIANKUN__=undefined
if (!window.__POWERED_BY_QIANKUN__) {
  render()
}

// 应用启动执行一次
export async function bootstrap() {
}

export async function mount(props) {
  const storeData = props.store.getters
  // 加载用户信息
    store.dispatch('user/loadUserInfo', storeData).then(() => {
        const routers = processRouter(storeData.userInfo.permissions, [])
        router.addRoutes(routers)
    render(props)
  })
}

function processRouter(permissions, routers) {
    if (!permissions) {
    return
  }
  for (const item of permissions) {
    // 未指定组件,采用BlankLayout
    let component = '' // 录入的文件路径
    // 菜单属性当前系统
    if (item.microApp && microAppCode === item.microApp) {
      component = item.microAppComponent.trim()
      let menuPath = item.url || `/${item.id}`
      // 将带有应用上下文的路径替换为/,上下文路由路由的base指定
      menuPath = menuPath.replace(process.env.VUE_APP_PUBLICPATH, '/')
      const menu = {
        path: menuPath,
        name: item.perms,
        component: resolve => require(['@/views/' + component + '.vue'], resolve),
        hidden: !item.isVisible,
        meta: {
          title: item.name
        }
      }
      routers.push(menu)
    }
      if (item.children && item.children.length > 0) {
          processRouter(item.children, routers)
      }
  }
  return routers
}

export async function unmount() {
  instance.$destroy()
  instance = null
  resetRouter()
}

1.3.9、vue.config.js

const { name } = require('./package')
const tmpTime = new Date().getTime()
// 子应用打包格式
configureWebpack: {
    output: {
      // 把子应用打包成 umd 库格式
      library: `${name}-[name]`,
      libraryTarget: 'umd',
      jsonpFunction: `webpackJsonp_${name}`,
      // 输出重构  打包编译后的 文件名称  【模块名称.时间戳】
      filename: `static/js/[name].${tmpTime}.js`,
      chunkFilename: `static/js/[name].${tmpTime}.js`
    }
},
......    
// 字体打包    
chainWebpack: config => {
	config.module
	  .rule('fonts')
	  .test(/.(ttf|otf|eot|woff|woff2)$/)
	  .use('url-loader')
	  .loader('url-loader')
	  .tap(options => {
	    options = {
	      // limit: 10000,
	      name: '/static/fonts/[name].[ext]'
	    }
	    return options
	  })
	  .end()    
......    
// 添加headers,让应用可以跨域调用
devServer: {
 headers: {
   'Access-Control-Allow-Origin': '*'
 },    

1.3.10、测试验证

1683944866750

2,微应用主题色设置

在主应用采用自定义的主题色后,微应用集成加载后,可能会出现主题色还原到默认主题,这里通过加载微应用后,再设置一下使用的主题色。

2.1.定义主题颜色

// styles/element-variables.less
@--color-primary: #0070D2;
:export {
  theme: @--color-primary;
}

2.2.设置主题色

调用一次setTheme(variables.theme)

//  main.js
import { setTheme } from '@/utils/theme'
import variables from '@/styles/element-variables.less'
export async function mount(props) {
 	......
  // 加载用户信息
  store.dispatch('user/loadUserInfo', storeData).then(() => {
    const routers = processRouter(storeData.userInfo.permissions, [])
    if (process.env.NODE_ENV !== 'production') {
      console.info('routers:', routers)
    }
    setTheme(variables.theme)
    router.addRoutes(routers)
    render(props)
  })
}

3、 IFrame集成

iframe集成是最常规的做法,直接嵌入一个地址,传递相应的参数即可,在基本框架平台中,也支持IFrame的方式集成。

  • 菜单配置

iframe集成菜单配置只需要注意组件的地址:layouts/IframeLayout,路由地址可以是相对地址,也可以是绝对地址,在集成的地址会默认带一个token参数,值为当前登录的用户token

1594368841631

  • 集成建议

对于基于基础框架开发的应用,如果有多个子应用,建立采用微前端-qiankun的集成方式,目前不兼容IE,如必需在IE下运行还是采用老套路,通过IFrame来集成。

上次更新: 5/13/23, 2:32 AM
编辑者: 李贤伟
Prev
文件上传组件