最近,咱們團隊開源了一套沉澱了2年的前端SPA架構框架,主要是用來解決動態路由的問題。咱們的思路來源於後端,採用中間件的設計模式來架構整個框架。咱們的原則是讓你們快速開發一個SPA單頁應用,只關心業務邏輯,其餘的行爲均可以幫助處理掉。javascript
其實咱們的開源比較匆忙,從不少方面看仍是有些不標準的,可是以後咱們會嚴格按照標準的規範和流程走下去,給你們一份穩定的架構。對於如今開始關注咱們團隊的小夥伴,我表示很是的感謝。有你們的支持,咱們會作的更好。html
這個問題從咱們設計的初衷來講,就是解決全部路由的問題。在業界,其實你們都廣泛承認一對一的路由模式,從而生產出了不少路由體系,好比vue-router
和react-router
。他們都是很是不錯的架構,從某種意義上來講,引領了前端路由的前進。不少小夥伴都是直接使用他們的全家桶來開發項目,也獲得了很好的效果。前端
我不肯定你們是否考慮過一個問題,當一個用戶從登錄頁面A登錄後進入B,再回到A頁面的狀況。一對一的靜態路由其實理論上最終會將A頁面顯示爲未登陸,這是徹底正確的,可是邏輯上它應該是一個登陸的頁面。靜態路由沒法區分環境變量對頁面選擇的影響,只能經過hook等手段來將頁面內容替換掉。理論上,咱們藉助後端的寫法應該是這樣的邏輯:vue
route.get('/a', ctx => {
if (global.logined) {
ctx.render(webveiwA);
} else {
ctx.render(webviewB);
}
})複製代碼
咱們須要根據周邊的環境變量去自動選擇該渲染哪一個頁面,如此的邏輯纔是咱們想要的。因此咱們會根據這樣的思路來設計動態路由。在nodejs
的世界中,這種模式已經很是常見,好比express-router
與koa-router
,都是採用這種設計思路來實現動態話的路由體系。我觀察了前端的發展,都沒有提出在前端實現這樣的邏輯。因而,咱們便開始研究如何將這種思路架設在前端使用,來得到更理想的邏輯體驗。java
爲何這麼說呢?緣由很是簡單。在公司裏面,咱們大概有90%的H5業務都是採用Miox來實現的。咱們的技術棧實際上是Vue,由於Miox對Vue的結合太過深刻,因此天然有部分小夥伴認爲Miox是基於Vue來開發的,也就是說Miox是依賴着Vue?其實不是,Miox並不依賴任何框架實現。我來舉個列子:node
咱們的電腦,若是換了一個顯卡,那麼必需要裝顯卡驅動。根據不一樣的顯卡驅動,表現也不一樣。若是咱們將Vue看成一個顯卡,而Miox看成咱們的電腦,那麼咱們須要一份顯卡驅動來讓整臺電腦接受這塊顯卡。react
因此我提出一個概念,就是渲染驅動的概念。Vue僅僅是咱們Miox的一個渲染引擎,用來渲染頁面的,能夠理解爲模板。咱們還須要一份驅動告訴Miox,來講明Vue的渲染是如何在Miox實現的。這部分能夠從這裏看明白。固然,不只僅是Vue,咱們還可以將React接入到Miox中。理論上,只要能提供對應的渲染驅動,均可以將任意的渲染引擎接入到Miox中來使用。天然的,你們的書寫都將會變成那種引擎的書寫方式。這就是咱們的插拔式設計。在公司裏面使用的時候,咱們沒必要在乎使用何種渲染引擎,Miox均可以支持,同時幫您管理好整個路由體系。git
在前端,若是咱們可以用中間件來攔截整個邏輯過程的話,對開發是至關有利的,不只僅在代碼層面可以提升可讀性,同時能夠在具體業務層面提升效率。咱們用後端路由邏輯來舉個例子:github
當咱們遇到一些API都是須要通過登陸驗證的時候,咱們能夠將
/authorize
的路由前綴都使用中間件統一處理。其餘的都不走驗證邏輯。web
const Authorize = require('./auth');
const AuthorizeApi = require('./auth/routes');
route.use(
'/authorize',
Authorize.connect(), // Authorize.connect是統一的驗證邏輯代碼
AuthorizeApi.routes(),
AuthorizeApi.allowedMethods()
);複製代碼
如此當經過/authorize
的路由都須要通過Authorize.connect()
來驗證是否具備權限。這使得代碼很是簡潔易懂。
Miox的設計即是如此,經過這種中間件的架構,使得咱們在前端得到了統一攔截處理的能力。在實際生產中,咱們不少地方都用到了中間件來處理統一的校驗邏輯,使得代碼維護性獲得很大地提高。
那麼若是不使用呢?咱們須要在進入頁面的時候,每一個頁面中都要嵌入一段代碼來處理權限問題,不只僅代碼量增長,並且對於以後的維護,可能會產生漏改的問題。
具體想了解中間件的小夥伴,我推薦去看下koa-router。
說到緩存,這個話題過於龐大,對於Miox的緩存機制,我只能簡單介紹一下,有興趣的小夥伴能夠看下源碼。
在開發過程當中,特別是對於開發移動端頁面,咱們須要保存前一個頁面滾動位置,那麼咱們在切換到另外一個頁面的時候是不能將前一個頁面銷燬的,緣由是咱們但願回到前一個頁面的時候仍是停留在以前滾動的位置。那麼咱們須要緩存這個頁面來確保位置的不改動。Miox模擬了history的部分API,同時增長了一層頁面堆棧。咱們須要維護這層頁面堆棧來確保頁面的可溯性。每次咱們經過一種算法來動態比較路由與頁面的關係,從而從這個堆棧中選出咱們想要的緩存頁面。固然,沒有這個對應關係的時候,咱們天然是要建立的。咱們基於盡最大可能限度複用頁面的宗旨,來緩存這些頁面與路由的關係。
可能有人要問,若是緩存過多,對於頁面的切換會有性能上的影響吧?是的,過多的頁面緩存也是阻礙性能的關鍵。這裏咱們進行了緩存個數的優化。在啓動miox服務的時候,咱們有一個配置的參數max
,通常默認爲一個,固然,你們也能夠本身自由設置最大個數,來保障緩存的性能問題。
在History中,咱們都認可沒法判斷出當前瀏覽器行爲的方向性,因此沒法給出咱們想要的方向來自動作頁面切換的效果。爲了解決這個問題,咱們蒐集了業界的解決方案,採用sessionStorage
來模擬history堆棧,從而解決這個問題(新history的API中已經增長了history.index
動態屬性來告訴咱們如今位於堆棧中的位置)。咱們將此方案整合到了Miox中,而且對其增強,來告訴動畫引擎此次行爲在瀏覽器中是如何表現的。天然,動畫引擎就可以根據這個來自動切換頁面的動畫,達到自動處理的效果。
官方提供了一個簡單的模塊來支持動畫,固然小夥伴想要自定義動畫也是很是簡單的,具體見這裏。
在傳統的MVX框架中提供了組件的生命週期,咱們在某種意義上也認爲是頁面的生命週期,可是咱們對比原生IOS的週期行爲,仍是有所欠缺的。好比說active
生命週期。這個是什麼意思呢?我來舉例說明:
當一個頁面被推入後臺,又被喚起的時候,咱們根本不知道它是否是再一次被激活,咱們只能知道頁面又一次被進入,第二次進入的概念是須要不少代碼來輔助完成的。而在傳統框架中,很難觸發再一次的
mounted
生命週期,由於頁面已經被mounted
過了。Miox提供了這樣的生命週期的定義。
// use vue.js
export default Vue.extend({
mounted() {
this.$on('webview:active', this.activeLife);
},
methods: {
activeLife() {
console.log('我被喚起了');
}
}
})複製代碼
固然,咱們也能夠將這些生命週期直接拋到全局去,用於全局的監控。
app.on('webview:mounted', webview => {
console.log(webview);
})複製代碼
對於前置的生命週期,咱們一樣提供瞭如下的生命週期來輔助:
這些週期可以讓你很好地掌控整個過程,對於自動埋點什麼的功能很是實用。
目前主流的架構都支持了服務端渲染來加強SEO的能力,那麼對於Miox而言,也須要支持他們的服務端渲染。考慮到Miox自身會給渲染出來的內容包裹一些代碼,因此,咱們須要本身實現SSR。固然,渲染引擎的SSR實現是交給本身來完成的。也就是說咱們須要給他包裹一層SSR渲染。
Miox暫時支持Vue的SSR渲染,後續會逐步添加對於React的SSR渲染。還有好比百度的san.js
其實也能夠接入進來實現SSR渲染。服務端渲染並非太麻煩,若是你們可以掌握Miox的運行原理的話。
對於開源,咱們團隊內部作了不少努力,也諮詢了不少大牛,但願可以給你們創造出一份簡易開發的架構來幫助你們完成業務。目前,團隊後續計劃以下:
但願你們看到這篇文章後能夠支持咱們,給咱們多提供一些意見和建議,讓咱們共同將Miox完善下去。喜歡的小夥伴,幫忙點個Star。
項目開源在 GitHub: 51nb/Miox