當前位置:
首頁 > 最新 > 七行JSON代碼將你的網站變成移動應用

七行JSON代碼將你的網站變成移動應用

作者|Ethan

譯者|大愚若智

編輯|覃雲

本文介紹了藉助 Jasonette 將 Web 視圖和原生組件融合構建真正「混合」應用的做法。

如果我告訴你,只需要上述 7 行橙色的 JSON 代碼就可以將一個網站變成移動應用,你相信嗎?完全不需要使用某種框架 API 重寫網站,就可以獲得與移動應用相同的行為。如果你已經有一個現成的網站,只需要簡單地引用 URL 就可以將其「打包」為原生應用。

而如果在此基礎上,只需要略微調整 JSON 代碼內容,就可以直接訪問所有原生 API、原生 UI 組件以及原生視圖切換(View Transition)。

最簡化的範例效果如下圖所示:

GIF

從中可以看出,我嵌入了一個 GitHub.com 的 Web 頁面,但界面上其餘布局均為原生 UI 組件,例如 導航條 以及 底部的標籤欄。而我們並不需要使用任何 API 重寫網站,就可以自動獲得原生的切換效果。

在介紹具體做法前你可能會問:「看著挺酷,但除了在原生應用框架內展示 Web 頁面之外,這種技術還有什麼意義?」

問得好!這也是本文要講的重點。我們只需要創建一個無縫的Web 視圖與應用間雙向通信,藉此,父應用就可以觸發 Web 視圖內的任何 JavaScript 函數,隨後 Web 視圖即可從外部調用原生 API。

例如:

GIF

請注意,這個視圖包含:

原生導航條,以及內置的切換功能

一個 Web 視圖,其中嵌入了一個可以生成二維碼的 Web 應用

在底部包含一個原生的文字輸入組件

上述所有這一切只需要略微調整 JSON 代碼的屬性即可實現。

最後請注意,隨著在文字輸入區輸入不同內容,二維碼也會產生相應變化。輸入的文字可觸發二維碼生成器 Web 應用內部的 JavaScript 函數重新生成二維碼圖像。

目前還沒有任何一個開發框架曾試圖從根本上解決「Web 視圖與原生應用無縫集成」的問題,因為這些框架都專註於完全原生,或完全 HTML5 的做法。

無論什麼時候當我們聽到有人討論移動應用的未來時,很可能會聽到類似「到底是 HTML5 還是原生方法會最終勝出呢?」這樣的說法。

似乎沒人覺得和可以共存,而且二者的協同和最終實現似乎也並不容易。

本文我將要介紹:

為何 Web 引擎與原生組件的融合通常是一種更好的做法。

為何 HTML 與原生的無縫集成那麼難,具體又該如何實現。

更重要的是,該如何使用這樣的技術快速構建自己的應用。

為何要在原生應用中使用 HTML?

在進一步介紹前,首先一起看看這樣做是好是壞,以及什麼時候適合使用這種方法。這種做法的一些潛在用例如下:

1. 使用 Web 原生功能

應用中的部分內容使用 Web 引擎來實現也許是一種更適合的做法。例如 WebSocket 是一種原生的 Web 功能,主要面向 Web 環境而設計。這種情況下就更適合使用內建的 Web 引擎(iOS 的 WKWebView以及Android 的 WebView),而非安裝某些只能「模擬」WebSocket 的第三方庫。

無需額外安裝任何代碼,使用免費工具即可實現目標,這樣豈不是更好。同時這也催生了下一個原因。

2. 避免二進位文件體積過大

有些功能也許需要藉助龐大的第三方庫,而你可能希望能快速用上這樣的功能。

例如,為了以原生方式包含二維碼圖像生成器,可能需要安裝某些第三方庫,這會導致二進位文件體積增大。但如果使用 Web 視圖引擎並通過一個簡單的調用 JavaScript 庫,就可以免費實現這一切,並且避免了使用第三方原生庫。

3. 缺乏可靠的移動庫

對於一些前沿技術,可能暫時並不具備穩定可靠的移動端實現。

好在大部分此類技術都具備 Web 實現,因此最高效的集成方法就是使用 JavaScript 庫。

4. 構建部分原生,部分基於 Web 的應用

很多新手開發者想要將自己的網站移植為移動應用,但在發現自己現有網站的部分功能過於複雜,無法面向每種移動平台快速重寫時,往往會感到沮喪或受挫。

