qiankun微前端

微前端
微前端是指存在于浏览器中的微服务,其借鉴了后端微服务的架构理念,将微服务的概念扩展到了前端。
如果对微服务的概念比较陌生的话,可以简单的理解为微前端就是将一个大型的前端应用拆分成多个模块,每个前端模块可以由不同的团队进行管理,并可以自主选择框架,并且有自己的仓库,可以独立部署上线。

一般呢,微前端多应用于企业中的中后台项目中,因为企业内部的中后台项目存活时间都比较长,动辄三五年或者更久,最后演变成一个巨石应用的概率往往高于其他类型的web应用。这就带来了技术栈落后,编译部署慢两个问题。

微前端正好解决了技术落后的问题,因为项目子应用对技术栈的要求不做限制,你可以用react,vue,angular等等技术子应用分为不同的仓库代码管理;
当开发到一定地步会导致项目变得很大,所以子应用拆分开部署,让基座去做一个类似于iframe的映射只需把基座代码和子应用代码分开部署各不响应性;
解决了部署编译慢的问题

微前端的优点
1.子应用独立开发部署;
2.兼容技术选型广泛;
3.子应用技术选型多样化;
4.Css样式沙箱隔离,子应用间css样式不会互相影响;

基座的概念:
基座就是所有子应用当中最大的父应用,就相当于我们的路由,子应用就相当于路由出口routerview,子路由可以用任何技术栈去写,并且单独部署;

现有的微前端方案
Iframe大家都很熟悉,通过iframe实现的话就是每个子应用通过iframe标签来嵌入到父应用中,iframe具有天然的隔离,各个子应用之间以及子应用和父应用之间都可以做到互相不影响;

但是iframe也有很多的缺点:
1.如果刷新页面,iframe中子应用的路由会丢失;
【比如我们嵌套antdUI框架,我们刚进入是首页,当我们切换到组件的路有时候,我们刷新父应用的页面,你会发现,iframe回到了antd的首页,而不是在组件库的路由上;】

2.全局上下文完全隔离,内存变量不共享;
就是父应用有一个window对象,子应用也有一个自己的window对象,这两个是不互通的,需要通过一些传值方法才可以互通;
3.UI不同步,比如iframe中的页面如果带有遮罩层的弹窗组件,则遮罩就不能覆盖整个浏览器,只能在iframe中生效;
4.Iframe会阻塞页面加载,每次子应用进入都是一次浏览器上下文重建,资源重新加载的过程;
比如我们在父应用里使用了iframe,那么加载整个页面的时候iframe内部的网页是重新加载的过程;
每次刷新都会重新建立iframe的资源;

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

新的微前端框架是qiankun
qiankun是阿里开源的一个微前端框架,在阿里内部已经经过一批线上应用的充分检验打磨的,所以可以放心使用。
https://qiankun.umijs.org/zh
qiankun的优势
qiankun是基于single-spa封装的,提供了一个开箱即用的API;
0.技术栈无关,任意技术栈的应用均可使用/接入qiankun,不论是react/vue/angular/jquery还是其他框架;
1.html entry的方式接入,像使用iframe一样简单;
2.实现了single-spa不具备的样式隔离和js隔离【js沙箱隔离】,子应用间js和css会隔离【css的影子隔离,影子树隔离Shadow DOM】;
3.资源预加载,在浏览器空闲时间预加载未打开的微应用资源【子应用】,加速微应用的打开速度;
qiankun
基座(主应用):主要负责集成所有的子应用,提供一个入口能够访问你所需要的子应用,基座工程可以写复杂的业务代码逻辑,不过一般不建议那样做;

子应用:根据不同业务划分的模块,每个子应用都打包成umd模式的形式供基座(主应用)来加载;

qiankun中公共依赖包优化
比如基座用到了antd或者其他插件lodash,子应用也用到了这两个依赖,那么qiankun可以做一定的配置不会重复打包基座和子应用依赖。

基座配置后,会把依赖包打包出来以一个链接的方式引用到基座中,而子应用可以通过一个ignore属性去配置忽视掉基座已经打包的依赖,优化子应用不重复加载已经被打包加载过的依赖或插件;

qiankun切点
qiankun只做了子应用之间的样式隔离,并没有做基座和子应用的样式隔离
解决方案就是
1.基座样式类名带上基座名称
比如基座a ,className=‘a-css’
子应用 b ,className=‘b -css’
这样就不会出现样式冲突,通过前缀去解决

2.通过css module
在使用css_module时,类名是动态生成的,唯一的,并准确对应到源文件中的各个类的样式。 
类名有一个hash值,不会让基座和子应用的样式出现覆盖

qiankun子应用之间的跳转可以用

React搭建基座
然后安装qiankun
$ yarn add qiankun # or npm i qiankun -S

import { start, registerMicroApps } from ‘qiankun’;

const app = [
    {
        name: ‘qiankunvue3’,//name表示子应用的名称你可以在子应用的package文件去找到应用的名称
        entry: ‘//localhost:20000’,//基座找到这个域名端口下的html并且解析对应的css和js
        container: ‘#container’,//就是加载的容器
        activeRule: ‘/vue3’,//基座中你点击路由切换,如果匹配到activeRule中的这个路径就会去加载name属性下和entry对应匹配的子应用
    }, {
        name: ‘qiankunreact’,
        entry: ‘//localhost:30000’,
        container: ‘#container’,
        activeRule: ‘/react’,
    },
    {
        name: ‘qiankunvue2’,
        entry: ‘//localhost:8080’,
        container: ‘#container’,
        activeRule: ‘/vue2’,
    },
];

