日韩性视频-久久久蜜桃-www中文字幕-在线中文字幕av-亚洲欧美一区二区三区四区-撸久久-香蕉视频一区-久久无码精品丰满人妻-国产高潮av-激情福利社-日韩av网址大全-国产精品久久999-日本五十路在线-性欧美在线-久久99精品波多结衣一区-男女午夜免费视频-黑人极品ⅴideos精品欧美棵-人人妻人人澡人人爽精品欧美一区-日韩一区在线看-欧美a级在线免费观看

歡迎訪問 生活随笔!

生活随笔

當(dāng)前位置: 首頁 > 编程资源 > 编程问答 >内容正文

编程问答

10分钟弄懂微应用框架——乾坤,真香!

發(fā)布時(shí)間:2023/12/29 编程问答 50 豆豆
生活随笔 收集整理的這篇文章主要介紹了 10分钟弄懂微应用框架——乾坤,真香! 小編覺得挺不錯(cuò)的,現(xiàn)在分享給大家,幫大家做個(gè)參考.

前言

今天剛剛學(xué)習(xí)了一個(gè)微前端框架——乾坤,正著熱乎勁,寫一篇入門博客。這篇文章不會(huì)討論太多的原理和實(shí)現(xiàn),只是一個(gè)入門寫 Hello World 的教程。

文章的很多地方都參考官網(wǎng),但是官網(wǎng)的教程太簡潔了,個(gè)人覺得還是做不到無腦上手,希望這篇文章可以幫到正在入門微前端的伙伴。

想直接看代碼的,我寫了個(gè)比官網(wǎng)更簡單的例子,點(diǎn)擊即可學(xué)會(huì)。

什么是微前端

首先,來了解一下微前端是個(gè)啥。

當(dāng)我們寫了一個(gè)又一個(gè)的 SPA 應(yīng)用。突然有一天,老板說要將這些應(yīng)用合并,前端工程們就頭大了——每個(gè)應(yīng)用的代碼都是一座搖搖欲墜的💩山,別說合并了,就算動(dòng)都不敢動(dòng)呀。

雖然很麻煩,但是前端工程師還是把這個(gè)問題解決了,而這個(gè)將多個(gè) SPA 合并成一個(gè) Web App 的解決方案就是微前端

為什么要微前端

“多個(gè) SPA 合并成一個(gè) Web App?”,可能有人會(huì)想到用 <iframe/> 也可以實(shí)現(xiàn)一個(gè)網(wǎng)頁里內(nèi)嵌多個(gè)網(wǎng)頁呀。原因有:

  • 不感知 url 狀態(tài),比如前進(jìn)、后退沒法玩

  • UI 不同步、DOM 不同享。<iframe/> 本質(zhì)上是頁面的硬隔離,所以如果你有個(gè)遮罩層,可能只能在那一小片區(qū)域才展示遮罩層

  • 頁面之間的通信很麻煩

  • 每次都要加載子應(yīng)用,速度很慢

而微前端正好可以補(bǔ)足上面的缺點(diǎn)。

微前端的優(yōu)勢(shì)

除了解決了上面的問題,微前端還有如下的優(yōu)點(diǎn):

  • 子應(yīng)用技術(shù)棧無關(guān),即類似上頁說的頁面硬隔離,但是是以 sandbox 的方式實(shí)現(xiàn)的

  • 合并多個(gè)子應(yīng)用,相對(duì)地,也可以將大應(yīng)用拆解成多個(gè)應(yīng)用,實(shí)現(xiàn)業(yè)務(wù)解耦

  • 子應(yīng)用高度自治,發(fā)布、報(bào)錯(cuò)、測(cè)試流程僅限于子應(yīng)用,不會(huì)受別的業(yè)務(wù)影響,同時(shí)也不影響別的業(yè)務(wù)

乾坤由來

最原始的微前端框架并不是乾坤,而是 single-spa。但是這個(gè)框架只提供最基本的功能,而且全是英文,文檔寫得也很繁瑣,應(yīng)該沒人想去看。

阿里的乾坤則是基于 single-spa 開發(fā)的又一個(gè)微前端框架,提供了更多的功能,也解決一些坑,官網(wǎng)也很簡潔。