例如你可能有一個非常複雜的 Web 頁面無法快速轉換為移動應用,但網站的其他內容可以很容易地轉換。

面對這種情況,如果通過某種方法將應用的大部分內容以原生方式構建,對於特別複雜的頁面直接將其以 HTML 的形式無縫集成到應用中,是不是很棒啊。

這是如何實現的?

A. Jasonette

Jasonette 是一種基於標記語言,構建跨平台原生應用的開源方法。

該技術看似 Web 瀏覽器,但並不會將 HTML 標記語言解釋為 Web 頁面,而是會將 JSON 標記解釋為 iOS 和 Android 上的原生應用。

正如所有 Web 瀏覽器都有完全相同的代碼,但只要按需解釋不同類型的 HTML 標記,即可為用戶提供所有不同類型的 Web 應用,所有 Jasonette 應用也有著完全相同的庫,可按需解釋不同類型的 JSON 標記並創建出你的應用。開發者完全無需觸及代碼本身,只需要編寫標記,將代碼實時「翻譯」為原生應用,即可開發出自己的應用來。

雖然 Jasonette 的核心作用在於構建原生應用,但本文的重點在於介紹如何將 HTML 集成到核心原生引擎中,接下來就一起了解一下吧。

B. Jasonette Web 容器

原生應用很棒,但有時候我們依然需要使用 Web 功能。

但 Web 視圖與原生應用的集成是個麻煩的過程。無縫的集成要求:

Web 視圖應作為原生布局的一部分進行集成:Web 視圖應作為原生布局的一部分納入應用中,並且操作方式應與其他任何原生 UI 組件保持一致。否則會讓用戶感覺很笨拙,並且感覺上就像自己實際上是在訪問網站那樣。

父應用可以控制子 Web 容器:父應用應能隨意控制子 Web 視圖。

子 Web 容器可觸發父應用的原生事件:子應用應該能觸發父應用的事件以運行原生 API。

這是一個非常繁重的工作,因此先從第一個環節著手介紹:直接將 Web 容器嵌入原生布局?—並將其作為第 1 版發布:

JSON Web 容器,JSON 中的 HTML 將變為原生應用組件。

僅這一點就已經很實用了,但由於無法交互,依然存在一定的局限。

父應用無法控制子 Web 容器,子容器無法向父應用發送任何事件通知,這導致 Web 容器與外界完全隔離

C. Jasonette Web 容器 2.0:使其可交互

發布第 1 版之後,我們開始處理第二個問題:為 Web 容器添加交互能力

下文將介紹如何為之前創建的靜態 Web 容器添加交互能力,讓它變得更強大。

實現:互動式 Web 容器

1. 通過 URL 載入

問題

之前在第 1 版中,為了使用 Web 容器作為後台視圖組件,我們首先需要將設置為,隨後在屬性下添加硬編碼的 HTML 文本,例如這樣:

一般來說,人們往往更希望直接使用 Web URL 對容器進行實例化,而不希望將整個 HTML 代碼以硬編碼的方式作為一行代碼加入。

解決方案

Web 容器 2.0 增加了屬性,我們可以嵌入形式的本地 HTML,例如這樣(可以從伴隨應用發布的本地 HTML 文件載入):

或者也可以嵌入遠程的 URL,例如這樣(可以從遠程 HTML 載入):

2. 父應用與 Web 容器的雙向通信

問題

之前,Web 容器只能用於展示內容,無法交互。這意味著下列做法全部無法實現

Jasonette 到 Web 容器的通信:從 Jasonette 中調用 Web 容器內部的 JavaScript 函數。

Web 容器到 Jasonette 的通信:從 Web 容器代碼中調用原生 API。

此時我們只能展示 Web 容器的內容。這就像網頁中嵌入的 iframe 框架,主頁面完全無法訪問 iframe 框架中的內容。

解決方案

Jasonette 最大的目標在於設計一種可以描述跨平台移動應用的標準化標記語言。因此我們需要這個標記語言能夠全面地描述父應用和子 Web 容器之間的雙向通信。

為此我在父應用和子 Web 容器之間使用了一種基於 JSON-RPC 的通信管道。由於 Jasonette 中的一切都是通過 JSON 對象表達的,因此使用 JSON-RPC 標準格式作為通信協議就成了一種非常自然合理的方式。