//registerMicroApps注册子应用
registerMicroApps(
    app,//这边是我们上面配置的路由
    {
        beforeLoad: (app) => {
            console.log(‘before load_加载前’, app.name)
        },
        beforeMount: (app) => {
            console.log(‘before mount_挂载后’, app.name)
        },
        afterMount: (app) => {
            console.log(‘afterMount_挂载后’, app.name);
        },
        beforeUnmount: (app) => {
            console.log(‘beforeUnmount_卸载前’, app.name);
        },
        afterUnmount: (app) => {
            console.log(‘afterUnmount_卸载前’, app.name);
        },
    },
);

//启动前端qiankun微服务
start({
    sandbox: {
        experimentalStyleIsolation: true,//设置样式隔离
    }
});

子应用React项目的搭建
rescripts/cli这个插件可以用来修改react项目的webpack配置
npm i -D @rescripts/cli

然后我们把子应用React项目中的package文件
scripts配置改成如下↓
“scripts”: {
    “start”: “rescripts start”,
    “build”: “rescripts build”,
    “test”: “rescripts test”,
    “eject”: “rescripts eject”
  },
在和src同级目录创建.env文件然后写下如下代码端口号切记要和你在基座中配置的子应用端口号一直
PORT=30000
WDS_SOCKET_PORT=30000

然后我们在项目中和src同级别目录下创建.rescriptsrc.js文件
module.exports = {
    webpack: (config) => {
        config.output.library = ‘qiankunreact’;//这个是你在基座上配置的name属性要和你子应用中package.json文件中的name属性值一样
        config.output.libraryTarget = ‘umd’;
        config.output.publicPath = ‘http://localhost:30000/’;//设置成window.__INJECTED_PUBLIC_PATH_BY_QIANKUN__的值
        return config;
    },
    devServer: (config) => {
        config.headers = {
            ‘Access-Control-Allow-Origin’: ‘*’,
        }
        return config;
    }
}

配置vue2项目的子应用
在main.js中配置如下
import Vue from ‘vue’
import App from ‘./App.vue’

Vue.config.productionTip = false

function render(props = {}) {
  const { container } = props;
  new Vue({
    render: (h) => h(App),
  }).$mount(container ? container.querySelector(‘#app’) : ‘#app’);
}

// 独立运行时
if (!window.POWERED_BY_QIANKUN) {
  render();
}

// 生命周期
export async function bootstrap() {
  console.log(‘[vue] vue app bootstraped’);
}
export async function mount(props) {
  console.log(‘[vue] props from main framework’, props);
  // 通过主应用引入时调用
  render(props);
}
export async function unmount() {

}

然后在vue.config.js下配置如下
const { defineConfig } = require(‘@vue/cli-service’)

module.exports = defineConfig({
  transpileDependencies: true,
  publicPath: ‘http://localhost:8080/’,//保证子应用静态资源都是像8080端口上发送的,这里填写的是window.INJECTED_PUBLIC_PATH_BY_QIANKUN
  devServer: {
    port: 8080,//fetch
    headers: {
      ‘Access-Control-Allow-Origin’: ‘*’,
    }
  },
  configureWebpack: {//需要获取我打包的内容,systemjs → umd格式
    output: {
      libraryTarget: ‘umd’,
      library: ‘qiankunvue2’,
    }
  }
})

在vue3当中配置子应用
然后在vue.config.js下配置如下

const { defineConfig } = require(‘@vue/cli-service’)
module.exports = defineConfig({
  transpileDependencies: true,
  publicPath: ‘http://localhost:20000/’,//保证子应用静态资源都是像20000端口上发送的
  devServer: {
    port: 20000,//fetch
    headers: {
      ‘Access-Control-Allow-Origin’: ‘*’,
    }
  },
  configureWebpack: {//需要获取我打包的内容,systemjs → umd格式
    output: {
      libraryTarget: ‘umd’,
      library: ‘qiankunvue3’,
    }
  }
})

main.js作如下配置
// import { createApp } from ‘vue’
// import App from ‘./App.vue’

// createApp(App).mount(‘#app’)

import { createApp } from ‘vue’;
import App from ‘./App.vue’;
// import routes from ‘./router/index’;
// import { createRouter, createWebHistory } from ‘vue-router’;
//不能直接挂载,需要切换的时候,调用mount方法时候再去挂载
let app;

function render(props = {}) {
    app = createApp(App);
    const { container } = props;
    app.use().mount(container ? container.querySelector(‘#app’) : ‘#app’);
}
console.log(‘window.POWERED_BY_QIANKUN’, window.POWERED_BY_QIANKUN);
//乾坤在渲染前,给我提供了,一个变量,window.POWERED_BY_QIANKUN
if (!window.POWERED_BY_QIANKUN) {
    render();
}

export async function bootstrap() {
    console.log(‘vue3_bootstrap’);
}

export async function mount(props) {
    console.log(‘vue3_mount_props’, props);
    render(props);
}

export async function unmount() {
    console.log(‘vue3_unmount’);
}

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值