最新国产好看的视频,伊人天堂AV在线,国产Aaaaaa视频,蜜臀视频在线观看一区,人妻av色图,密臀久久久精品影片,青青视频免费观看毛片,久草在线观看视,国产三级精品色情在线

qiankun 找不到入口問題徹底解決

 更新時(shí)間:2022年08月30日 14:56:02   作者:寫代碼的海怪  
這篇文章主要為大家介紹了qiankun 找不到入口問題徹底解決,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進(jìn)步,早日升職加薪

前言

嗨害嗨,好久不見,我是海怪。

有一陣子沒寫文章了,今天來更一期關(guān)于 qiankun 找不到生命周期的問題。

剛開始給項(xiàng)目接入 qiankun 的時(shí)候,時(shí)不時(shí)就會報(bào)

Application died in status LOADING_SOURCE_CODE: You need to export the functional lifecycles in xxx entry:

開發(fā)的時(shí)候一切正常,只有在打包發(fā)布后才會報(bào)這個(gè) Bug,讓人非常惱火。相信有不少同學(xué)也遇到過這個(gè)問題,今天就來分享一下這個(gè)問題的思考和解決方案吧。

為什么要找生命周期

首先,我們要知道為什么 qiankun 加載微應(yīng)用時(shí)要找生命周期鉤子。

早在 qiankun 出來前,已經(jīng)有一個(gè)微前端框架 single-spa 了。

它的思想是:無論 React、Vue 還是 Angular,項(xiàng)目打包最終的產(chǎn)物都是 JS。如果在 合適的時(shí)機(jī) 以 某種執(zhí)行方式 去執(zhí)行微應(yīng)用的 JS 代碼,大概就能實(shí)現(xiàn) 主-微 結(jié)構(gòu)的微前端開發(fā)了。

這里有兩個(gè)關(guān)鍵詞:合適的時(shí)機(jī) 和 執(zhí)行方式。對于前者,single-spa 參考了單頁應(yīng)用(Single Page Application)的思路,也希望用生命周期來管理微應(yīng)用的 bootstrap, mount, update, unmount。而對于后者,則需要開發(fā)者自己實(shí)現(xiàn)執(zhí)行微應(yīng)用 JS 的方式。

總的來說,開發(fā)者需要在微應(yīng)用的入口文件 main.js 里寫好生命周期實(shí)現(xiàn):

export async function bootstrap() {
  // 啟動微應(yīng)用
}
export async function mount() {
  // 加載微應(yīng)用
}
export async function unmount() {
  // 卸載微應(yīng)用
}
export async function update() {
  // 更新微應(yīng)用
}

single-spa 會自動劫持和監(jiān)聽網(wǎng)頁地址 URL 的變化,在命中路由規(guī)則后,執(zhí)行這些生命周期鉤子,從而實(shí)現(xiàn)微應(yīng)用的加載、卸載和更新。

但這就有一個(gè)嚴(yán)重的問題了:一般我們項(xiàng)目的入口文件就只有:

React.render(<App/>, document.querySelector('#root'))

這要如何和主應(yīng)用交互呢?而且里面的樣式、全局變量隔離又要怎么實(shí)現(xiàn)呢?Webpack 又該如何改造呢?然而,single-spa 只提供了生命周期的調(diào)度,并沒有解決這一系列問題。

既然前人解決不了,后人則可以基于原有框架繼續(xù)優(yōu)化,這就是 qiankun。

qiankun 和 single-spa 最大的不同是:qiankun 是 HTML 入口。它的原理如圖所示:

可以看到 qiankun 自己實(shí)現(xiàn)了一套通過 HTML 地址加載微應(yīng)用的機(jī)制,但對于 “要在什么時(shí)候執(zhí)行 JS” 依然用了 single-spa 的生命周期調(diào)度能力。

這就是為什么微應(yīng)用的入口文件 main.js 依然需要提供 single-spa 的生命周期回調(diào)。

如何找入口

現(xiàn)在我們來聊聊如何找入口的問題。

對于一個(gè)簡單的 SPA 項(xiàng)目來說,一個(gè) <div id="app"></div> + 一個(gè) main.js 就夠了,入口很好找。

但真實(shí)項(xiàng)目往往會做分包拆包、自動注入 <script> 腳本等操作,使得最終訪問的 HTML 會有多個(gè) <script> 標(biāo)簽:

