前言
近來,微前端的概念非?;鸨?,那么什么是微前端架構?微前端架構是一種架構風格類似于微服務的架構,它將微服務的理念應用于瀏覽器端,即將 Web 應用由單一的單體應用轉變為多個小型前端應用聚合為一的應用。由此帶來的變化是,這些前端應用可以獨立運行、獨立開發、獨立部署。微前端所具備的便于引入新框架,代碼簡潔、易維護等特點使其應用愈發廣泛。
愛奇藝號前端工程基于Vue的框架已經使用了3年之余,這一版本雖有不錯的可擴展性,但隨著接入的業務越來越多,項目復雜度和代碼行數增長,現有框架需要將框架與業務更加的解耦以方便其他團隊進行維護,同時也需要模塊的獨立部署、發布等提高迭代效率?;诜N種原因,愛奇藝號技術團隊基于Vue定制開發了微前端框架,此文從偏向實踐的方面來簡要介紹這套框架的實現原理。
容器應用
首先需要一個容器應用,這個容器應用需要有最基本的代碼架構,比如Vue、主路由、Vuex及其他基礎的通用代碼,但需要將這些通用代碼中任何業務相關的部分剝離。簡單來說,容器應用是一個基于Vue的框架,其作用是將其他分散的各個微前端模塊通過頁面路由攢在一起,再組成一個個的頁面。
除了基本的Vue啟動應用等功能,微前端容器的架構邏輯需要做到下面幾點:
1、將微前端模塊需要的通用代碼綁定至全局函數;
2、基于請求的url獲取相對應的微前端模塊部署的js manifest文件地址列表(列表manifest中包含業務自己定義的路由、js模塊的地址列表);
3、通過匹配微前端模塊中定義的路由獲取頁面所需的微前端模塊;
4、加載每個微前端模塊的具體js文件地址并將其渲染。
下面我們來依次對容器應用中各個部分進行介紹:
- App.js
這一部分用于進行Vue的初始化的相關邏輯。與正常Vue初始化邏輯相比,這里唯一不同的是需要將一些其他微前端模塊必需的通用組件綁定到全局變量中(此處用window,也可以自己定義一個全局變量),比如:
· Vue:其他微前端模塊都需要使用容器應用提供的Vue包初始化,而非每個微前端模塊自己在package.json中引入Vue,這樣會嚴重增加js包大小,拖累頁面性能;
· Router:為了讓微前端能夠無縫跳轉其他頁面,統一使用全局路由,在子模塊中定義的路由將會在加載頁面時實時合并到主路由中;
· Store:很多不同小模塊、大微前端模塊之間的交互可以使用全局通用的Store;
· RenderPage模塊:給其他微前端模塊路由渲染頁面使用的component(后文會詳細講這個模塊);
· 其他的一些小模塊,比如用戶信息管理模塊等.
在框架中,為了方便子模塊的運行,全部通用組件被綁定到window下,如window.mp.Vue等,這為子模塊按需取用提供便利。
- hosts配置
因微前端容器應用中不引用任何子模塊的依賴,需要直接通過正常url訪問獲取子模塊的js代碼,所以容器應用需要通過自定義的配置文件來獲得每個子模塊所在的服務器host,以便拼湊出需要加載的js具體路徑。每當增加一個新的模塊,就需要在這里添加模塊部署的相應地址,否則容器應用不會成功解析。
例如:

配置中可以包含多個不同服務器環境的地址,在不同的環境中自動使用相對應的url。值得注意的是,本地環境build中使用localhost加port的地址,方便本地啟動調試模塊。本地啟動時,需要將容器應用以及全部微前端模塊一起啟動,頁面才會正常運行。
- 路由
在微前端中路由實現分治,即每個微前端模塊(業務)維護各自的路由,容器應用負責匹配并拉取即將訪問的頁面相對應的模塊及其路由。在這里我們首先為主容器定義一個非常寬泛的路由定義,比如path: '*',然后在路由的鉤子函數如beforeEach中實現微前端模塊的路由獲取,并將其添加進主路由中。

