自拍偷在线精品自拍偷,亚洲欧美中文日韩v在线观看不卡

微前端方案 Qiankun 只是更完善的 Single-Spa

開發(fā) 前端
前端應(yīng)用能夠單獨(dú)跑,也能被集成到另一個(gè)應(yīng)用中跑,這種架構(gòu)叫做微前端架構(gòu)。它在解決跨技術(shù)棧的應(yīng)用集成、大項(xiàng)目拆分的場(chǎng)景下是很有用的。

一個(gè)前端應(yīng)用能夠單獨(dú)跑,也能被作為一個(gè)模塊集成到另一個(gè)應(yīng)用里,這種架構(gòu)方式就叫做微前端。

它在前端領(lǐng)域能解決一些特定的問題:

  • 中后臺(tái)系統(tǒng)中,有一些別的技術(shù)棧開發(fā)的歷史模塊,但是希望能夠在入口里集成進(jìn)來。
  • sass 類的前端應(yīng)用,業(yè)務(wù)比較復(fù)雜,可能模塊很多,希望能拆分成多個(gè)應(yīng)用獨(dú)立維護(hù),也能夠集成到一起。

跨技術(shù)棧的應(yīng)用集成、大的項(xiàng)目拆分成獨(dú)立的小項(xiàng)目,這些是微前端解決的典型問題。

微前端的實(shí)現(xiàn)方案有很多,比較流行的是 single-spa 以及對(duì)它做了一層封裝的 qiankun。

今天我們就來了解下這兩個(gè)微前端實(shí)現(xiàn)方案:

single-spa

微前端的基本需求就是在 url 變化的時(shí)候,加載、卸載對(duì)應(yīng)的子應(yīng)用,single spa 就實(shí)現(xiàn)了這個(gè)功能。

它做的事情就是注冊(cè)微應(yīng)用、監(jiān)聽 URL 變化,然后激活對(duì)應(yīng)的微應(yīng)用:

圖片

注冊(cè)一個(gè)微應(yīng)用是這樣的:

import { registerApplication } from 'single-spa';
registerApplication({
name: 'app',
app: () => {
loadScripts('./chunk-a.js');
loadScripts('./chunk-b.js');
return loadScripts('./entry.js')
}
activeWhen: '/appName'
})

singleSpa.start()

要指定當(dāng) url 是什么的時(shí)候,去加載子應(yīng)用,怎么加載。

它要求子應(yīng)用的入口文件導(dǎo)出 bootstrap、mount、unmount 的生命周期函數(shù),也就是在加載完成、掛載前、卸載前執(zhí)行的邏輯。

比如 react 的子應(yīng)用:

import React from 'react';
import ReactDOM from 'react-dom';
import App from './index.tsx'
export const bootstrap = () => {}
export const mount = () => {
ReactDOM.render(<App/>, document.getElementById('root'));
}
export const unmount = () => {}

這部分邏輯還可以簡(jiǎn)化,single-spa 提供了和 react、vue、angular 等集成的包,可以直接用:

import React from 'react';
import ReactDOM from 'react-dom';
import App from './index.tsx';
import singleSpaReact from 'single-spa-react';
const reactLifecycles = singleSpaReact({
React,
ReactDOM,
rootComponent: App
});

export const bootstrap = reactLifecycles.bootstrap;
export const mount = reactLifecycles.mount;
export const unmount = reactLifecycles.unmount;

這就是完成了微前端的基本需求,能夠在 url 變化的時(shí)候,加載、卸載對(duì)應(yīng)的子應(yīng)用。

但是 single spa 做的事情比較簡(jiǎn)單,不夠完善,比如說:

  • 加載微應(yīng)用的時(shí)候要指定加載哪些 js、css,如果子應(yīng)用的打包邏輯發(fā)生了變化,這里也要跟著變
  • 一個(gè)頁(yè)面可能有多個(gè)子應(yīng)用,之間會(huì)不會(huì)有樣式的沖突、JS 的沖突?
  • 多個(gè)子應(yīng)用之間通信怎么處理?

這些都要使用 sigle-spa 的時(shí)候,自己去解決。

所以說 single-spa 并不夠完善,于是 qiankun 就出來了:

qiankun

qiankun 并不是新的微前端框架,它只是解決了 single-spa 沒解決的一些問題,是更完善的基于 single-spa 的微前端方案。

它解決了哪些問題呢?

我們一個(gè)個(gè)來看一下:

加載子應(yīng)用的資源的方式

