您的位置:首页 >聚焦 >

微前端 从 0到 1搭建

2022-04-08 05:58:31    来源:程序员客栈
在这里插入图片描述
微前端

微前端

Single-SPA

微服务是面向服务架构(SOA)的一种变体,把应用程序设计成一系列松耦合的细粒度服务,并通过轻量级的通信协议组织起来具体地,将应用构建成一组小型服务。这些服务都能够独立部署、独立扩展,每个服务都具有稳固的模块边界,甚至允许使用不同的编程语言来编写不同服务,也可以由不同的团队来管理

image-20220405155852230
概念

官网:

2018年 Single-SPA诞生了, single-spa是一个用于前端微服务化的JavaScript前端解决方案 (本身没有处理样式隔离、js执行隔离) 实现了路由劫持和应用加载;

Alibaba -

springboot

- sofaboot

Single-SPA 搞了个入口 --> qiankun

2019年 qiankun基于Single-SPA, 提供了更加开箱即用的 API (single-spa + sandbox + import-html-entry),它 做到了技术栈无关,并且接入简单(有多简单呢,像iframe一样简单)。

总结:子应用可以独立构建,运行时动态加载,主子应用完全解耦,并且技术栈无关,靠的是协议接入(这里提前强调一下:子应用必须导出 bootstrap、mount、unmount三个方法)。

micro front ends single spot

应用量庞大,

实现上,关键问题在于:

多个 Bundle 如何集成?

子应用之间怎样隔离影响?

公共资源如何复用?

子应用间怎样通信?

如何测试?

当然,这种架构模式并非百益而无一害,一些问题也随之而来:

导致依赖项冗余,增加用户的流量负担

团队自治程度的增加,可能会破坏协作

「.....」

简单来讲,微前端的理念类似于微服务:

In short, micro frontends are all about slicing up big and scary things into smaller, more manageable pieces, and then being explicit about the dependencies between them.

将庞大的整体拆成可控的小块,并明确它们之间的依赖关系。关键优势在于:

代码库更小,更内聚、可维护性更高松耦合、自治的团队可扩展性更好「渐进地升级、更新甚至重写部分前端功能成为了可能」

「微前端」

微前端到底是什么?
img
微前端架构实战中-single-spa 篇
微前端

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

image-20220403073941748

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

今天来一块聊聊微前端 技术

img
一门前端语言的基础 Vue ReactSingleSpa 实战构建子应用

首先创建一个vue子应用,并通过single-spa-vue来导出必要的生命周期:

vuecreatespa-vuenpminstallsingle-spa-vue

importsingleSpaVuefrom"single-spa-vue";constappOptions={el:"#vue",router,render:h=>h(App)}//在非子应用中正常挂载应用if(!window.singleSpaNavigate){deleteappOptions.el;newVue(appOptions).$mount("#app");}constvueLifeCycle=singleSpaVue({Vue,appOptions});//子应用必须导出以下生命周期:bootstrap、mount、unmountexportconstbootstrap=vueLifeCycle.bootstrap;exportconstmount=vueLifeCycle.mount;exportconstunmount=vueLifeCycle.unmount;exportdefaultvueLifeCycle;

