热门标签 | HotTags
当前位置:  开发笔记 > 编程语言 > 正文

微前端学习笔记

一、为什么要学习微前端什么是微前端微前端就是将不同的功能按照不同的维度拆分成多个子应用。通过主应用来加载这些子应用。微前端的核心在于拆,拆完后在合!为什么要使用微前端不同团

一、为什么要学习微前端


什么是微前端

在这里插入图片描述

微前端就是将不同的功能按照不同的维度拆分成多个子应用。通过主应用来加载这些子应用。

微前端的核心在于拆, 拆完后在合!


为什么要使用微前端


  • 不同团队间开发同一个应用技术栈不同怎么破?
  • 希望每个团队都可以独立开发,独立部署怎么破?
  • 项目中还需要老的应用代码怎么破?

我们是不是可以将一个应用划分成若干个子应用,将子应用打包成一个个的lib。当路径切换时加载同的子应用。这样每个子应用都是独立的,技术栈也不用做限制了!从而解决了前端协同开发问题


怎样落地微前端

在这里插入图片描述

微前端的灵感来源于,计算机上的应用,每一次用户打开一个应用,就相当于打开了一个新的页面


  • 2018年 Single-SPA诞生了, single-spa 是一个用于前端微服务化的 Javascript 前端解决方案 (本身没有处理样式隔离, js 执行隔离) 实现了路由劫持和应用加载
  • 2019年 qiankun 基于Single-SPA, 提供了更加开箱即用的 API ( single-spa + sandbox + import-html-entry ) 做到了,技术栈无关、并且接入简单(像iframe 一样简单)

总结:子应用可以独立构建,运行时动态加载,主子应用完全解耦,技术栈无关,靠的是协议接入(子应用必须导出 bootstrap、mount、unmount方法)


这里先回答下大家的问题:


  • 这不是iframe吗?

    如果使用 iframe , iframe 中的子应用切换路由时用户刷新页面就尴尬了

    更多参考 Why Not Iframe

  • 应用之间怎么通信

    • 基于URL来进行数据传递,但是传递消息能力弱
    • 基于 CustomEvent 实现通信
    • 基于props主子应用间通信
    • 使用全局变量、 Redux 进行通信
  • 公共依赖

    • CDN - externals
    • webpack 联邦模块

微前端架构具备以下几个核心价值:


  • 技术栈无关

    主框架不限制接入应用的技术栈,微应用具备完全自主权

  • 独立开发、独立部署

    微应用仓库独立,前后端可独立开发,部署完成后主框架自动完成同步更新

  • 增量升级

    在面对各种复杂场景时,我们通常很难对一个已经存在的系统做全量的技术栈升级或重构,而微前端是一种非常好的实施渐进式重构的手段和策略

  • 独立运行时

    每个微应用之间状态隔离,运行时状态不共享


二、SingleSpa 实战

single-spa官网


构建子应用

我们需要父应用加载子应用,需要暴露三个方法

1. bootstrap
2. mount
3. unmount

  1. 构建子应用

vue create single-child
npm i --save single-spa-vue

// 在子应用的 main.js 中导入依赖并进行配置
import singleSpaVue from 'single-spa-vue'const appOptions = {el: '#vue', // 挂载到父应用中的 id 为 vue 的标签中router,render: h => h(App)
}const vueLifeCycle = singleSpaVue({ // 返回single-spa 的生命周期也就是 bootstrap/mount/unmountVue,appOptions
});// single规定的协议,父应用会调用这些方法
export const bootstrap = vueLifeCycle.bootstrap;
export const mount = vueLifeCycle.mount;
export const unmount = vueLifeCycle.unmount;
// 这样做就有一个严重的问题,子应用无法启动了??

  1. 配置子应用中的打包路径

// 配置vue.config.js
module.exports = {configureWebpack: {output: {library: 'singleVue',libraryTarg: 'umd'},devServer: {port: 10000}}
};

  1. 配置子应用的路由

const router = new VueRouter({mode: 'history',base: '/vue', // 配置子应用路由的基础路径routes
})

  1. 父应用搭建

vue create single-parent
npm i --save single-spa // 注意这里是 single-spa

  1. 将子应用挂载到 id="vue" 的容器中

