Vue:关于微前端的整合

目录

  • 微前端是什么?
    • 背景
    • 核心价值
  • 目前微前端的几种解决方案
    • iframe
    • 阿里乾坤 qiankun (阿里 14.2k)
    • microApp (京东 4k)
    • 无界:(腾讯 2k)
    • 小结
  • microApp基础示例
    • 1. 基座应用
      • 1. 依赖版本
      • 2. main.js引入
      • 3. router/index.js路由设置
      • 4. 各页面的内容
        • 4.1 Layout.vue页面
        • 4.2 my-page.vue页面
        • 4.3 parent1.vue和parent2.vue页面
    • 2. 子应用
      • 1. router/index.js路由设置
      • 2. vue.config.js设置跨域支持
      • 3. main.js引入自定义public-path.js文件
      • 4. public-path.js文件
    • 3. 注意事项
  • 乾坤示例

微前端是什么?

背景

1、微前端架构旨在解决单体应用在一个相对长的时间跨度下,由于参与的人员、团队的增多、变迁,从一个普通应用演变成一个巨石应用(Frontend Monolith)后,随之而来的应用不可维护的问题。这类问题在企业级 Web 应用中尤其常见。
2、微前端是一种类似于微服务的架构,它将微服务的理念应用于浏览器端,即将 Web 应用由单一的单体应用转变为多个小型前端应用聚合为一的应用。各个前端应用还可以独立运行、独立开发、独立部署。

核心价值

1、不限制技术栈:主框架不限制接入应用的技术栈,微应用具备完全自主权
2、独立开发、独立部署:微应用仓库独立,前后端可独立开发,部署完成后主框架自动完成同步更新
3、增量升级(拓展):在面对各种复杂场景时,我们通常很难对一个已经存在的系统做全量的技术
4、升级或重构:而微前端是一种非常好的实施渐进式重构的手段和策略
5、独立运行:每个微应用之间状态隔离,运行时状态不共享

目前微前端的几种解决方案

iframe

优点:

1、最稳定的、上手难度最低的。
2、提供了浏览器原生的硬隔离方案,不论是样式隔离、js 隔离这类问题统统都能被完美解决

缺点:

1、url 不同步。浏览器刷新 iframe url 状态丢失、后退前进按钮无法使用。
2、加载是另一个window窗口,页面缩放时内部窗口内部缩放不同步,以及弹窗无法全局覆盖,和双滚动条问题;
3、通信复杂;要用postMessage,另外发送消息时需要在onload监听事件处理,会出现发送额外的噪声消息的情况只适合简单的页面渲染

阿里乾坤 qiankun (阿里 14.2k)

官网地址
基于 single-spa 的微前端实现库,通过监听 url change 事件,在路由变化时匹配到渲染的子应用并进行渲染,这个思路也是目前实现微前端的主流方式。

特点

1、基于 single-spa 封装,提供了更加开箱即用的 API。
2、技术栈无关,任意技术栈的应用均可 使用/接入,不论是React/Vue/Angular/JQuery 还是其他等框架。
3、HTML Entry 接入方式,让你接入微应用像使用 iframe 一样简单。
4、样式隔离,确保微应用之间样式互相不干扰。
5、JS 沙箱,确保微应用之间 全局变量/事件 不冲突。
6、资源预加载,在浏览器空闲时间预加载未打开的微应用资源,加速微应用打开速度。
7、umi 插件,提供了 @umijs/plugin-qiankun 供 umi 应用一键切换成微前端架构系统。

不足

1、 适配成本比较高,工程化、生命周期、静态资源路径、路由等都要做一系列的适配工作;
2、css 沙箱采用严格隔离会有各种问题,js 沙箱在某些场景下执行性能下降严重;
3、无法同时激活多个子应用,也不支持子应用保活;
4、无法支持 vite 等 esmodule 脚本运行;

microApp (京东 4k)

官网地址
micro-app是京东零售推出的一款微前端框架,它基于类WebComponent进行渲染,从组件化的思维实现微前端。

核心功能在CustomElement基础上进行构建,CustomElement用于创建自定义标签,并提供了元素的渲染、卸载、属性修改等钩子函数,我们通过钩子函数获知微应用的渲染时机,并将自定义标签作为容器,微应用的所有元素和样式作用域都无法逃离容器边界,从而形成一个封闭的环境。

特点

1、JS 沙箱。
2、样式隔离。
3、元素隔离。
4、预加载。
5、资源地址补全。
6、插件系统。
7、数据通信。
8、与技术栈无关
9、支持子应用保活

不足

1、接入成本较 qiankun 有所降低,但是路由依然存在依赖;
2、多应用激活后无法保持各子应用的路由状态,刷新后全部丢失;
3、css 沙箱依然无法绝对的隔离,js 沙箱做全局变量查找缓存,性能有所优化;
4、支持vite运行,但必须使用plugin改造子应用,且 js 代码没办法做沙箱隔离;
5、对于不支持 webcompnent 的浏览器没有做降级处理;

无界:(腾讯 2k)

官网地址
无界微前端方案基于 webcomponent 容器 + iframe 沙箱,能够完善的解决适配成本、样式隔离、运行性能、页面白屏、子应用通信、子应用保活、多应用激活、vite 框架支持、应用共享等用户的核心诉求。

小结

除了Iframe,乾坤microApp无界三个框架都可以实现JS 沙箱,样式隔离,数据通信的功能,并且任意技术栈的应用均可 使用/接入。

结合目前的实际工作情况(vue2,webpack),下面以vue进行demo测试JS 沙箱样式隔离数据通信(子应用保活)的功能。