<script>
  // 初始化 XX SDK
</script>
<body>
  ...
</body>
<script src="你真實(shí)的入口 main.js"></script>
<script src="ant-design.js"></script>
<script>
  // 打包后自動注入的靜態(tài)資源 retry 邏輯
</script>
<script>
  // 公司代碼網(wǎng)關(guān)自動注入的 JS 邏輯
</script>

對于這樣復(fù)雜的情況,qiankun 提供了 2 種定位入口的方式:

  • 找 帶有 entry 屬性的 <script entry src="main.js"></script>
  • 如果找不到,那么把 最后一個(gè) <script> 作為入口

第一種方法是最穩(wěn)妥的,可以使用 html-webpack-inject-attributes-plugin 這個(gè) Webpack 插件,在打包的時(shí)候就給入口 main.js 添加 entry 屬性:

plugins = [
    new HtmlWebpackPlugin(),
    new htmlWebpackInjectAttributesPlugin({
        entry: "true",
    })
]

不推薦大家使用最后一種方法來確定入口,這種方式很不可靠。 因?yàn)槲?yīng)用 HTML 有可能在一些公司代理、網(wǎng)關(guān)層中被攔截,自動注入一些腳本。

這樣最終拿到 HTML 里最后的一個(gè) <script> 就不是原先的入口 main.js 文件了:

<script src="你真實(shí)的入口 main.js"></script>
<script>
  // 自動注入的網(wǎng)關(guān)層的代理邏輯
</script>

兜底找入口

上面兩種找入口方式并不能 100% 覆蓋所有情況,比如我就遇到過這樣的場景:

  • 腳手架封裝得太黑盒,導(dǎo)致添加插件不生效,無法在打包時(shí)注入 entry 屬性
  • 測試環(huán)境中,代理工具會自動往 HTML 插入 <script>,無法將最后一個(gè) JS 作為入口

這下 qiankun 徹底找不到我的入口了。你總不能說:手寫一個(gè) JS 腳本,然后每次打包后用正則去 replace HTML,以此來添加 entry 屬性吧???

當(dāng)然不行!

曾經(jīng)我在 qiankun 的文檔里看到過這段配置:

module.exports = {
  webpack: (config) => {
    config.output.library = `microApp`;
    config.output.libraryTarget = 'umd';
    config.output.jsonpFunction = `webpackJsonp_${name}`;
    config.output.globalObject = 'window';
    return config;
  },
  ...
};

文檔里說這是一個(gè)兜底找入口的邏輯:

但文檔沒有說這里的細(xì)節(jié),下面就來一起研究一下。

微應(yīng)用的 Webpack 配置

libraryTarget 指定打包成 umd 格式,也即最終模塊會兼容 CommonJS 和 AMD 等多種格式來進(jìn)行導(dǎo)出,最終 main.js 會是這樣:

(function webpackUniversalModuleDefinition(root, factory) {
  // CommonJS 導(dǎo)出
  if (typeof exports === 'object' && typeof module === 'object')
    module.exports = factory(require('lodash'));
  // AMD 導(dǎo)出
  else if (typeof define === 'function' && define.amd)
    define(['lodash'], factory);
  // 另一種導(dǎo)出
  else if (typeof exports === 'object')
    exports['microApp'] = factory(require('lodash'));
  // 關(guān)鍵點(diǎn)
  else root['microApp'] = factory(root['_']);
})(this, function (__WEBPACK_EXTERNAL_MODULE_1__) {
  // 入口文件的內(nèi)容
  // ...
  return {
    bootstrap() {},
    mount() {},
    // ...
  }
});

直接看最后一種導(dǎo)出方式 root['microApp'] = factory(root['_'])。Webpack 配置的 globalObjectlibrary 正好對應(yīng)了里面的 root 以及 'microApp'

而且上面的函數(shù) factory 則是入口文件的執(zhí)行函數(shù),理論上當(dāng)執(zhí)行 factory() 后會返回模塊的輸出。

最終的效果是:Webpack 會把入口文件的輸出內(nèi)容掛在到 globalObject[library]/window['microApp'] 上:

window['microApp'] = {
  // main.js 所 export 的內(nèi)容
  bootstrap() {},
  mount() {},
  unmount() {},
  update() {},
  // ...
}

主應(yīng)用的兜底邏輯