不過,個(gè)人覺得有點(diǎn)太簡潔了,寫 Hello World 的時(shí)候還是遇到一些坑,只能看 Github 的 /examples 目錄學(xué)習(xí)。

主應(yīng)用 VS 子應(yīng)用

首先,要知道現(xiàn)在項(xiàng)目并不是只有一個(gè)了,而是區(qū)分出 主應(yīng)用子應(yīng)用,關(guān)系如下:

兩者區(qū)別:

  • 主應(yīng)用

    • 概念:就是要統(tǒng)治各個(gè)子應(yīng)用的應(yīng)用,也即合并結(jié)果頁面

    • 負(fù)責(zé)子應(yīng)用的注冊(cè)、路由分發(fā)。可以簡單理解為 React.js 和 Vue.js 里的 App 組件,主要做一些初始化、路由注冊(cè)、全局狀態(tài)注冊(cè)、銷毀時(shí)的動(dòng)作

  • 子應(yīng)用

    • 概念:各個(gè) SPA 應(yīng)用,可以理解為 SPA 里的頁面組件

    • 負(fù)責(zé)暴露一些函數(shù),以此對(duì)接主應(yīng)用,讓主應(yīng)用知道:哦,原來你是子應(yīng)用,要和我對(duì)接。常見的對(duì)應(yīng)函數(shù)有:bootstrap, mount, unmount

項(xiàng)目創(chuàng)建

乾坤官網(wǎng)最推薦的做法是將主應(yīng)用和子應(yīng)用分成兩個(gè)項(xiàng)目,各自管理。當(dāng)然,也可以一個(gè)項(xiàng)目里分成不同的目錄來存放。

├── main # 主應(yīng)用 ├── baidu # 子應(yīng)用 └── taobao # 子應(yīng)用

如果你覺得 官方的例子 太復(fù)雜,也可以看我自己建的 qiankun-bigass-app,子應(yīng)用只有兩個(gè)用 React.js 的項(xiàng)目。我把很多無關(guān)的代碼都刪了。

實(shí)現(xiàn)主應(yīng)用

理清上面的關(guān)系后,我們直接干代碼,先看主應(yīng)用。

首先,我們弄一個(gè) .html 文件出來,作為主頁面的 HTML 模板:

<body><div?class="mainapp"><!--?標(biāo)題欄?--><header?class="mainapp-header"><h1>QianKun</h1></header><div?class="mainapp-main"><!--?側(cè)邊欄?--><ul?class="mainapp-sidemenu"><li?onclick="push('/taobao')">淘寶</li><li?onclick="push('/baidu')">百度</li></ul><!--?子應(yīng)用??--><main?id="subapp-container"></main></div></div><script>function?push(subapp)?{?history.pushState(null,?subapp,?subapp)?}</script> </body>

然后,使用 Webpack,指定為 template HTML,并配置 dev server,注意一定要配置 headers,不然會(huì)有跨域的問題,子應(yīng)用同理:

//?webpack.config.js const?HtmlWebpackPlugin?=?require('html-webpack-plugin');module.exports?=?{entry:?'./index.js',devtool:?'source-map',devServer:?{open:?true,port:?'7099',clientLogLevel:?'warning',disableHostCheck:?true,compress:?true,headers:?{'Access-Control-Allow-Origin':?'*',},historyApiFallback:?true,overlay:?{?warnings:?false,?errors:?true?},},output:?{publicPath:?'/',},mode:?'development',resolve:?{extensions:?['.js',?'.jsx',?'.ts',?'.tsx'],},module:?{rules:?[{test:?/\.jsx?$/,exclude:?/node_modules/,use:?{loader:?'babel-loader',options:?{presets:?['@babel/preset-env'],plugins:?['@babel/plugin-transform-react-jsx'],},},},{test:?/\.(le|c)ss$/,use:?['style-loader',?'css-loader',?'less-loader'],},],},plugins:?[new?HtmlWebpackPlugin({filename:?'index.html',template:?process.env.MODE?===?'multiple'???'./multiple.html'?:?'./index.html',minify:?{removeComments:?true,collapseWhitespace:?true,},}),], };

