原文地址html
距 qiankun 開源已過去了 11 個月,距上次官方 發聲 已過去 8 個月。前端
Announcing qiankun@2.0react
2019 年 6 月,微前端框架 qiankun 正式發佈了 1.0 版本,在這一年不到的時間內,咱們收穫了 4k+ star,收穫了來自 single-spa 官方團隊的問候,支撐了阿里 200+ 線上應用,也成爲社區不少團隊選用的微前端解決方案。webpack
在今天,qiankun 將正式發佈 2.0 版本。git
qiankun@2.0 帶來了一些新能力的同時,只作了很小的 API 調整,1.x 的用戶能夠很輕鬆的遷移到 2.x 版本,詳細信息見下方 升級指南 小節。github
可能有的朋友還不太瞭解 微前端 和 qiankun 是什麼。web
微前端是最近一年國內前端領域被頻繁說起的關鍵字,雖然它並非一個全新的領域/技術,但很顯然在當今愈來愈多的前端應用即將步入第 3 個、第 5 個甚至更久的年頭的背景下,如何給 巨石應用/遺產應用 注入新鮮的技術血液已經成爲咱們不得不正視的問題,而微前端正是解決這類問題的一個很是合適的解決方案。api
qiankun 是一個生產可用的微前端框架,它基於 single-spa,具有 js 沙箱、樣式隔離、HTML Loader、預加載 等微前端系統所需的能力。qiankun 能夠用於任意 js 框架,微應用接入像嵌入一個 iframe 系統同樣簡單。瀏覽器
更多信息能夠查閱咱們的 官方站點bash
qiankun 2.0 帶來的最大變化即是 qiankun 的定位將由 微前端框架 轉變爲 微應用加載器。
此前 qiankun 的典型應用場景是 route-based 的控制檯應用,作爲一個微應用的聚合框架而被使用。
如上圖所示,在這種場景下,一個負責聚合與切換的主應用 與 多個相互獨自的微應用 一塊兒構成了整個大的微前端應用,通常來講頁面上活躍着的也每每只有一個微應用。
而這是微前端的場景之一,在另一些場景下,你應該能夠在同一個頁面中,加載多個不一樣的微應用,每一個微應用都是主應用的組成部分 或者是 提供一些加強能力,這種場景能夠說是微應用粒度的前端組件化。
所以,qiankun@2.0 將跳出 route-based 的微前端場景,提供更加通用的微應用加載能力,讓用戶能夠更加自由的組合微應用來搭建產品。
新功能
此外咱們還作了
另外咱們還升級了相應的 umi qiankun plugin,在 umi 場景下你能夠這樣去加載一個微應用:
import { MicroApp } from 'umi';
function MyPage() {
return (
<div>
<MicroApp name="qiankun"/>
</div>
);
}
複製代碼
在 qiankun@1.x 中,咱們的沙箱、樣式隔離等機制只能對單一微應用場景生效,多個微應用共存的支持能力尚不完備。
而在 2.0 版本中,咱們終於完善了這一功能,如今,你能夠同時激活多個微應用,而微應用之間能夠保持互不干擾。
**在多應用場景下,每一個微應用的沙箱都是相互隔離的,也就是說每一個微應用對全局的影響都會侷限在微應用本身的做用域內。**好比 A 應用在 window
上新增了個屬性 test
,這個屬性只能在 A 應用本身的做用域經過 window.test
獲取到,主應用或者其餘微應用都沒法拿到這個變量。
可是注意,頁面上不能同時顯示多個依賴於路由的微應用,由於瀏覽器只有一個 url,若是有多個依賴路由的微應用同時被激活,那麼大機率會致使其中一個 404。
爲了更方便的同時裝載多個微應用,咱們提供了一個全新的 API loadMicroApp
,用於手動控制微應用:
import { loadMicroApp } from 'qiankun';
/** 手動加載一個微應用 */
const microApp = loadMicroApp(
{
name: "microApp",
entry: "https://localhost:7001/micro-app.html",
container: "#microApp"
}
)
// 手動卸載
microApp.mountPromise.then(() => microApp.unmount());
複製代碼
這也是 qiankun 做爲一個應用加載器的使用方式。
基於這個 api,你能夠很容易的封裝一個本身的微應用容器組件,好比:
class MicroApp extends React.Component {
microAppRef = null;
componentDidMount() {
const { name, entry } = this.props;
this.microAppRef = loadMicroApp({ name, entry, container: '#container' });
}
componentWillUnmount() {
this.microAppRef.mountPromise.then(() => this.microAppRef.unmount());
}
render() {
return <div id="container"/>;
}
}
複製代碼
在 qiankun issue 區域呼聲最高的就是 IE 的兼容,有很多小夥伴都期待 qiankun 可以在 IE 下使用。
qiankun 1.x 在 IE 使用的主要阻礙就是 qiankun 的沙箱使用了 ES6 的 Proxy,而這沒法經過 ployfill 等方式彌補。這致使 IE 下的 qiankun 用戶沒法開啓 qiankun 的沙箱功能,致使 js 隔離、樣式隔離這些能力都沒法啓用。
爲此,咱們實現了一個 IE 特供的快照沙箱,用於這些不支持 Proxy 的瀏覽器;這不須要用戶手動開啓,在代理沙箱不支持的環境中,咱們會自動降級到快照沙箱。
注意,因爲快照沙箱不能作到互相之間的徹底獨立,因此 IE 等環境下咱們不支持多應用場景,
singlur
會被強制設爲 true。
樣式隔離也是微前端面臨的一個重要問題,在 qiankun@1.x 中,咱們支持了微應用之間的樣式隔離(僅沙箱開啓時生效),這尚存一些問題:
爲此,咱們引入了一個新的選項, sandbox: { strictStyleIsolation?: boolean }
。
在該選項開啓的狀況下,咱們會以 Shadow DOM 的形式嵌入微應用,以此來作到應用樣式的真正隔離:
import { loadMicroApp } from 'qiankun'
loadMicroApp({xxx}, { sandbox: { strictStyleIsolation: true } });
複製代碼
Shadow DOM 能夠作到樣式之間的真正隔離(而不是依賴分配前綴等約定式隔離),其形式以下:
圖片來自 MDN
在開啓 strictStyleIsolation
時,咱們會將微應用插入到 qiankun 建立好的 Shadow Tree 中,微應用的樣式(包括動態插入的樣式)都會被掛載到這個 Shadow Host 節點下,所以微應用的樣式只會做用在 Shadow Tree 內部,這樣就作到了樣式隔離。
可是開啓 Shadow DOM 也會引起一些別的問題:
一個典型的問題是,一些組件可能會越過 Shadow Boundary 到外部 Document Tree 插入節點,而這部分節點的樣式就會丟失;好比 antd 的 Modal
就會渲染節點至 ducument.body
,引起樣式丟失;針對剛纔的 antd 場景你能夠經過他們提供的 ConfigProvider.getPopupContainer
API 來指定在 Shadow Tree 內部的節點爲掛載節點,但另一些其餘的組件庫,或者你的一些代碼也會遇到一樣的問題,須要你額外留心。
此外 Shadow DOM 場景下還會有一些額外的事件處理、邊界處理等問題,後續咱們會逐步更新官方文檔指導用戶更順利的開啓 Shadow DOM。
因此請根據實際狀況來選擇是否開啓基於 shadow DOM 的樣式隔離,並作好相應的檢查和處理。
微前端場景下,咱們認爲最合理的通訊方案是經過 URL 及 CustomEvent 來處理。但在一些簡單場景下,基於 props 的方案會更直接便捷,所以咱們爲 qiankun 用戶提供這樣一組 API 來完成應用間的通訊:
主應用建立共享狀態:
import { initGloabalState } from 'qiankun';
initGloabalState({ user: 'kuitos' });
複製代碼
微應用經過 props 獲取共享狀態並監聽:
export function mount(props) {
props.onGlobalStateChange((state, prevState) => {
console.log(state, prevState);
});
};
複製代碼
更詳細的 API 介紹能夠查看官方文檔。
除了基本的平常維護、bugfix 以外,咱們還會嘗試走的更遠:
2.0 版本 調整了至關多的內部 API 名字,但你們使用的外部 API 變化並不大(基本徹底兼容 1.x),你能夠在十分鐘內完成升級。
import { registerMicroApps } from 'qiankun'
registerMicroApps(
[
{
name: 'react16',
entry: '//localhost:7100',
- activeRule: location => location.pathname.startsWith('/react'),
+ activeRule: '/react',
- render: renderFn,
+ container: '#subapp-viewport',
},
]
)
複製代碼
如今你能夠簡單的指定一個掛載節點便可,而不用本身手寫對應的 render 函數了。簡單場景下 activeRule
配置也不須要再手寫函數了(固然仍是支持自定義函數),只須要給出一個前綴規則字符串便可,同時支持 react-router 類的動態規則,如 /react/:appId/name
(來自 single-spa 5.x 的支持)。
同時,微應用收到的 props
中會新增一個 container
屬性,這就是你的掛載節點的 DOM,這對處理動態添加的容器以及開啓了 Shadow DOM 場景下很是有用。
注意,舊的 render 配置依然可使用,咱們作了兼容處理方便不想升級的用戶;但 render 存在時,container 就不會生效。
由於咱們引入了一些新的能力,由於 start 的配置也發生了一些變化:
import { start } from 'qiankun'
start({
- jsSandbox: true,
+ sandbox: {
+ strictStyleIsolation: true
+ }
})
複製代碼
loadMicroApp
這個 API 用於手動掛載一個微應用
/** 用於加載一個微應用 */
loadMicroApp(app: LoadableApp, configuration?: FrameworkConfiguration)
複製代碼
使用詳情可見上面 多應用支持 小節。