主要研究對象qiankun,探究內部原理css
$ yarn add qiankun # 或者 npm i qiankun -S
複製代碼
registerMicroApps([
{
name: 'reactApp',
entry: '//localhost:3000',
container: '#container',
activeRule: '/views/app-react',
},
{
name: 'vueApp',
entry: '//localhost:8080',
container: '#container',
activeRule: '/views/app-vue',
},
{
name: 'reactApp2',
entry: 'http://localhost:9100/react-app2/template/index.html',
container: '#container',
activeRule: '/views/app-react2',
}
]);
// 啓動 qiankun
start();
複製代碼
當微應用信息註冊完以後,一旦瀏覽器的 url 發生變化,便會自動觸發 qiankun 的匹配邏輯,全部 activeRule 規則匹配上的微應用就會被插入到指定的 container 中,同時依次調用微應用暴露出的生命週期鉤子。若是子應用還存在子路由,則須要配置子應用路由的basenamehtml
微應用須要在本身的入口 js (一般就是 webpack 的 entry js) 導出 bootstrap、mount、unmount 三個生命週期鉤子,以供主應用在適當的時機調用前端
/** * bootstrap 只會在微應用初始化的時候調用一次,下次微應用從新進入時會直接調用 mount 鉤子,不會再重複觸發 bootstrap。 * 一般咱們能夠在這裏作一些全局變量的初始化,好比不會在 unmount 階段被銷燬的應用級別的緩存等。 */
export async function bootstrap() {
console.log('react app bootstraped');
}
/** * 應用每次進入都會調用 mount 方法,一般咱們在這裏觸發應用的渲染方法 */
export async function mount(props) {
ReactDOM.render(<App />, props.container ? props.container.querySelector('#root') : document.getElementById('root'));
}
/** * 應用每次 切出/卸載 會調用的方法,一般在這裏咱們會卸載微應用的應用實例 */
export async function unmount(props) {
ReactDOM.unmountComponentAtNode(props.container ? props.container.querySelector('#root') : document.getElementById('root'));
}
/** * 可選生命週期鉤子,僅使用 loadMicroApp 方式加載微應用時生效 */
export async function update(props) {
console.log('update props', props);
}
複製代碼
public-path.js
文件,並在入口文件最頂部引用,用於修改運行時的 publicPathconst packageName = require('./package.json').name;
module.exports = {
output: {
library: `${packageName}-[name]`,
libraryTarget: 'umd',
jsonpFunction: `webpackJsonp_${packageName}`,
},
};
複製代碼
關於
public-path
的描述能夠參見此文檔:webpack.docschina.org/guides/publ…vue關於
library
的描述能夠參見此文檔:webpack.docschina.org/guides/auth…react
single-spa主要功能是實現了路由劫持與應用加載,但未實現js、css隔離webpack
在研究qiankun的原理以前,咱們應該試着提出一些問題,而後在閱讀源碼的過程當中去尋找答案。對於qiankun,咱們比較關心的問題有:git
qiankun
與single-spa
是什麼關係?qiankun
如何實現js沙箱?qiankun
如何實現css隔離?配置微應用的第一步就是在主應用中註冊子應用,配置好子應用的名稱、入口、dom容器以及喚醒條件,以後qiankun
會按照喚醒條件將子應用掛載到dom容器上。github
Array<RegistrableApp>
- 必選,微應用的一些註冊信息LifeCycles
- 可選,全局的微應用生命週期鉤子string
- 必選,微應用的名稱,微應用之間必須確保惟一string | { scripts?: string[]; styles?: string[]; html?: string }
- 必選,微應用的入口string | HTMLElement
- 必選,微應用的容器節點的選擇器或者 Element
實例。如container: '#root'
或 container: document.querySelector('#root')
string | (location: Location) => boolean | Array<string | (location: Location) => boolean>
- 必選,微應用的激活規則(loading: boolean) => void
- 可選,loading
狀態發生變化時會調用的方法object
- 可選,主應用須要傳遞給微應用的數據Lifecycle | Array<Lifecycle>
- 可選Lifecycle | Array<Lifecycle>
- 可選Lifecycle | Array<Lifecycle>
- 可選Lifecycle | Array<Lifecycle>
- 可選Lifecycle | Array<Lifecycle>
- 可選import { registerMicroApps } from 'qiankun';
registerMicroApps([{
name: 'app1',
entry: '//localhost:8080',
container: '#container',
activeRule: '/react',
props: {
name: 'kuitos'
}
}], {
beforeLoad: app => console.log('before load', app.name),
beforeMount: [
app => console.log('before mount', app.name)
]
});
複製代碼
single-spa
的registerApplication
方法,此處主要工做在registerApplication
的app
參數中處理:registerApplication({
name,
app: async () => {
loader(true);
await frameworkStartedDefer.promise;
const { mount, ...otherMicroAppConfigs } = (
await loadApp({ name, props, ...appConfig }, frameworkConfiguration, lifeCycles)
)();
return {
mount: [async () => loader(true), ...toArray(mount), async () => loader(false)],
...otherMicroAppConfigs,
};
},
activeWhen: activeRule,
customProps: props,
});
複製代碼
其中loadApp
是核心,從新包裝了single-spa
所需applicat
的mount
、unmount
方法,在這兩個生命週期中分別加入了一些額外的操做。如在mount
的時候,會執行beforeMount
、afterMount
回調,以及mountSandbox
開啓沙箱。在unmount
的時候,會執行beforeUnmount
、afterUnmount
回調,以及unmountSandbox
釋放沙箱。web
qiankun是如何實現沙箱的?npm
經過調用createSandboxContainer
生成一個沙箱,若是當前瀏覽器環境支持Proxy
則採用Proxy
,反之則採用快照方式。對於Proxy
模式來講,首先調用createFakeWindow(rawWindow)
生成一個原始window
的複製版fakeWindow
執行用戶調用registerApplication
時傳入的app
方法,加載應用
import-html-entry
模塊的importEntry
函數,獲取到對應子應用的html文件、可執行腳本文件以及publicpathgetDefaultTplWrapper
將子應用的html內容用div
標籤包裹起來createElement
函數生成剔除html、body、head標籤後的子應用html內容(經過innerHTML
達到過濾效果)getRender
函數獲得render函數getAppWrapperGetter
函數,生成一個能夠獲取處理過的子應用dom元素的函數initialAppWrapperGetter
,以備後續使用子應用dom元素sandbox
爲true,則調用createSandboxContainer
函數execScripts
函數,執行子應用腳本getMicroAppStateActions
函數,獲取onGlobalStateChange
、setGlobalState
、offGlobalStateChange
,用於主子應用傳遞信息parcelConfigGetter
函數,包裝mount
和unmount
{name,loadApp,activeWhen,customProps}
appName
是否重名,重名則拋出異常application
配置推入到全局數組apps
中保存jQuery
的前提早下window.jQuery
可以使用reroute
函數,並進行下列操做:
apps
數組,獲取到NOT_MOUNTED
、MOUNTED
、LOADING_SOURCE_CODE
的application
start
方法,若是已經執行了(例如在頁面上切換路由),則執行performAppChanges
函數,反之執行loadApps
函數(首次刷新頁面時)利用Promise.resolve().then()
將函數內的所有代碼異步執行
window.dispatchEvent
和CustomEvent
觸發single-spa:before-no-app-change
或single-spa:before-app-change
以及single-spa:before-routing-event
事件(這兩個接口IE不支持)app
的mount
和unmount
操做(具體操做有待進一步研究)啓動 qiankun,能夠傳入一些資源獲取方式以及是否採用沙箱等配置
single-spa
的start方法,執行reroute
或setUrlRerouteOnly
函數