為了讓 JavaScript 函數能夠調用 Web 容器,需要聲明一個名為的操作:

$agent.request 是一種原生 API,可觸發 JSON-RPC 請求並發送給 Web 容器。為了使用該 API,必須將對象作為參數傳遞。

對象實際上是發送給 Web 容器的 JSON-RPC 請求。每個屬性的含義如下:

:Web 容器構建在一種名為 Agent 的底層架構基礎上,通常來說,我們可以為一個視圖使用多個 Agent,每個 Agent 可以有自己的唯一 ID。但 Web 容器是一種特殊類型的 Agent,只能使用 $webcontainer 作為 ID,因此這裡需要使用 ID。

:要調用的 JavaScript 函數名稱。

:傳遞給 JavaScript 函數的參數數組。

因此完整來看,所用的標記應該是類似這樣的:

這串標記實際上是在說:

隨後會查找一個名為 login 的 JavaScript 函數並傳遞下的兩個參數(和)。

上文介紹了父應用如何觸發子 Web 容器的 JavaScript 函數調用,我們還可以反著來,讓 Web 容器觸發父應用的原生 API。

詳情請參閱 Agent 文檔。

Agent 文檔:https://docs.jasonette.com/agents/

范 例

繼續回到上文介紹的二維碼生成器範例:

GIF

其中 底部的文字輸入組件是 100% 原生的。

二維碼由 作為 Web 應用運行 的 Web 容器生成。

當用戶輸入內容並按下「生成」,將調用 Web 容器 Agent 中的操作,進而調用 JavaScript 函數「qr」。

具體示例可以參閱:

https://github.com/Jasonette/Jasonpedia/blob/gh-pages/webcontainer/agent/fn/index.json

3. 腳本注入

問題

有時候我們可能需要在 Web 容器完成初始 HTML 載入後,動態地將 JavaScript 代碼注入 Web 容器。

假設要構建一個自定義的 Web 瀏覽器應用,我們可能希望將自己的自定義 JavaScript 注入到每個 Web 視圖,藉此定製 Web 視圖的行為,這有點類似於 Web 瀏覽器的擴展。

就算不需要構建 Web 瀏覽器,當希望為所包含的內容不由我們控制的 URL 實現自定義行為時,同樣需要使用腳本注入的方法。原生應用和 Web 容器只能通過 API 通信,但如果無法更改 HTML 內容,只能通過動態注入的方式將介面加入 Web 容器。

解決方案

正如上文所述,這個 Web 容器也是一個,這意味著我們可以使用與普通 Agent 完全相同的 $agent.inject 方法。

4. 對 URL 點擊的處理

以往,Web 容器只能通過兩種方式處理鏈接點擊操作:

只讀:將 Web 容器視作只讀的,忽略所有諸如觸控或滾動等事件。此時所有 Web 容器都是只讀的,除非明確令其表現得像是普通瀏覽器,具體做法見下文。

普通瀏覽器行為:像是普通瀏覽器那樣,允許用戶與頁面交互。為此需要進行聲明,將設置為屬性。

問題

兩者均為「全無或全有(All or nothing)」解決方案

解決方案

通過使用新的 Web 容器,可以將任何附加到這個 Web 容器,進而處理鏈接點擊之類的事件。

一起看一個例子:

在這裡我們為 Web 容器附加了,這意味著當用戶點擊 Web 容器內的任何鏈接後,將觸發操作,而非直接交由 Web 視圖處理。

此外如果查看操作會發現,這裡出現了變數。在本例中,點擊的鏈接將通過變數傳遞。例如,如果點擊一個名為的 URL,將獲得下列值:

這意味著我們可以 檢查 $jason.url 的值 進而選擇性地觸發不同操作。

用自定義 Web 瀏覽器的實現作為另一個例子一起來看看:

我們會檢查 URL 是否包含字元串,並根據結果執行兩個不同操作。

如果包含,打開一個新視圖並以原生方式完成登錄操作。

如果不包含,則直接運行操作,實現類似普通瀏覽器的行為。

用法示範

構建自定義 Web 瀏覽器

利用新版 Web 容器的下列特性,可以實現很多有趣的操作:

通過屬性實現自我載入,並充當一個功能齊備的瀏覽器。

根據 URL 的不同,選擇性地處理鏈接點擊操作。

我們甚至可以通過幾十行 JSON 代碼構建一個自定義的 Web 瀏覽器。由於現在可以劫持每個鏈接點擊,因此可以檢查,並根據結果運行我們需要的任何操作。