從子模塊生成的manifest.json(后面微前端模塊部分會介紹)中獲取子模塊的路由js文件并將其解析,再加入主路由中。經過此番操作后,同一個窗口再次訪問這個地址時就會直接匹配成功主路由了。
- 微前端模塊渲染
容器應用中最重要的部分就是如何獲取并渲染微前端模塊?,F提供一個module.vue模塊專門負責拉取并渲染微前端模塊,則module.vue需要做的事情如下:
· 定位微前端模塊所在的host并拉取微前端模塊manifest.json列表(在上文介紹的路由中已經完成)
· 由于獲取manifest的過程在加載路由時已經完成,因此接下來直接從store中獲取該模塊的js文件url,并將其加載,再插入頁面。此處直接通過往document.head中插入script標簽的方法加載模塊,這與加載路由文件的方法一樣,不再贅述
· 執行微前端模塊的渲染函數,最終將其渲染至頁面中。每個微前端模塊都定義有一個全局的渲染函數,比如window.mp.render_home(‘#containerId’)則會調用微前端模塊中的Vue實例化、渲染函數,這一點會在后文詳細講述。這里要注意的是,需要傳入一個DOM id以便Vue將渲染過的模塊插入頁面對應的位置。
- 通用模塊注冊為懶加載組件
在業務中經常會碰到一些通用的代碼需要被引用,這些代碼如果是兩三行,引用相對簡單,但如果是成百上千行或是一個通用的組件,每個微前端模塊都通過import引用它,則會極大增加用戶瀏覽器需要下載并解析的代碼量,拖累頁面性能。所以在容器應用中(或者容器應用引用的一個common組件中)需要將重復引用的代碼注冊為全局引用的方法。
需要注意的是,以往在Vue中注冊全局組件往往需要在app.js中import,這會增加頁面初始化app.js文件的大小,所以為避免這一問題,在注冊組件時可以利用webpack的動態加載import的方式進行,這樣一來則只有在組件需要的時候才會真正引入這個組件的代碼,而不是直接將其打包進app.js中。在import時加入webpackChunkName的注釋可以起到告訴webpack打包后chunk的文件名的作用。,比如下面引入echarts的方式:

微前端模塊
完整的微前端模塊可以代表一個單獨的業務,其中可以包括一個或多個子模塊,比如一個關于收入的微前端模塊,其中包括收入概覽、收入圖表、收入按天列表等多個子模塊,這就要求每個微前端模塊都有如下功能:
1、一個遵循框架規范的業務自己定義的路由。這能給業務最大的自由度:上線新頁面而無需部署容器應用;
2、每個需要獨立出來的模塊的entry文件。Webpack打包時會將每一個entry生成一個js文件以便單獨調用,因為每個entry js代碼都包含初始化框架的邏輯,所以理論上每個entry代碼都可以獨立運行;
3、因為每次部署后所有js代碼的hash都會變化,所以微前端模塊需要能夠生成一個manifest文件列表,包括上面的路由文件以及全部獨立的模塊js文件的url列表。容器應用會實時通過這個manifest文件獲取最新的js代碼。
下面我們來詳細介紹微前端模塊的組成:
- webpack相關配置
微前端模塊的webpack相比主容器,有下面幾點不同:
· 忽略一些第三方包。盡管模塊在package.json中也引用了Vue等第三方包,但在webpack打包時無法將其包括進來,這主要是以下兩點原因:其一,這會導致js大小以幾十幾百倍增加;其二,Vue等框架代碼完全可以使用主容器已經引用了的三方包。所以我們利用webpack的external配置忽略了一些不希望被打包過程包括的代碼:

· 多個entry。因為一個微前端模塊可能會包括很多個可單獨引用的子模塊,每一個子模塊都單獨有一個Vue初始化等功能的js文件(類似主容器的app.js)。在打包過程中,我們加入了掃描全部jobs文件夾下js文件的功能,每一個js文件都代表了一個微前端模塊下的子模塊。
· 部署路徑:因為每一個微前端模塊都需要與其他微前端模塊部署到不同的服務器位置,所以在webpack打包部署時需要單獨配置。開發環境下可以直接使用localhost加不同的端口port,線上環境可根據實際情況進行配置。
· manifest:我們通過webpack-manifest-plugin來在打包時自動生成一個列表文件。這樣每次訪問到微前端模塊時都能通過這個文件獲取到最新上線的js代碼文件。
- 路由
一個微前端模塊可能代表了一個業務,而每個業務可能會頻繁變化路由配置,所以如果路由分配能由業務微前端模塊自行配置,不同團隊則能更方便的進行更改而不需要重新上線容器應用。上文已經介紹了主容器如何獲取并使用微前端中的路由配置,這里詳細介紹模塊中如何配置自己的路由。
在jobs/routes.js中,我們綁定一個全局函數用來讓主容器獲取到我們自己配置的路由信息,類似下面的代碼jobs/route.js:

可見,子模塊的路由完全遵循Vue的路由規范(或遵循自身使用的框架的規范),這樣可以讓容器應用毫不費力地直接添加引用。容器應用加載完成js文件后,可以直接調用window.mp.home_routes()獲來取已定義好的ROUTES配置。
- 子模塊的entry文件(jobs/*.js)
每一個子模塊都需要一個單獨的entry文件,這類似于主容器的jobs/app.js,能用于初始化子模塊代碼、綁定全局渲染函數等。entry文件與正常的初始化Vue方式一樣,只是需要將初始化邏輯包裹在一個全局函數中,以便主容器能在加載此模塊js完畢后,方便的進行渲染調用。例如:

根據以上代碼,渲染出來的Vue模塊會被放到指定的#${containerId}中,這個ID由主容器在渲染時動態傳入,以便將模塊插入到指定的頁面位置。
- 子模塊代碼開發
子模塊代碼開發與正常的Vue開發并無不同,可以輕松遷移老代碼,只需要注意一些通用代碼的引用并通過上文提到的懶加載模式引入。
后記
愛奇藝號技術團隊基于Vue定制開發了微前端框架,通過應用上面介紹的基本方法及其他的自定義邏輯,我們搭建了與業務代碼開發、代碼迭代非常契合的一套微前端框架。我們認為微前端的主要好處有:
· 更小,更緊密且更易維護的代碼庫。
· 組織更具擴展能力,團隊可更加獨立自治。
未來,我們會繼續完善前端框架,使其性能更加優秀,進一步提升愛奇藝號前端代碼的開發效率,實現不同應用之間頁面級別的自由組合,從而可生成個性化控制臺。
實現微前端有很多技術方法,不同的企業有不同的對策;從服務端轉換到 iframe 和 JAVAScript 元框架和 Web 組件等都是可選的微前端方案。
感興趣的朋友可以閱讀實現微前端的技術方法:https://medium.com/dazn-tech/adopting-a-micro-frontends-architecture-e283e6a3c4f3