<div id&#61;"app"><router-link to&#61;"/vue">加载vue引用router-link><router-view/><div id&#61;"vue">div>
div>

  1. 配置父应用加载子应用

// 在父应用中导入依赖
import { registerApplication, start } from &#39;single-spa&#39;async function loadScript(url) { // 异步加载子组件中的脚本return new Promise((resolve, reject) &#61;> {let script &#61; document.createElement(&#39;script&#39;);script.src &#61; url;script.onload &#61; resolve;script.onerror &#61; reject;document.head.appendChild(script);});
}// 注册一个应用
registerApplication(&#39;myVueApp&#39;,async () &#61;> {console.log(&#39;加载模块&#39;);// 加载子应用中的脚本await loadScript(&#96;http://localhost:10000/js/chunk-vendors.js&#96;)await loadScript(&#96;http://localhost:10000/js/app.js&#96;)// 这里需要要返回 bootstrap/mount/unmountreturn window.singleVue},location &#61;> location.pathname.startsWith(&#39;/vue&#39;), // 此路径用来判断当前路由切换到 /vue 的路径下&#xff0c;需要加载我们定义的子应用{ a: 1 } // 选传&#xff0c;传给子应用 props 的参数&#xff0c;可以是对象或值
);start(); // 启动应用

  1. 配置子应用的路径

// 设置路径
if (window.singleSpaNavigate) { // 如果是父应用加载子应用&#xff0c;那会自动挂载一个属性&#xff0c;值为true__webpack_public_path__ &#61; &#39;http://localhost:10000/&#39;
}

  1. 希望子应用可以独立运行&#xff0c;在子应用中添加一个配置

if(!window.singleSpaNavigate){delete appOptions.el; // 子应用中没有#vue&#xff0c;所以需要手动删除&#xff0c;挂载到 #app 中new Vue(appOptions).$mount(&#39;#app&#39;);
}

singleSpa 缺陷


  1. 不能动态加载JS文件
  2. 样式不隔离
  3. 全局对象&#xff0c;没有JS沙箱的机制

三、qiankun 实战

qiankun官网


特点

1. 简单&#xff1a;任意 js 框架均可使用。微应用接入像使用接入一个 iframe 系统一样简单&#xff0c;但实际不是 iframe。
2. 完备&#xff1a;几乎包含所有构建微前端系统时所需要的基本能力&#xff0c;如 样式隔离、js 沙箱、预加载等。
3. 生产可用&#xff1a;已在蚂蚁内外经受过足够大量的线上系统的考验及打磨&#xff0c;健壮性值得信赖。

项目构建


  1. 主应用搭建 qiankun-base

vue create qiankun-base
npm i --save qiankun

// 配置主项目的加载 main.js
import Vue from &#39;vue&#39;
import App from &#39;./App.vue&#39;
import router from &#39;./router&#39;
import ElementUI from &#39;element-ui&#39;;
import &#39;element-ui/lib/theme-chalk/index.css&#39;;import {registerMicroApps, start} from &#39;qiankun&#39;;Vue.config.productionTip &#61; false
Vue.use(ElementUI);const apps &#61; [{name: &#39;vueApp&#39;, // 应用的名字entry: &#39;http://localhost:10000/&#39;, // 默认加载这个html&#xff0c;解析里面的js动态的执行&#xff08;子应用必须支持跨域&#xff0c;内部使用的是 fetch&#xff09;container: &#39;#vue&#39;, // 要渲染到的容器名idactiveRule: &#39;/vue&#39; // 通过哪一个路由来激活},{name: &#39;reactApp&#39;,entry: &#39;http://localhost:20000/&#39;,container: &#39;#react&#39;,activeRule: &#39;/react&#39;}
];registerMicroApps(apps); // 注册应用
start(); // 开启应用new Vue({router,render: h &#61;> h(App)
}).$mount(&#39;#app&#39;)


<template><div><el-menu :router&#61;"true" mode&#61;"horizontal"><el-menu-item index&#61;"/">首页el-menu-item><el-menu-item index&#61;"/vue">vue应用el-menu-item><el-menu-item index&#61;"/react">react应用el-menu-item>el-menu><router-view v-show&#61;"$route.name">router-view><div id&#61;"vue">div><div id&#61;"react">div>div>
template>

  1. 搭建Vue子项目

vue create qiankun-vue
// 子项目中不需要安装任何依赖&#xff0c;父组件会给window设置一些环境变量

// mian.js
import Vue from &#39;vue&#39;
import App from &#39;./App.vue&#39;
import router from &#39;./router&#39;Vue.config.productionTip &#61; false/*
new Vue({router,render: h &#61;> h(App)
}).$mount(&#39;#app&#39;)
*/

let instance &#61; null;
function render(props) {// props 组件通信instance &#61; new Vue({router,render: h &#61;> h(App)}).$mount(&#39;#app&#39;) // 这里是挂载到自己的HTML中&#xff0c;基座会拿到这个挂载后的HTML&#xff0c;将其插入进去
}if (!window.__POWERED_BY_QIANKUN__) { // 如果是独立运行&#xff0c;则手动调用渲染render();
}
if(window.__POWERED_BY_QIANKUN__){ // 如果是qiankun使用到了&#xff0c;则会动态注入路径__webpack_public_path__ &#61; window.__INJECTED_PUBLIC_PATH_BY_QIANKUN__;
}// 根据 qiankun 的协议需要导出 bootstrap/mount/unmount
export async function bootstrap(props) {};
export async function mount(props) {render(props);
};
export async function unmount(props) {instance.$destroy();
};

// 设置router路径
const router &#61; new VueRouter({mode: &#39;history&#39;,base: &#39;/vue&#39;,routes
})