把入口的內(nèi)容掛載到 window 上有什么好處呢?我們來稍微看點(diǎn)源碼:

// 發(fā) Http 請求獲取 HTML, JS 執(zhí)行器
const { template, execScripts, assetPublicPath } = await importEntry(entry, importEntryOpts);
// 執(zhí)行微應(yīng)用的 JS,但這里不一定有入口
const scriptExports: any = await execScripts(global, sandbox && !useLooseSandbox);
// 獲取入口導(dǎo)出的生命周期
const { bootstrap, mount, unmount, update } = getLifecyclesFromExports(
  scriptExports,
  appName,
  global,
  sandboxContainer?.instance?.latestSetProp,
);

上面的代碼很簡單,就是獲取微應(yīng)用 HTML 和 JS,試圖從里面獲取生命周期,所以下面我們來看看 getLifecyclesFromExports 做了什么:

function getLifecyclesFromExports(
  scriptExports: LifeCycles<any>,
  appName: string,
  global: WindowProxy,
  globalLatestSetProp?: PropertyKey | null,
) {
  // 如果在獲取微應(yīng)用的 JS 時(shí)可以鎖定入口文件,那么直接返回
  if (validateExportLifecycle(scriptExports)) {
    return scriptExports;
  }
  // 不用看
  if (globalLatestSetProp) {
    const lifecycles = (<any>global)[globalLatestSetProp];
    if (validateExportLifecycle(lifecycles)) {
      return lifecycles;
    }
  }
  // 獲取 globalObject[library] 里的內(nèi)容
  const globalVariableExports = (global as any)[appName];
  // 判斷 globalObject[library] 里的內(nèi)容是否為生命周期
  // 如果是合法生命周期,那么直接返回
  if (validateExportLifecycle(globalVariableExports)) {
    return globalVariableExports;
  }
  throw new QiankunError(`You need to export lifecycle functions in ${appName} entry`);
}

從上面可以看到,在 getLifecyclesFromExports 最后會試圖從 windowProxy[微應(yīng)用名] 中拿導(dǎo)出的生命周期。

這也是為什么兜底找入口操作需要微應(yīng)用配置 Webpack,同時(shí)主應(yīng)用指定的微應(yīng)用名要和 library 名要一樣。

注意:qiankun 會使用 JS 沙箱來隔離微應(yīng)用的環(huán)境,所以這里的 globalObject 并不是 window 而是微應(yīng)用對應(yīng)的沙箱對象 windowProxy。

在微應(yīng)用里寫 console.log(window['microApp']) 或在主應(yīng)用里輸入 console.log(window.proxy['microApp']) 即可看到微應(yīng)用導(dǎo)出的生命周期:

因此,在主應(yīng)用中注冊微應(yīng)用的時(shí)候,微應(yīng)用 name 最好要和 Webpack 的 output.library 一致,這樣才能命中 qiankun 的兜底邏輯。

總結(jié)