constrouter=newVueRouter({mode:"history",base:"/vue",//改变路径配置routes})

配置库打包

//vue.config.jsmodule.exports={configureWebpack:{output:{library:"singleVue",libraryTarget:"umd"},devServer:{port:10000}}}

主应用搭建

将子应用挂载到id="vue"标签中

importVuefrom"vue"importAppfrom"./App.vue"importrouterfrom"./router"importElementUIfrom"element-ui";import"element-ui/lib/theme-chalk/index.css";Vue.use(ElementUI);constloadScript=async(url)=>{awaitnewPromise((resolve,reject)=>{constscript=document.createElement("script");script.src=url;script.onload=resolve;script.onerror=reject;document.head.appendChild(script)});}import{registerApplication,start}from"single-spa";registerApplication("singleVue",async()=>{//这里通过协议来加载指定文件awaitloadScript("http://localhost:10000/js/chunk-vendors.js");awaitloadScript("http://localhost:10000/js/app.js");returnwindow.singleVue},location=>location.pathname.startsWith("/vue"))start();newVue({router,render:h=>h(App)}).$mount("#app")

动态设置子应用

if(window.singleSpaNavigate){__webpack_public_path__="http://localhost:10000/"}

前置条件

npminstall-gyarn

yarninit

安装 官方 React

Create React App是FaceBook的React团队官方出的一个构建React单页面应用的脚手架工具。它本身集成了Webpack,并配置了一系列内置的loader和默认的npm的脚本,可以很轻松的实现零配置就可以快速开发React的应用。

# 全局安装npm install -g create-react-app# 构建一个my-app的项目npx create-react-app my-appcd my-app# 启动编译当前的React项目,并自动打开 http://localhost:3000/npm start

构建 React项目

npm

npminitreact-appmy-app

Yarn

#yarncreateisavailableinYarn0.25+yarncreatereact-appmy-app

使用 qiankun 微前端构建

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

首先我们需要创建 3个 前端应用, 微前端 ,就是 代表 一个小型应用的独立部署,独立交互,需要 应用之间进行通信,这里我们使用qiankun来完成 微前端 应用

创建 3 个 react app

yarncreatereact-appqiankun-base--templatetypescript

yarncreatereact-appqiankun-micro-app1--templatetypescript

yarncreatereact-appqiankun-micro-app2--templatetypescript

app2app1基座

在 react app 应用中 安装 qiankun 依赖

$yarnaddqiankun#ornpmiqiankun-S

分别创建 .env 文件来指定 项目 运行的端口号

PORT=3010PORT=3011PORT=3012

image-20220405201705407

在主应用中index.tsx 注册子应用

import React from "react";import ReactDOM from "react-dom";import "./index.css";import App from "./App";import reportWebVitals from "./reportWebVitals";import { registerMicroApps, start } from "qiankun";registerMicroApps([  {    name: "react app one", // app name registered    entry: "//localhost:3011",    container: "#micro-app2",    activeRule: "/micro-app2",    props:{       nickname: "全栈小刘",       age:19    }  },  {    name: "react app two", // app name registered    entry: "//localhost:3012",    container: "#micro-app1",    activeRule: "/micro-app1",    props:{       nickname: "全栈小刘",       age:18    }  },]);start();ReactDOM.render(        ,  document.getElementById("root"));// If you want to start measuring performance in your app, pass a function// to log results (for example: reportWebVitals(console.log))// or send to an analytics endpoint. Learn more: https://bit.ly/CRA-vitalsreportWebVitals();

注:子应用加载进来 ,需要有主应用进行挂载,现在我们已经将 子应用注册在 了 主应用当中

name 应用名称entry 端口号container 挂载容器activeRule 激活的规则props: 父子属性之间传参

api文档: https://qiankun.umijs.org/zh/api

在 App.tsx 中创建挂载点

import React from "react";import logo from "./logo.svg";import "./App.css";function App() {  return (      );}export default App;

在「所有」应用中 添加 public-path.js 用于 加载静态 资源

if(window.__POWERED_BY_QIANKUN__){__webpack_public_path__=window.__INJECTED_PUBLIC_PATH_BY_QIANKUN__;}

在子应用中 添加 webpack 重写项

添加

yarnaddreact-app-rewired-D

「设置 子应用」启动,在 scripts

"scripts":{"start":"react-app-rewiredstart","build":"react-scriptsbuild","test":"react-scriptstest","eject":"react-scriptseject"},

在webpack 中 进行 overrides 重写,重写的 目的是 允许跨域

config-overrides.js

const{name}=require("./package");module.exports={webpack:(config)=>{config.output.library=`${name}-[name]`;config.output.libraryTarget="umd";//config.output.jsonpFunction=`webpackJsonp_${name}`;config.output.globalObject="window";returnconfig;},devServer:(_)=>{constconfig=_;config.headers={"Access-Control-Allow-Origin":"*",};config.historyApiFallback=true;config.hot=false;config.watchContentBase=false;config.liveReload=false;returnconfig;},};

在不同的子应用当中 去加载 tsx 生命周期

app1 、app2 、 index.tsx

import React from "react";import ReactDOM from "react-dom";import "./index.css";import App from "./App";import reportWebVitals from "./reportWebVitals";export async function bootstrap() {  console.log("[react] react app bootstraped");}// @ts-ignoreexport async function mount(props) {  console.log(props)  ReactDOM.render(      ,      props.container     ? props.container.querySelector("#root")     : document.getElementById("root"));}// @ts-ignoreexport async function update(props){   console.log("update props",props)}// @ts-ignoreexport async function unmount(props) {  ReactDOM.unmountComponentAtNode(     props.container     ? props.container.querySelector("#root")     : document.getElementById("root"));}// @ts-ignoreif (!window.__POWERED_BY_QIANKUN__) {  ReactDOM.render(,document.getElementById("root"))}// If you want to start measuring performance in your app, pass a function// to log results (for example: reportWebVitals(console.log))// or send to an analytics endpoint. Learn more: https://bit.ly/CRA-vitalsreportWebVitals();

在 index.tsx 引入 public-path.js 解决静态资源

import "./public-path.js"

在 主 应用 添加 访问

import React from "react";import logo from "./logo.svg";import "./App.css";function App() {  return (      );}export default App;

在 主 应用 index.tsx 中 传递 数据

props:{    nickname: "全栈小刘",    age:19  }

在 子应用 周期中进行打印

export async function mount(props) {  console.log(props)  ReactDOM.render(      ,      props.container     ? props.container.querySelector("#root")     : document.getElementById("root"));}

image-20220402075342284
在 主应用中 监听事件改变

import { initGlobalState, MicroAppStateActions } from "qiankun";const state ={  nickname: "全栈小刘"}// 初始化const actions: MicroAppStateActions = initGlobalState(state);actions.onGlobalStateChange((state,prev)=>{    console.log(state,prev)})// 2秒钟后 改变setTimeout(()=>{   actions.setGlobalState({...state,age:19})},2000)

在子应用中 监听改变

export async function mount(props) {      console.log(props)  // @ts-ignore    props.onGlobalStateChange((state,prev)=>{      console.log(state,prev)      setTimeout(()=>{        props.setGlobalState({ ...state, age:20 });      },2000)    })  // @ts-ignore    ReactDOM.render(      ,      props.container     ? props.container.querySelector("#root")     : document.getElementById("root"));}

安装 NPM SCRIPT 插件 ,分别 启动 运行
image-20220402073016755
接入 Vue3

通用 vue3

安装最新的脚手架

npminstall-g@vue/cli

创建 项目 es6 js 模块

vuecreateqiankun-vue-micro-app3

添加typescript ,转换 ts -Yyes

vueaddtypescript

依次加入 public-path.js

/*eslint-disable*/if(window.__POWERED_BY_QIANKUN__){__webpack_public_path__=window.__INJECTED_PUBLIC_PATH_BY_QIANKUN__;}

安装 qiankun

yarnaddqiankun

参考qiankun官网的示例main.js ,完成生命周期的钩子函数

base 中 进行注册 index.tsx

vue-config.js 设置启动端口

vue-config.js

/*eslint-disable*/const{name}=require("./package");module.exports={devServer:{port:3013,headers:{"Access-Control-Allow-Origin":"*",}},configureWebpack:{output:{library:`${name}-[name]`,libraryTarget:"umd"}}};

微前端项目 实战

https://github.com/a1029563229/micro-front-template

效果图
Reference Document :https://juejin.cn/post/6844903943873675271https://zhuanlan.zhihu.com/p/96464401https://single-spa.js.org/

关键词: 生命周期 前置条件

相关阅读