// 配置打包 vue.config.js
module.exports &#61; {devServer: {port: 10000,headers:{&#39;Access-Control-Allow-Origin&#39;: &#39;*&#39; // 允许跨域}},configureWebpack: {output: {library: &#39;vueApp&#39;,libraryTarget: &#39;umd&#39;}}
};

  1. 搭建React项目

npx create-react-app qiankun-react
npm i --save-dev react-app-rewired

// 入口配置 /src/index.js
import React from &#39;react&#39;;
import ReactDOM from &#39;react-dom&#39;;
import &#39;./index.css&#39;;
import App from &#39;./App&#39;;function render(){ReactDOM.render(<React.StrictMode><App /></React.StrictMode>,document.getElementById(&#39;root&#39;));
}
if(!window.__POWERED_BY_QIANKUN__){render();
}
export async function bootstrap(){}
export async function mount() {render()
}
export async function unmount(){ReactDOM.unmountComponentAtNode( document.getElementById(&#39;root&#39;));
}

// 配置启动 config-overrides.js
module.exports &#61; {webpack:(config)&#61;>{config.output.library &#61; &#39;reactApp&#39;;config.output.libraryTarget &#61; &#39;umd&#39;;config.output.publicPath &#61; &#39;http://localhost:20000/&#39;;return config;},devServer:(configFunction)&#61;>{return function (proxy,allowedHost){const config &#61; configFunction(proxy,allowedHost);config.headers &#61; {"Access-Control-Allow-Origin":&#39;*&#39;}return config}}
}

添加react环境变量 .envPORT&#61;20000
WDS_SOCKET_PORT&#61;20000

// 配置react路由
import { BrowserRouter, Route, Link } from "react-router-dom"
const BASE_NAME &#61; window.__POWERED_BY_QIANKUN__ ? "/react" : "";
function App() {return (<BrowserRouter basename&#61;{BASE_NAME}><Link to&#61;"/">首页</Link><Link to&#61;"/about">关于</Link><Route path&#61;"/" exact render&#61;{() &#61;> <h1>hello home</h1>}></Route><Route path&#61;"/about" render&#61;{() &#61;> <h1>hello about</h1>}></Route></BrowserRouter>);
}

推荐阅读
  • VueCLI多页分目录打包的步骤记录
    本文介绍了使用VueCLI进行多页分目录打包的步骤,包括页面目录结构、安装依赖、获取Vue CLI需要的多页对象等内容。同时还提供了自定义不同模块页面标题的方法。 ... [详细]
  • 本文介绍了OC学习笔记中的@property和@synthesize,包括属性的定义和合成的使用方法。通过示例代码详细讲解了@property和@synthesize的作用和用法。 ... [详细]
  • Webpack5内置处理图片资源的配置方法
    本文介绍了在Webpack5中处理图片资源的配置方法。在Webpack4中,我们需要使用file-loader和url-loader来处理图片资源,但是在Webpack5中,这两个Loader的功能已经被内置到Webpack中,我们只需要简单配置即可实现图片资源的处理。本文还介绍了一些常用的配置方法,如匹配不同类型的图片文件、设置输出路径等。通过本文的学习,读者可以快速掌握Webpack5处理图片资源的方法。 ... [详细]
  • 本文介绍了如何使用vue-awesome-swiper组件,包括在main.js中引入和使用swiper和swiperSlide组件,以及设置options和ref属性。同时还介绍了如何在模板中使用swiper和swiperSlide组件,并展示了如何通过循环渲染swipes数组中的数据,并使用picUrl属性显示图片。最后还介绍了如何添加分页器。 ... [详细]
  • loader资源模块加载器webpack资源模块加载webpack内部(内部loader)默认只会处理javascript文件,也就是说它会把打包过程中所有遇到的 ... [详细]
  • 1、etcnginxconf.ddefault.conf,添加如下信息:location{try_files$uri$urirouter;rootho ... [详细]
  • 本文介绍了使用Java实现大数乘法的分治算法,包括输入数据的处理、普通大数乘法的结果和Karatsuba大数乘法的结果。通过改变long类型可以适应不同范围的大数乘法计算。 ... [详细]
  • 本文介绍了一个Java猜拳小游戏的代码,通过使用Scanner类获取用户输入的拳的数字,并随机生成计算机的拳,然后判断胜负。该游戏可以选择剪刀、石头、布三种拳,通过比较两者的拳来决定胜负。 ... [详细]
  • HDU 2372 El Dorado(DP)的最长上升子序列长度求解方法
    本文介绍了解决HDU 2372 El Dorado问题的一种动态规划方法,通过循环k的方式求解最长上升子序列的长度。具体实现过程包括初始化dp数组、读取数列、计算最长上升子序列长度等步骤。 ... [详细]
  • 目录实现效果:实现环境实现方法一:基本思路主要代码JavaScript代码总结方法二主要代码总结方法三基本思路主要代码JavaScriptHTML总结实 ... [详细]
  • 本文讨论了如何优化解决hdu 1003 java题目的动态规划方法,通过分析加法规则和最大和的性质,提出了一种优化的思路。具体方法是,当从1加到n为负时,即sum(1,n)sum(n,s),可以继续加法计算。同时,还考虑了两种特殊情况:都是负数的情况和有0的情况。最后,通过使用Scanner类来获取输入数据。 ... [详细]
  • 本文介绍了九度OnlineJudge中的1002题目“Grading”的解决方法。该题目要求设计一个公平的评分过程,将每个考题分配给3个独立的专家,如果他们的评分不一致,则需要请一位裁判做出最终决定。文章详细描述了评分规则,并给出了解决该问题的程序。 ... [详细]
  • 本文介绍了C#中数据集DataSet对象的使用及相关方法详解,包括DataSet对象的概述、与数据关系对象的互联、Rows集合和Columns集合的组成,以及DataSet对象常用的方法之一——Merge方法的使用。通过本文的阅读,读者可以了解到DataSet对象在C#中的重要性和使用方法。 ... [详细]
  • 本文讨论了将HashRouter改为Router后,页面全部变为空白页且没有报错的问题。作者提到了在实际部署中需要在服务端进行配置以避免刷新404的问题,并分享了route/index.js中hash模式的配置。文章还提到了在vueJs项目中遇到过类似的问题。 ... [详细]
  • 现象:[root@localhost~]#dockerrun-d-p9000:80centos:httpdbinsh-cusrlocalbinstart.shd5b2bd5a7bc ... [详细]
author-avatar
dtd3795290
这个家伙很懒,什么也没留下!
PHP1.CN | 中国最专业的PHP中文社区 | DevBox开发工具箱 | json解析格式化 |PHP资讯 | PHP教程 | 数据库技术 | 服务器技术 | 前端开发技术 | PHP框架 | 开发工具 | 在线工具
Copyright © 1998 - 2020 PHP1.CN. All Rights Reserved | 京公网安备 11010802041100号 | 京ICP备19059560号-4 | PHP1.CN 第一PHP社区 版权所有