最后總結(jié)一下,qiankun 要找入口是因?yàn)橐獜闹心玫缴芷诨卣{(diào),把它們給 single-spa 做調(diào)度。

qiankun 支持 2 種找入口的方式:

  • 正則匹配 帶有 entry 屬性的 <script> ,找到就把這個(gè) JS 作為入口
  • 當(dāng)找不到時(shí),默認(rèn)把 最后一個(gè) JS 作為入口

如果這兩種方法都無法幫你正確定位入口,那么你需要:

  • 在微應(yīng)用配置 library, libraryTarget 以及 globalObject,把入口導(dǎo)出的內(nèi)容掛載到 window
  • 加載微應(yīng)用時(shí),主應(yīng)用會試著從 window[library] 找微應(yīng)用的生命周期回調(diào),找到后依然能正常加載
  • 在主應(yīng)用注冊微應(yīng)用時(shí),要把微應(yīng)用的 name 和 Webpack 的 output.library 設(shè)為一致,這樣才能命中第二步的邏輯

最后還要注意的是,上面說到的 window 并不是全局對象,而是 qiankun 提供的 JS 沙箱對象 windowProxy

以上就是qiankun 找不到入口問題徹底解決的詳細(xì)內(nèi)容,更多關(guān)于qiankun 找不到入口的資料請關(guān)注腳本之家其它相關(guān)文章!

相關(guān)文章

  • 手寫實(shí)現(xiàn)JS中的new

    手寫實(shí)現(xiàn)JS中的new

    這篇文章主要介紹JS中的new,new 運(yùn)算符創(chuàng)建一個(gè)用戶定義的對象類型的實(shí)例或具有構(gòu)造函數(shù)的內(nèi)置對象的實(shí)例。下面我們一起來看看我呢很臟具體內(nèi)容的詳細(xì)介紹,需要的朋友可以參考一下
    2021-11-11
  • javascript 判斷是否是微信瀏覽器的方法

    javascript 判斷是否是微信瀏覽器的方法

    這篇文章主要介紹了javascript 判斷是否是微信瀏覽器的方法的相關(guān)資料,需要的朋友可以參考下
    2016-10-10
  • JavaScript手寫異步加法asyncAdd方法詳解

    JavaScript手寫異步加法asyncAdd方法詳解

    這篇文章主要為大家介紹了JavaScript手寫異步加法asyncAdd方法詳解,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進(jìn)步,早日升職加薪
    2022-08-08
  • 詳解微信小程序 wx.uploadFile 的編碼坑

    詳解微信小程序 wx.uploadFile 的編碼坑

    編寫微信小程序時(shí),用到 wx.uploadFile,用來上傳圖片+文本信息,本篇文章主要介紹了微信小程序 wx.uploadFile 的編碼坑,有興趣的可以了解一下。
    2017-01-01
  • 微信小程序教程之本地圖片上傳(leancloud)實(shí)例詳解

    微信小程序教程之本地圖片上傳(leancloud)實(shí)例詳解

    這篇文章主要介紹了微信小程序教程之本地圖片上傳(leancloud)實(shí)例詳解的相關(guān)資料,這里舉例說明該如何實(shí)現(xiàn)和實(shí)例代碼,文章一一表述,需要的朋友可以參考下
    2016-11-11
  • 微信小程序 scroll-view隱藏滾動條詳解

    微信小程序 scroll-view隱藏滾動條詳解

    這篇文章主要介紹了微信小程序 scroll-view隱藏滾動條和跳轉(zhuǎn)頁面的相關(guān)資料,需要的朋友可以參考下
    2017-01-01
  • 微信小程序?qū)崿F(xiàn)給循環(huán)列表添加點(diǎn)擊樣式實(shí)例

    微信小程序?qū)崿F(xiàn)給循環(huán)列表添加點(diǎn)擊樣式實(shí)例

    這篇文章主要介紹了微信小程序?qū)崿F(xiàn)給循環(huán)列表添加點(diǎn)擊樣式實(shí)例的相關(guān)資料,需要的朋友可以參考下
    2017-04-04
  • 微信小程序template模板實(shí)例詳解

    微信小程序template模板實(shí)例詳解

    這篇文章主要介紹了微信小程序template模板使用的相關(guān)資料,希望通過本文能幫助到大家,讓大家理解掌握這樣的功能,需要的朋友可以參考下
    2017-10-10
  • 微信小程序 Windows2008 R2服務(wù)器配置TLS1.2方法

    微信小程序 Windows2008 R2服務(wù)器配置TLS1.2方法

    微信小程序免費(fèi)SSL證書https、TLS版本問題的解決方案《二十四》request:fail錯(cuò)誤(含https解決方案)(真機(jī)預(yù)覽問題把下面的代碼復(fù)制到PowerShell里運(yùn)行一下,然后重啟服務(wù)器。# Enables TLS 1.2 on ...,需要的朋友可以參考下
    2016-12-12
  • 微信小程序 教程之WXSS

    微信小程序 教程之WXSS

    這篇文章主要介紹了微信小程序 WXSS的相關(guān)資料,并附簡單實(shí)例代碼,需要的朋友可以參考下
    2016-10-10

最新評論

青海省| 芦山县| 大名县| 获嘉县| 缙云县| 宝山区| 卢氏县| 漳州市| 伊金霍洛旗| 清新县| 内江市| 湖口县| 罗城| 巨鹿县| 赣州市| 鄂伦春自治旗| 新蔡县| 延庆县| 新乡县| 呼图壁县| 温宿县| 襄城县| 林口县| 凤山市| 高州市| 涞源县| 清水河县| 台前县| 仁化县| 观塘区| 丹江口市| 古交市| 乐昌市| 临澧县| 黔南| 义乌市| 石台县| 彭山县| 通山县| 庄浪县| 舒兰市|