例如下面的例子:

GIF

GIF

這一切都可以根據的值選擇性地觸發實現。

第 1 步:向 Web 容器附加一個名為的操作

第 2 步:根據的值運行內部的相關操作

在下列代碼中,我們會檢查是否與、、等內容(均為頂級菜單項鏈接)相符。如果相符,設置即可讓 Web 容器做出與普通瀏覽器一樣的行為。

如果模式不符,則可通過原生的轉換打開一個新視圖,並將點擊的鏈接作為參數傳遞過去。

該 Web 瀏覽器的完整 JSON 標記請參閱(僅 48 行!):

https://github.com/Jasonette/Jasonpedia/blob/gh-pages/webcontainer/agent/hijack.json

瞬間構建「混合」應用

人們通常在說「混合」應用時,主要是指封裝在原生應用框架內部的 HTML Web 應用。

但此處說的並不是這種應用。這裡所謂的「混合」是指真正的混合應用,也就是可以同時包含多個原生視圖以及多個基於 Web 的視圖的應用。在這種應用中,一個視圖可以有多個原生 UI 組件,以及一個用相同原生布局渲染的 Web 容器。

Web 視圖與原生視圖的交織應當儘可能無縫,使得用戶完全無法分辨。

GIF

在這個例子中,我創建了一個可以在 Web 容器中顯示 jasonbase.com 的內容,並將其作為主頁視圖的應用。

Jasonbase 是我開發的免費 JSON 託管服務,該服務可以很簡單地用於託管 Jasonette 應用所用到的 JSON 標記。

完全無需觸及 Jasonbase.com 的代碼就可以構建出這個應用。

只需要將網站作為 Web 容器嵌入 Jasonette,隨後劫持鏈接點擊操作的原生處理方式,這樣就可以實現原生應用所具備的各類功能,例如觸發原生 API 以及進行原生轉換。

完整代碼可參閱這裡:

https://github.com/Jasonette/Jasonpedia/blob/gh-pages/webcontainer/agent/hybrid.json

結 論

在我看來,讓這一切如此令人讚歎的原因在於,在框架層面上即可妥善處理好一切。所有最困難的工作都是在後台完成的。

應用開發者並不需要自行費時費力從零開始實現下列這一切:

將 Web 視圖嵌入原生布局

創建 JavaScript 橋,以便讓應用能夠調用 Web 視圖中的函數

創建原生事件處理架構,以便讓 Web 視圖能夠觸發父應用的原生事件

整個解決方案創建了下列內容組成的抽象:

聲明式標記語言:用於描述如何將 Web 視圖嵌入原生應用。

通信協議(JSON-RPC):用於在應用及其子 Web 視圖之間實現極為簡單的通信。

我並不覺得這種方法可以解決所有問題,但從自己的用例來看,至少可以說這是個不錯的解決方案。

我試著以非常前沿的技術來構建應用,而這些技術已經前沿到在移動端還沒有任何穩定可靠的實現(由於協議的一些本質,甚至不清楚最終是否會有移動端的實現)。好在這些技術都有 JavaScript 實現,因此不費什麼事就可以輕鬆地將其與應用相集成。

總的來說,這種技術很棒,我對目前的效果非常滿意。最新版文檔 已經包含了所有新功能,歡迎大家深入研究並嘗試。

聲明:能力越大,需要擔負的責任也就越大

最後我想說:雖然這種新技術確實很強大,但我覺得大家在開發應用時都應該在用戶體驗方面進行更全面的權衡。

有些人可能會藉助這種技術構建完全由 Web 視圖組成的應用,但說到底這樣的做法,你的應用實際上就只是一個網站,已經與開發專屬應用的本意背道而馳了。

需要強調的是,我並不認為你的每個應用都應同時包含 HTML 和原生組件。我只是認為,這樣的做法對很多面臨某些具體狀況的人會顯得較為有用,只不過別過火就好。


喜歡這篇文章嗎?立刻分享出去讓更多人知道吧!

本站內容充實豐富,博大精深,小編精選每日熱門資訊,隨時更新,點擊「搶先收到最新資訊」瀏覽吧!


請您繼續閱讀更多來自 前端之巔 的精彩文章:

現在你可以在電腦Chrome上使用PWA啦!

TAG:前端之巔 |