qiankun:微前端-乾坤(vue2为例)

qiankun官方文档地址:https://qiankun.umijs.org/zh

一、主应用

1、安装qiankun

yarn add qiankun   # yarn安装
npm i qiankun -S   # npm安装

2、注册/加载微应用

(1)方式一:基于路由配置

  • 适用于 route-based 场景。

  • 通过将微应用关联到一些 url 规则的方式,实现当浏览器 url 发生变化时,自动加载相应的微应用的功能。

  • main.js 中注册微应用(全局注册)

import { registerMicroApps, start } from 'qiankun';

registerMicroApps([
  {
    name: 'appName1', // 注册的微应用名称,必须唯一
    entry: '//localhost:7100',  // 微应用入口
    container: '#yourContainer',  // 微应用的容器节点,即微应用植入的位置
    activeRule: '/yourActiveRule', // 微应用的激活规则,路由匹配此规则表示当前微应用会被激活。支持字符串、字符串数组、function
  },
  {
    name: 'appName2',
    entry: { scripts: ['//localhost:7100/main.js'] },
    container: '#yourContainer2',
    activeRule: '/yourActiveRule2',
  },
]);

start();

(2)方式二:手动加载

  • 适用于需要手动 加载/卸载 一个微应用的场景。

    import { loadMicroApp } from 'qiankun'
    
    handleLoadMicroApp(data) {
        const baseUserId = this.baseUserId
        const path = data.path
        // 卸载微应用
        this.microApp && this.microApp.unmount()
        // 手动加载微应用
        this.microApp = loadMicroApp({
            name: data.id, // 必选,微应用的名称,微应用之间必须确保唯一
            entry: data.url, // 必选,微应用的入口
            container: '#content', // 必选,微应用的容器节点的选择器或者 Element 实例
            props: {   // 可选,初始化时需要传递给微应用的数据
                path: path  // 路径参数,唯一
                baseUserId: baseUserId, 
            },
        }) 
    }
    

3、router/index.js 路由配置

主应用基路由一定要与微应用的基路由保持一致

  {
    path: '/personalCenter',  // personalCenter为例
    name: 'personalCenter',
    component: () => import('@/views/personalCenter/index'),
    meta: {
      title: '个人中心',
    },
  },
  {
    path: '/personalCenter/*',  // personalCenter为基路由。*表示通配,这个一定要配置
    name: 'personalCenter',
    component: () => import('@/views/personalCenter/index'),
    meta: {
      title: '个人中心',
    },
  },

二、微应用

1、mian.js配置

// 判断是否是qiankun环境,一定要写在最上面
if (window.__POWERED_BY_QIANKUN__) {
  __webpack_public_path__ = window.__INJECTED_PUBLIC_PATH_BY_QIANKUN__
}

import router from './router'

// 设置全局变量,用于保存或销毁Vue实例
let instance = null 

//不是在qiankun环境下的话,独立运行
if (!window.__POWERED_BY_QIANKUN__) {
  render()
}

function render(props) {
  const { container } = props || {}
  instance = new Vue({
    router,
    store,
    render: h => h(App),
  }).$mount(container ? container.querySelector('#app') : '#app') // 用于限定当前上下文下的#app,防止与主应用中的#app冲突
}

// 导出相应的生命周期钩子
/**
 * bootstrap 只会在微应用初始化的时候调用一次,下次微应用重新进入时会直接调用 mount 钩子,不会再重复触发 bootstrap。
 * 通常我们可以在这里做一些全局变量的初始化,比如不会在 unmount 阶段被销毁的应用级别的缓存等。
 */
export async function bootstrap() {
  console.log('qiankun-bootstrap')
}

/**
 * 应用每次进入都会调用 mount 方法,通常我们在这里触发应用的渲染方法
 */
export async function mount(props) {
  console.log('qiankun-mount参数', props)
  render(props)  // 从qiankun启动
  // ReactDOM.render(, props.container ? props.container.querySelector('#root') : document.getElementById('root'))
}

/**
 * 应用每次 切出/卸载 会调用的方法,通常在这里我们会卸载微应用的应用实例
 */
export async function unmount(props) {
  instance.$destroy() // 销毁子应用实例
  // ReactDOM.unmountComponentAtNode(props.container ? props.container.querySelector('#root') : document.getElementById('root'))
}

/**
 * 可选生命周期钩子,仅使用 loadMicroApp 方式加载微应用时生效
 */
export async function update(props) {
  console.log('update props', props)
  render(props)  // 从qiankun启动
}

2、路由配置

(1)路由模式为history模式时

import VueRouter from 'vue-router'
import routes from './router';