microApp基础示例

1. 基座应用

1. 依赖版本

npm i @micro-zoe/micro-app --save

"@micro-zoe/micro-app": "^0.8.10",
"vue": "^2.6.11",

2. main.js引入

import Vue from 'vue'
import App from './App.vue'
import router from './router'
import store from './store'
// 引入element
import ElementUI from 'element-ui'
import 'element-ui/lib/theme-chalk/index.css'
// 引入microApp--start
import microApp from '@micro-zoe/micro-app'
microApp.start()
// 引入microApp--end

Vue.config.productionTip = false

Vue.use(ElementUI)
new Vue({
  router,
  store,
  render: h => h(App)
}).$mount('#app')

3. router/index.js路由设置

import Vue from 'vue'
import VueRouter from 'vue-router'

import Layout from '../views/Layout.vue'
import MyPage from '../views/my-page.vue'
import Parent1 from '../views/parent1.vue'
import Parent2 from '../views/parent2.vue'

Vue.use(VueRouter)

const routes = [
  {
    path: '/',
    redirect: '/layout'
  },
  {
    path: '/layout', // 基座项目的父路由
    name: 'layout',
    component: Layout,
    redirect: '/layout/parent1',
    children: [
      {
        path: 'parent1', // 基座项目的子路由1
        name: 'parent1',
        component: Parent1
      },
      {
        path: 'parent2', // 基座项目的子路由2
        name: 'parent2',
        component: Parent2
      },
      {
      	//  非严格匹配,/layout/my-page/* 都指向 MyPage 页面
        path: 'my-page/:page*', // 微服务的路由
        name: 'my-page',
        component: MyPage
      }
    ]
  }
  // {
  //   path: '/my-page/:page*',
  //   name: 'my-page',
  //   component: MyPage
  // }
]

const router = new VueRouter({
  mode: 'history',
  base: process.env.BASE_URL,
  routes
})

export default router

4. 各页面的内容

4.1 Layout.vue页面

<template>
  <div>
    <el-menu
      :default-active="activeIndex"
      class="el-menu-demo"
      mode="horizontal"
      @select="handleSelect"
    >
      <el-menu-item index="/layout/parent1">parent路由1el-menu-item>
      <el-menu-item index="/layout/parent2">parent路由2el-menu-item>
      <el-menu-item index="/layout/my-page/">children1-vue路由el-menu-item>
    el-menu>
    <router-view>router-view>
  div>
template>

<script>
export default {
  name: 'layout',
  data () {
    return {
      activeIndex: '/layout/parent1'
    }
  },
  methods: {
    handleSelect (key, keyPath) {
      this.$router.push({ path: key })
    }
  },
  created () {
    // 刷新路由保持当前状态
    this.activeIndex = this.$route.path
  }
}
script>

<style scoped>
style>

4.2 my-page.vue页面

<template>
  <div>
    <h1>父应用页面----子应用h1>
    
    <micro-app
      name="app1"
      url="http://localhost:3000/"
      baseroute="/my-page"
    >micro-app>
  div>
template>

<script>
export default {
  name: 'my-page'
}
script>

<style scoped>
style>

4.3 parent1.vue和parent2.vue页面

<template>
  <div>parent【1】div>
template>

<script>
export default {
  name: 'parent1'
}
script>

<style scoped>
style>

2. 子应用

1. router/index.js路由设置

import Vue from 'vue'
import VueRouter from 'vue-router'
import Home from '../views/Home.vue'

Vue.use(VueRouter)

const routes = [
  {
    path: '/',
    name: 'Home',
    component: Home
  },
  {
    path: '/about',
    name: 'About',
    // route level code-splitting
    // this generates a separate chunk (about.[hash].js) for this route
    // which is lazy-loaded when the route is visited.
    component: () => import(/* webpackChunkName: "about" */ '../views/About.vue')
  }
]

const router = new VueRouter({
  mode: 'history',
  //  __MICRO_APP_BASE_ROUTE__ 为micro-app传入的基础路由.子应用可以通过window.__MICRO_APP_BASE_ROUTE__获取基座下发的baseroute
  base: window.__MICRO_APP_BASE_ROUTE__ || process.env.BASE_URL,
  routes
})

export default router

2. vue.config.js设置跨域支持

module.exports = {
  devServer: {
    port: 3000,
    headers: {
      'Access-Control-Allow-Origin': '*'
    }
  }
}

3. main.js引入自定义public-path.js文件

// main.js
import './public-path'
import Vue from 'vue'
import App from './App.vue'
import router from './router'

Vue.config.productionTip = false

const app = new Vue({
  router,
  render: (h) => h(App)
}).$mount('#app')

// 监听卸载操作
window.addEventListener('unmount', function () {
  app.$destroy()
})

4. public-path.js文件

// public-path.js
// __MICRO_APP_ENVIRONMENT__和__MICRO_APP_PUBLIC_PATH__是由micro-app注入的全局变量
if (window.__MICRO_APP_ENVIRONMENT__) {
  // eslint-disable-next-line
  __webpack_public_path__ = window.__MICRO_APP_PUBLIC_PATH__
}

3. 注意事项

  1. name(my-page.vue)必须以字母开头,且不可以带有除中划线和下划线外的特殊符号
  2. url只是html地址,子应用的页面渲染还是基于浏览器地址的
  3. baseroute的作用请查看官网路由配置
  4. 子应用必须支持跨域访问,跨域配置参考这里
  5. 官网地址

乾坤示例

参考链接:https://blog.csdn.net/weixin_43193877/article/details/125717755

你可能感兴趣的:(Vue,前端,vue.js,javascript)