入口文件 index.js 就比較重要了,需要完成主應(yīng)用的很多事情:

import?{?registerMicroApps,?runAfterFirstMounted,?setDefaultMountApp,?start,?initGlobalState?}?from?'qiankun'; import?'./index.less';/***?主應(yīng)用?**可以使用任意技術(shù)棧***?以下分別是?React?和?Vue?的示例,可切換嘗試*/ import?render?from?'./Render'; //?import?render?from?'./render/VueRender';/***?Step1?初始化應(yīng)用(可選)*/ render({?loading:?true?});const?loader?=?loading?=>?render({?loading?});/***?Step2?注冊(cè)子應(yīng)用*/registerMicroApps([{name:?'taobao',entry:?'//localhost:7101',container:?'#subapp-viewport',loader,activeRule:?'/taobao',},{name:?'baidu',entry:?'//localhost:7102',container:?'#subapp-viewport',loader,activeRule:?'/baidu',},],{beforeLoad:?[app?=>?{console.log('[LifeCycle]?before?load?%c%s',?'color:?green;',?app.name);},],beforeMount:?[app?=>?{console.log('[LifeCycle]?before?mount?%c%s',?'color:?green;',?app.name);},],afterUnmount:?[app?=>?{console.log('[LifeCycle]?after?unmount?%c%s',?'color:?green;',?app.name);},],}, );const?{?onGlobalStateChange,?setGlobalState?}?=?initGlobalState({user:?'qiankun', });onGlobalStateChange((value,?prev)?=>?console.log('[onGlobalStateChange?-?master]:',?value,?prev),?true);setGlobalState({ignore:?'master',user:?{name:?'master',}, });/***?Step3?設(shè)置默認(rèn)進(jìn)入的子應(yīng)用*/ setDefaultMountApp('/taobao');/***?Step4?啟動(dòng)應(yīng)用*/ start();runAfterFirstMounted(()?=>?{console.log('[MainApp]?first?app?mounted'); });

上面主要完成:初始化、注冊(cè)子應(yīng)用、設(shè)置配置全局狀態(tài)、設(shè)置默認(rèn)進(jìn)入子應(yīng)用、啟動(dòng)應(yīng)用。

至于初始渲染函數(shù),可以這么寫:

import?React?from?'react'; import?ReactDOM?from?'react-dom';/***?渲染子應(yīng)用*/ function?Render(props)?{const?{?loading?}?=?props;return?(<>{loading?&&?<h4?className="subapp-loading">Loading...</h4>}<div?id="subapp-viewport"?/></>); }export?default?function?render({?loading?})?{const?container?=?document.getElementById('subapp-container');ReactDOM.render(<Render?loading={loading}?/>,?container); }

實(shí)現(xiàn)子應(yīng)用

子應(yīng)用其實(shí)和官網(wǎng)的差不多,這里以 React.js 子應(yīng)用舉例。首先用 create-react-app 來創(chuàng)建子應(yīng)用:

create-react-app?baidu

在 src 目錄下新增 public-path.js:

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

設(shè)置 history 模式路由的 base:

const?RouteExample?=?()?=>?{return?(<Router?basename={window.__POWERED_BY_QIANKUN__???'/baidu'?:?'/'}><nav><Link?to="/">Home</Link><Link?to="/about">About</Link></nav><Suspense?fallback={null}><Switch><Route?path="/"?exact?component={Home}?/><Route?path="/about"?component={About}?/></Switch></Suspense></Router>); };export?default?function?App()?{return?(<div?className="app-main"><h1>淘寶Taobao</h1><hr/><RouteExample?/></div>); }

__POWERED_BY_QIANKUN__ 用于判斷現(xiàn)在是否作為子應(yīng)用被訪問,其它地方與普通 React.js App 沒差別。

去掉一些無用的文件后,在入口配置子應(yīng)用:

function?render(props)?{const?{?container?}?=?props;ReactDOM.render(<App?/>,?container???container.querySelector('#root')?:?document.querySelector('#root')); }if?(!window.__POWERED_BY_QIANKUN__)?{render({}); }//?監(jiān)聽全局狀態(tài) function?storeTest(props)?{props.onGlobalStateChange((value,?prev)?=>?console.log('淘寶',?`[onGlobalStateChange?-?${props.name}]:`,?value,?prev),?true);props.setGlobalState({ignore:?props.name,user:?{name:?props.name,},}); }export?async?function?bootstrap()?{console.log('[淘寶]?react?app?bootstraped'); }export?async?function?mount(props)?{console.log('[淘寶]?props?from?main?framework',?props);storeTest(props);render(props); }export?async?function?unmount(props)?{const?{?container?}?=?props;ReactDOM.unmountComponentAtNode(container???container.querySelector('#root')?:?document.querySelector('#root')); }

注意上面的 bootstrap, mount 和 unmount 一定要 export 出去,不然沒人知道這個(gè)是子應(yīng)用。

下一步,是修改 Webpack 的配置。但是 creat-react-app 造出來的 React App 不 eject 出來就改不了,這里官網(wǎng)推薦使用 @rescripts/cli 來修改:

yarn?add?-D?@rescript/cli

在根目錄添加 .rescriptsrc.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';return?config;},devServer:?_?=>?{const?config?=?_;config.headers?=?{'Access-Control-Allow-Origin':?'*',};config.historyApiFallback?=?true;config.hot?=?false;config.watchContentBase?=?false;config.liveReload?=?false;return?config;}, };

Webpack 配置同樣很重要,一個(gè)是配置 historyApiFallback 處理單頁的 404 問題,另一個(gè)是通過 Access-Control-Allow-Origin 解決主應(yīng)用訪問子應(yīng)用的跨域問題。

在上面的主應(yīng)用里看到我們是要訪問不同的端口的,那端口要怎么配置呢?可以通過 .env 來配置:

SKIP_PREFLIGHT_CHECK=true BROWSER=none PORT=7101 WDS_SOCKET_PORT=7102

更多框架的配置可見這里。

API 粗講

乾坤的 API 也不是很多,詳見這里。簡單講一下用處:

API用處類比
registerMicroApps主應(yīng)用用來注冊(cè)多個(gè)子應(yīng)用的函數(shù)類似于 Vue 和 React 的路由
start啟動(dòng)主應(yīng)用類似于 React.js 的 render 函數(shù)和 Vue.js 的 new Vue()
loadMicroApp手動(dòng)加載子應(yīng)用也類似于 React.js 的 render 函數(shù)和 Vue.js 的 new Vue(),只不過更自由了
prefetchApps預(yù)加載子應(yīng)用類似于 Webpack 的 prefetch 功能
addGlobalUncaughtErrorHandler頁面報(bào)錯(cuò)時(shí)可以用于上報(bào)和兜底-
removeGlobalUncaughtErrorHandler都懂的-
initGlobalState初始化全局狀態(tài)類似于 Redux 的 createStore 和 Vue 的 new Vue.Store()

最后

查看:2021年總結(jié)記錄

關(guān)注前端開發(fā)博客,在后臺(tái)回復(fù)以下關(guān)鍵字可以獲取資源。

  • 回復(fù)「小抄」,領(lǐng)取Vue、JavaScript 和 WebComponent 小抄 PDF

  • 回復(fù)「Vue腦圖」獲取 Vue 相關(guān)腦圖

  • 回復(fù)「思維圖」獲取 JavaScript 相關(guān)思維圖

  • 回復(fù)「簡歷」獲取簡歷制作建議

  • 回復(fù)「簡歷模板」獲取精選的簡歷模板

  • 回復(fù)「加群」進(jìn)入500人前端精英群

  • 回復(fù)「電子書」下載我整理的大量前端資源,含面試、Vue實(shí)戰(zhàn)項(xiàng)目、CSS和JavaScript電子書等。

  • 回復(fù)「知識(shí)點(diǎn)」下載高清JavaScript知識(shí)點(diǎn)圖譜

  • 點(diǎn)贊和在看就是最大的支持??

    總結(jié)

    以上是生活随笔為你收集整理的10分钟弄懂微应用框架——乾坤,真香!的全部內(nèi)容,希望文章能夠幫你解決所遇到的問題。

    如果覺得生活随笔網(wǎng)站內(nèi)容還不錯(cuò),歡迎將生活随笔推薦給好友。