const router = new VueRouter({
    base: window.__POWERED_BY_QIANKUN__ ? '/personalCenter/' : '/',   // qiankun环境配置基路由personalCenter
    mode: 'history',  // 访问路径不带#号
    routes,
});

(2)路由模式为hash模式时

  • component在qiankun环境和非乾坤环境不同时,可直接写死路由:

    export const routes = [ 
        {
            path: '/personalCenter/basic',  // 一定要有基路由personalCenter,且一定要有非基路由
            component: () => import('@/views/setting/basic'),
        },
        {
            path: '/personalCenter/account',
            component: () => import('@/views/setting/account'),
        },
        {
            path: '/personalCenter/third',
            component: () => import('@/views/setting/third'),
        },
    ]
    
  • component在qiankun环境和非乾坤环境相同时,可动态配置路由:

const base = window.__POWERED_BY_QIANKUN__ ? '/personalCenter' : ''  // qiankun环境配置基路由personalCenter

export const routes = [ 
    {
        path: `${base}/basic`,  // qiankun环境和非qiankun环境,页面都是同一个component不变
        component: () => import('@/views/setting/basic'),
    },
    {
        path: `${base}/account`,  
        component: () => import('@/views/setting/account'),
    },
    {
        path: `${base}/third`,
        component: () => import('@/views/setting/third'),
    },
]

3、路由跳转

const base = window.__POWERED_BY_QIANKUN__ ? '/personalCenter' : '' // hash模式下,路由跳转需要带基路由
this.$router.push(`${base}/basic`)

4、路由守卫

  • 配置了路由守卫router.beforeEach(async (to, from, next) => {}且重写了浏览器tab标签的图标标题时,接入主应用的路由不重写浏览器的图标和标题
  • 路由跳转不需要验证即可访问的,可能需要在路由白名单里添加微应用路径path
/*
* permission.js 文件中
*/

import router from './router'
import changeFavicon from '@/utils/get-page-title'

// 路由白名单
const whiteList = [
  '/personalCenter/basic',
] 

const hasToken = getToken()

// 路由守卫
router.beforeEach(async (to, from, next) => {
    if(to.path.indexOf('personalCenter') === -1) {
        changeFavicon(to.meta.title) // 不是微应用才修改网站图标和标题
    }
    if (whiteList.indexOf(to.matched[0].path) !== -1 || hasToken) {
        next()  // 添加白名单直接进、登录了直接进
    }
}

5、vue.config.js 配置:跨域、打包

const packageName = require('./package.json').name  // 导入packageName

module.exports = {
    devServer: {
        headers: {
            'Access-Control-Allow-Origin': '*', // 允许跨域,允许所有域名的脚本访问该资源
        },
    },
    // 配置打包,一定要配置,不然会报错
    output: {
        library: `${packageName}-[name]`,
        libraryTarget: 'umd',
        jsonpFunction: `webpackJsonp_${packageName}`,
    },
}

三、样式隔离

参考地址:https://juejin.cn/post/6992944363798003743

注:上述文档中有写,qiankun自带的沙箱样式隔离存在诸多问题

故主应用目前未开启自带的样式隔离

不过qiankun默认情况切换应用时,会采用动态样式表,子应用之间不会样式污染

解决方案:如子应用未影响主应用页面样式,可不配置插件

使用postcss插件给各自的项目class全局加上一个各自的命名空间

1.添加依赖

 npm i postcss-plugin-namespace -D

2.配置postcss

module.exports = (ctx) => {
  return {
    plugins: [
      require('postcss-plugin-namespace')('#lee_project', {
        ignore: [
          'html', /body/, 'span', 'el-form-item'
        ]
      }),
    ]
  }
}

该插件会将全局所有class前加上统一前缀,并过滤掉ignore内的标签;ignore内可以写字符串,可以写正则表达式。但每次编译前都会运行,所以可能会增加编译时间,所以日常开发环境下可以将此文件名随便改成别的,上线前记得改回来调试一下(如果直接隐藏掉代码的话,只要有postcss.config.js这个文件webpack会自动帮你执行,并且会提示你的postcss啥也没干,也相当于每次都走了这个脚本)。

注意:如果用/body/这样的正则,会将所有带body的class都过滤掉,比如el-drawer__bodyel-dialog__body等。

https://juejin.cn/post/6888695499793268744

解决方案:这里我们可以采用一定的编程约束:

  • 尽量不要使用可能冲突全局的 class 或者直接为标签定义样式;
  • 定义唯一的 class 前缀,现在的项目都是用诸如 antd 这样的组件库,这类组件库都支持自定义组件 class 前缀;
  • 主应用一定要有自定义的 class 前缀;

你可能感兴趣的:(未分类,前端,javascript,webpack)