用 single-spa 的時(shí)候,要在注冊(cè)的時(shí)候指定如何加載子應(yīng)用:

import { registerApplication } from 'single-spa';
registerApplication({
name: 'app',
app: () => {
loadScripts('./chunk-a.js');
loadScripts('./chunk-b.js');
return loadScripts('./entry.js')
}
activeWhen: '/appName'
})

一般我們會(huì)結(jié)合 SystemJS 來用,簡(jiǎn)化加載的邏輯,但是依然要知道子應(yīng)用有哪些資源要加載,子應(yīng)用打包邏輯變了,這里加載的方式就要跟著變。

能不能把這個(gè)加載過程給自動(dòng)化了呢?

比如我根據(jù) url 加載子應(yīng)用的 html,然后解析出其中的 JS、CSS,自動(dòng)去加載。

qiankun 就是按照這個(gè)思路來解決的:

它會(huì)加載入口 html,解析出 scripts、styles 的部分,單獨(dú)去加載,而其余的部分,會(huì)做一些轉(zhuǎn)換之后放到 dom 里。

比如這樣一段 html:

圖片

qiankun 會(huì)把 head 部分轉(zhuǎn)換成 qiankun-head,把 script 部分提取出來自己加載,其余部分放到 html 里:

圖片

這樣也就不再需要開發(fā)者指定怎么去加載子應(yīng)用了,實(shí)現(xiàn)了解析 html 自動(dòng)加載的功能。

這個(gè)功能的實(shí)現(xiàn)放在 import-html-entry 這個(gè)包里。

single-spa 的實(shí)現(xiàn)叫做 Config Entry 或者 JS Entry,也就是要自己指定怎么加載子應(yīng)用,而 qiankun 這種叫做 Html Entry,會(huì)自動(dòng)解析 html 實(shí)現(xiàn)加載。

所以說,注冊(cè) qiankun 應(yīng)用的時(shí)候就更簡(jiǎn)單了一點(diǎn),只要指定 html 的地址就行:

import { registerMicroApps, start } from 'qiankun';
registerMicroApps([
{
name: 'vue app',
entry: '//localhost:7100',
container: '#container-vue',
activeRule: '/micro-vue'
},
{
name: 'react app',
entry: '//localhost:7101',
container: '#container-react',
activeRule: '/micro-react'
},
]);

start();

而且 qiankun 還支持預(yù)加載,會(huì)在空閑的時(shí)候加載解析出的 script 和 style:

圖片

除了實(shí)現(xiàn)了基于 html 的自動(dòng)加載,qiankun 還實(shí)現(xiàn)了 JS 和 CSS 的沙箱:

JS、CSS 沙箱

子應(yīng)用之間肯定要實(shí)現(xiàn)隔離,不能相互影響,也就是要實(shí)現(xiàn) JS 和 CSS 的隔離。

single-spa 沒有做這方面的處理,而 qiankun 實(shí)現(xiàn)了這個(gè)功能。

JS 的隔離也就是要隔離 window 這個(gè)全局變量,其余的不會(huì)有啥沖突,本來就是在不同函數(shù)的作用域執(zhí)行的。

qiankun 實(shí)現(xiàn) window 隔離有三種思路:

  • 快照,加載子應(yīng)用前記錄下 window 的屬性,卸載之后恢復(fù)到之前的快照。
  • diff,加載子應(yīng)用之后記錄對(duì) window 屬性的增刪改,卸載之后恢復(fù)回去。
  • Proxy,創(chuàng)建一個(gè)代理對(duì)象,每個(gè)子應(yīng)用訪問到的都是這個(gè)代理對(duì)象。

這幾個(gè)實(shí)現(xiàn)思路都比較容易理解。

前兩種思路有個(gè)問題,就是不能同時(shí)存在多個(gè)子應(yīng)用,不然會(huì)沖突。一般常用的還是第三種 Proxy 的思路。

在 qiankun 里有這樣的策略選擇邏輯:

圖片

當(dāng)支持 Proxy,并且傳入的配置沒設(shè)置 loose,就會(huì)使用 Proxy 的方式。

而 CSS 的隔離就是使用 shadow dom 了,這是瀏覽器支持的特性,shadow root 下的 dom 的樣式是不會(huì)影響其他 dom 的。

當(dāng)然,也有另一種策略,就是 scoped css 的思路,在 css 選擇器里加一個(gè)前綴,并且在 dom 上也加一個(gè) ID。

不過這種還是實(shí)現(xiàn)性的,需要手動(dòng)開啟:

圖片

在源碼里可以看到這兩種方式:

圖片

總之,JS、CSS 的隔離都有多種方案,可以通過配置來選擇。

此外,qiankun 還內(nèi)置了應(yīng)用間狀態(tài)管理的方案:

應(yīng)用間的狀態(tài)管理

多個(gè)子應(yīng)用、子應(yīng)用和主應(yīng)用之間自然有一些狀態(tài)管理的需求,qiankun 也實(shí)現(xiàn)了這個(gè)功能。

使用起來是這樣的:

主應(yīng)用里做全局狀態(tài)的初始化,定義子應(yīng)用獲取全局狀態(tài)的方法 getGlobalState 和全局狀態(tài)變化時(shí)的處理函數(shù) onGlobalStateChange:

import { initGlobalState } from 'qiankun'
const initialState = {
user: {
name: 'guang'
}
}
const actions = initGlobalState(initialState)
actions.onGlobalStateChange((newState, prev) => {
for (const key in newState) {
initialState[key] = newState[key]
}
})
actions.getGlobalState = (key) => {
return key ? initialState[key] : initialState
}
export default actions

子應(yīng)用里可以通過參數(shù)拿到 global state 的 get、set 方法:

export async function mount(props) {
const globalState = props.getGlobalState();
props.setGlobalState({user: {name: 'dong'}})

}

綜上,其實(shí) qiankun 就是更完善一些的 signle-spa,通過 html entry 的方式解決了要手動(dòng)加載子應(yīng)用的各種資源的麻煩,通過沙箱實(shí)現(xiàn)了 JS、CSS 的隔離,還實(shí)現(xiàn)了全局的狀態(tài)管理機(jī)制。

子應(yīng)用里大概這樣寫:

import React from 'react';
import ReactDOM from 'react-dom';
import App from './App';
if (window.__POWERED_BY_QIANKUN__) {
__webpack_public_path__ = window.__INJECTED_PUBLIC_PATH_BY_QIANKUN__;
}
function render(props) {
const { container } = props;
ReactDOM.render(<App />, container ? container.querySelector('#root') : document.querySelector('#root'));
}

if (!window.__POWERED_BY_QIANKUN__) {
render({});
}
export async function bootstrap() {
console.log('[react16] react app bootstraped');
}
export async function mount(props) {
props.onGlobalStateChange((value, prev) => console.log(`[onGlobalStateChange - ${props.name}]:`, value, prev), true);
props.setGlobalState({
ignore: props.name,
user: {
name: props.name,
},
});
render(props);
}
export async function unmount(props) {
const { container } = props;
ReactDOM.unmountComponentAtNode(container ? container.querySelector('#root') : document.querySelector('#root'));
}

qiankun 會(huì)在跑子應(yīng)用之前在 window 沙箱設(shè)置 POWERED_BY_QIANKUN 的變量,如果有這個(gè)變量就不要直接渲染,在 mount 生命周期里做渲染,否則就直接渲染。

還要指定靜態(tài)資源的加載地址,通過 webpack_public_path 的全局變量。

其余的就和 single-spa 差不多了。

總結(jié)

前端應(yīng)用能夠單獨(dú)跑,也能被集成到另一個(gè)應(yīng)用中跑,這種架構(gòu)叫做微前端架構(gòu)。它在解決跨技術(shù)棧的應(yīng)用集成、大項(xiàng)目拆分的場(chǎng)景下是很有用的。

主流的微前端方案是 single-spa 以及基于 single-spa 的 qiankun:

single-spa 實(shí)現(xiàn)了路由切換的時(shí)候,對(duì)子應(yīng)用的加載、卸載。

但是它不夠完善,沒有解決資源加載、沙箱、全局狀態(tài)管理的問題,而 qiankun 做的更完善了一些:

  • 基于 html 自動(dòng)分析 js、css,自動(dòng)加載,不需要開發(fā)者手動(dòng)指定如何加載。
  • 基于快照、Proxy 的思路實(shí)現(xiàn)了 JS 隔離,基于 Shadow Dom 和 scoped css 的思路實(shí)現(xiàn)了 CSS 隔離。
  • 提供了全局狀態(tài)管理的機(jī)制。

所以說,qiankun 基于 single-spa,使用方式差不多,但是各方面的功能更完善一些。

責(zé)任編輯:姜華 來源: 神光的編程秘籍
點(diǎn)贊
收藏

51CTO技術(shù)棧公眾號(hào)