前端技術演進(二):前端與協議

這個來自之前做的培訓,刪減了一些業務相關的,參考了很多資料(參考資料列表),謝謝前輩們,麼麼噠 😘

目前前後端的通信一般都是通過協議來完成的,這裏介紹和前端開發相關的各類協議。

HTTP

HTTP 協議是Hyper Text Transfer Protocol(超文本傳輸協議)的縮寫,是萬維網的數據通信的基礎。設計HTTP最初的目的是爲了提供一種發佈和接收HTML頁面的方法,現在基本上什麼類型的文件都可以傳輸了,比如圖片、CSS、JS、數據報文等。

HTTP一般基於TCP/IP通信協議來傳遞數據,它有如下特點:

  1. 簡單快速:客戶向服務器請求服務時,只需傳送請求方法和路徑。請求方法常用的有GET、HEAD、POST。每種方法規定了客戶與服務器聯繫的類型不同。由於HTTP協議簡單,使得HTTP服務器的程序規模小,因而通信速度很快。
  2. 靈活:HTTP允許傳輸任意類型的數據對象。正在傳輸的類型由Content-Type加以標記。
  3. 無連接:無連接的含義是限制每次連接只處理一個請求。服務器處理完客戶的請求,並收到客戶的應答後,即斷開連接。採用這種方式可以節省傳輸時間。
  4. 無狀態:HTTP協議是無狀態協議。無狀態是指協議對於事務處理沒有記憶能力。缺少狀態意味着如果後續處理需要前面的信息,則它必須重傳,這樣可能導致每次連接傳送的數據量增大。另一方面,在服務器不需要先前信息時它的應答就較快。
  5. 支持B/S及C/S模式。

HTTP使用統一資源標識符(Uniform Resource Identifiers, URI)來傳輸數據和建立連接。URL是一種特殊類型的URI,包含了用於查找某個資源的足夠的信息。一個完整的URL一般包括以下幾部分:

協議   域名   端口   虛擬目錄   文件名   參數

比如:http://test.google.com:80/test/test.html?query=admin#home

請求消息Request

通常一個HTTP請求消息包含如下內容:請求行、請求頭、空行、消息主體。

image.png | center | 466x165

比如:

 GET /books/?sex=man&name=Professional HTTP/1.1
 Host: www.example.com
 User-Agent: Mozilla/5.0 (Windows; U; Windows NT 5.1; en-US; rv:1.7.6)
 Gecko/20050225 Firefox/1.0.1
 Connection: Keep-Alive
  • 請求行:GET /books/?sex=man&name=Professional HTTP/1.1 用來說明請求類型,要訪問的資源以及所使用的HTTP版本。比較常見的請求類型有GET,POST,PUT,DELETE,OPTIONS等。
  • 請求頭:從第二行起爲請求頭部,用來說明服務器要使用的附加信息。
  • 空行:請求頭部後面的空行是必須的,即使請求數據爲空,也必須有空行。
  • 請求數據:也叫請求主體,可以添加任意的其他數據,這個例子的請求數據爲空。

帶有請求數據的POST請求:

 POST / HTTP/1.1
 Host: www.example.com
 User-Agent: Mozilla/5.0 (Windows; U; Windows NT 5.1; en-US; rv:1.7.6)
 Gecko/20050225 Firefox/1.0.1
 Content-Type: application/x-www-form-urlencoded
 Content-Length: 40
 Connection: Keep-Alive

 sex=man&name=Professional  

響應消息Response

一般情況下,服務器接收並處理客戶端發過來的請求後會返回一個HTTP的響應消息。通常一個HTTP請求消息包含如下內容:狀態行、消息報頭、空行、響應正文。

比如:

HTTP/1.1 200 OK
Date: Fri, 22 May 2009 06:07:21 GMT
Content-Type: text/html; charset=UTF-8

<html>
      <head></head>
      <body>
            <!--body goes here-->
      </body>
</html>
  • 狀態行:由HTTP協議版本號, 狀態碼, 狀態消息 三部分組成。
  • 消息報頭:用來說明客戶端要使用的一些附加信息。
  • 空行:消息報頭後面的空行是必須的。
  • 響應正文,服務器返回給客戶端的文本信息。

狀態碼

狀態碼由三位數字組成,第一個數字定義了響應的類別,共分五種類別:

  • 1xx:指示信息--表示請求已接收,繼續處理。
  • 2xx:成功--表示請求已被成功接收、理解、接受。
  • 3xx:重定向--要完成請求必須進行更進一步的操作。
  • 4xx:客戶端錯誤--請求有語法錯誤或請求無法實現。
  • 5xx:服務器端錯誤--服務器未能實現合法的請求。

常見的狀態碼有如下幾種:

  • 200 OK 客戶端請求成功。
  • 301 Moved Permanently 請求永久重定向。
  • 302 Moved Temporarily 請求臨時重定向。
  • 304 Not Modified 文件未修改,可以直接使用緩存的文件。
  • 400 Bad Request 由於客戶端請求有語法錯誤,不能被服務器所理解。
  • 401 Unauthorized 請求未經授權。
  • 403 Forbidden 服務器收到請求,但是拒絕提供服務。服務器通常會在響應正文中給出不提供服務的原因。
  • 404 Not Found 請求的資源不存在,例如,輸入了錯誤的URL。
  • 500 Internal Server Error 服務器發生不可預期的錯誤,導致無法完成客戶端的請求。
  • 503 Service Unavailable 服務器當前不能夠處理客戶端的請求,在一段時間之後,服務器可能會恢復正常。

HTTP和TCP/IP協議的關係

image.png | left | 827x1208

HTTP/2

HTTP2即超文本傳輸協議2.0版本,是HTTP協議的下一個版本。因爲標準委員會不打算再發布子版本了,所以直接叫HTTP/2,而不叫HTTP/2.0。

HTTP2相對於之前的HTTP協議有以下幾個優點:

  • HTTP 2採用完全二進制的格式來傳輸數據。同時HTTP 2對消息頭採用HPACK壓縮傳輸,最大限度地節省了傳輸帶寬。相比於HTTP 1.x 每次請求都會攜帶大量冗餘頭信息(例如瀏覽器Cookie信息等),HTTP2具有很大的優勢。
  • HTTP 2使用TCP多路複用的方式來降低網絡請求連接建立和關閉的開銷,多個請求可以通過一個TCP連接來併發完成。
  • HTTP2支持傳輸流的優先級和流量控制機制。HTTP2中每個文件傳輸流都有自己的傳輸優先級,並可以通過服務器來動態改變,服務器會保證優先級高的文件流先傳輸。例如在未來的瀏覽器端渲染中,服務器端就可以優先傳輸CSS文件保證頁面的渲染,然後在CSS文件全部傳輸完成後加載JavaScript腳本文件。
  • 支持服務器端推送。服務端能夠在特定條件下把資源主動推送給客戶端。

HTTP 1.1會讓資源排隊加載,如下圖所示:

image.png | center | 720x319

但當我們開啓了HTTP/2之後,有了TCP多路複用,個數幾乎沒有限制了,如下圖所示:

image.png | center | 720x571

HTTP/2 將 HTTP 協議通信分解爲二進制編碼幀的交換,這些幀對應着特定數據流中的消息。所有這些都在一個 TCP 連接內複用。這是 HTTP/2 協議所有其他功能和性能優化的基礎。

image.png | center | 827x645

目前支持HTTP2協議傳輸的瀏覽器依然很少,隨着技術的發展和瀏覽器的更新迭代,HTTP2的時代終會到來,但我們依然不能在短時間內企圖通過它來幫我們進行頁面優化。

HTTPS

HTTPS(超文本傳輸安全協議 Hypertext Transfer Protocol Secure)經由HTTP進行通信,但利用SSL/TLS來加密數據包。HTTPS的主要思想是在不安全的網絡上創建一安全信道。通常,HTTP 直接和 TCP 通信。當使用 SSL 時,則演變成先和 SSL 通信,再由 SSL 和 TCP 通信了。簡言之,所謂 HTTPS,其實就是身披 SSL 協議這層外殼的 HTTP。HTTP的URL由“http://”起始且默認使用端口80,HTTPS的URL由“https://”起始且默認使用端口443。

SSL 是獨立於 HTTP 的協議,所以不光是 HTTP 協議,其他運行在應用層的 SMTP 和 Telnet 等協議均可配合 SSL(Secure Socket Layer) 協議使用。

image.png | center | 342x157

HTTP是不安全的,攻擊者通過監聽和中間人攻擊等手段,可以獲取網站帳戶和敏感信息等。人們對 HTTPS 有一個普遍的錯誤認識,認爲只有處理敏感通信的網站才需要 HTTPS。 每個未受保護的 HTTP 請求都可能暴露與用戶行爲和身份有關的信息。儘管訪問一次未受保護的網站可能看上去無害,但一些入侵者會查看彙總的用戶瀏覽活動,以推斷他們的行爲和意圖,從而進行去匿名化攻擊,查出匿名用戶的身份。例如,員工可能在閱讀未受保護的醫療文章時不經意地向其僱主泄露敏感的健康信息。

公鑰和私鑰

加密和解密同用一個密鑰的方式稱爲共享密鑰加密(Common key crypto system),也被叫做對稱密鑰加密。

image.png | center | 600x456.23632385120345

以共享密鑰方式加密時必須將密鑰也發給對方。可究竟怎樣才能安全地轉交?在互聯網上轉發密鑰時,如果通信被監聽那麼密鑰就可會落入攻擊者之手,同時也就失去了加密的意義。

SSL 採用一種叫做公開密鑰加密(Public-key cryptography)的加密處理方式。公開密鑰加密使用一對非對稱的密鑰。一把叫做私有密鑰(private key),另一把叫做公開密鑰(public key)。顧名思義,私有密鑰不能讓其他任何人知道,而公開密鑰則可以隨意發佈,任何人都可以獲得。

使用公開密鑰加密方式,發送密文的一方使用對方的公開密鑰進行加密處理,對方收到被加密的信息後,再使用自己的私有密鑰進行解密。利用這種方式,不需要發送用來解密的私有密鑰,也不必擔心密鑰被攻擊者竊聽而盜走。

image.png | center | 600x464.6080760095012

HTTPS 採用混合加密機制:

image.png | center | 600x538.3863080684596

證書頒發機構

公開密鑰加密方式還是存在一些問題的。那就是無法證明公開密鑰本身就是貨真價實的公開密鑰。

證書頒發機構 (CA) 是一個組織,對公鑰和與公共 DNS 名稱之間的映射進行證實。例如,客戶端如何知道特定公鑰是否爲 www.foobar.com 的真實公鑰?按理說,無法知道。CA 證實特定密鑰是特定網站的真實密鑰,它使用自己的私鑰來加密簽名該網站的公鑰。此簽名在計算上是無法僞造的。瀏覽器(和其他客戶端)維護信任錨存儲庫,它包含知名 CA 擁有的公鑰,並且它們使用這些公鑰來加密驗證 CA 的簽名。

SSL握手流程

image.png | center | 827x886

最後使用共享密鑰來進行以後的通信,詳細流程:

image.png | center | 827x1564

Websocket

在實際的前端應用項目中,除了使用應答模式的HTTP協議進行普通網絡資源文件的請求加載外,有時也需要建立客戶端與服務端之間的實時連接進行通信,例如網頁實時聊天的應用場景,這就必須涉及瀏覽器端的實時通信協議了。對於這些對實時性要求較高的應用場景,普通的HTTP協議就並不適用。雖然前端可以通過Ajax定時向服務端輪詢的方式來持續獲取服務端的消息,但是這種方式效率相對較低。

WebSocket是瀏覽器端和服務器端建立實時連接的一種通信協議,可以在服務器和瀏覽器端建立類似Socket方式的消息通信。相對於HTTP1.1協議,WebSocket協議的優勢是方便服務器和瀏覽器之間的雙向數據實時通信。

7層網絡模型

這裏只是類似Socket方式,WebSocket 是建立在 TCP/IP 協議之上,屬於應用層的協議,而 Socket 是在應用層和傳輸層中的一個抽象層,它是將 TCP/IP 層的複雜操作抽象成幾個簡單的接口來提供給應用層調用。簡單回顧一下7層網絡模型:

image.png | center | 827x586

簡單來說,我們在傳輸數據時,可以只使用(傳輸層)TCP/IP協議,但是那樣的話,如果沒有應用層,便無法識別數據內容。如果想要使傳輸的數據有意義,則必須使用到應用層協議。應用層協議有很多,比如HTTP、FTP、TELNET等,也可以自己定義應用層協議。WEB使用HTTP協議作應用層協議,以封裝HTTP文本信息,然後使用TCP/IP做傳輸層協議將它發到網絡上。

TCP/IP只是一個協議棧,就像操作系統的運行機制一樣,必須要具體實現,同時還要提供對外的操作接口。這個就像操作系統會提供標準的編程接口,比如win32編程接口一樣,TCP/IP也要提供可供程序員做網絡開發所用的接口,這就是Socket編程接口。Socket的出現只是使得程序員更方便地使用TCP/IP協議棧而已,是對TCP/IP協議的抽象,從而形成了我們知道的一些最基本的函數接口,比如create、listen、connect、accept、send、read和write等等。

image.png | center | 827x1172

Websocket使用

WebSocket 的實現分爲握手,數據發送/讀取,關閉連接。

image.png | center | 827x505

var ws = new WebSocket("wss://echo.websocket.org");

ws.onopen = function(evt) { 
  console.log("Connection open ..."); 
  ws.send("Hello WebSockets!");
};

ws.onmessage = function(evt) {
  console.log( "Received Message: " + evt.data);
  ws.close();
};

ws.onclose = function(evt) {
  console.log("Connection closed.");
};      

在線演示:https://html5demos.com/web-socket/

DDP

DDP ( Distributed Data Protocol,分佈式數據協議)是一種新型的客戶端與服務器端的實時通信協議,由於兼容性的原因,目前使用還不廣泛。

DDP使用JSON的數據格式在客戶端和瀏覽器之間進行數據傳輸通信,所以對於前端開發者來說使用非常方便。有名的Meteor Web框架的雙向實時數據更新機制底層使用的就是DDP,這種協議模式下客戶端可向服務器端發起遠程過程調用,客戶端也可以訂閱服務端數據,在服務端數據變化時,服務器會向客戶端發起通知,觸發瀏覽器響應的操作。

//創建服務端
const DDPClient = require('ddp');
const client = new DDPClient({
  host: 'localhost',
  port: 3000
});

//監聽消息
client.on('message', function(data, flags){
  console.log('[DDP消息]: ', data);
});

//創建連接
client.connect(function(){
  client.subscribe('post',[],function(){
    console.log('[post訂閱消息]');
  });
});

協議標準:https://github.com/meteor/meteor/blob/devel/packages/ddp/DDP.md

RESTful

REST (Representational State Transfer,表述性狀態轉化)並不是某一種具體的協議,而是定義了一種網絡應用軟件之間的架構關係並提出了一套與之對應的網絡之間交互調用的規則。與之類似的例如早期的WebSevice,WebSevice現在基本都不用了。

在REST形式的軟件應用服務中,每個資源都有一個與之對應的URI地址,資源本身都是方法調用的目標,方法列表對所有資源都是一樣的,而且這些方法都推薦使用HTTP協議的標準方法,例如GET、POST、PUT、DELETE等。如果一個網絡應用軟件的設計是按照REST定義的,我們就可以認爲它使用的交互調用的方法設計遵循RESTful規範。換種方式理解,RESTful 是一種軟件架構之間交互調用數據的協議風格規範,它建議以一種通用的方式來定義和管理數據交互調用接口。

例如:對於書籍book的記錄管理接口,有增、刪、改、查操作,於是我們定義接口:

  • path/addBook
  • path/deleteBook
  • path/updateBook
  • path/getBook

看上去好像沒有什麼問題。後來,另一個項目也有類似的接口定義,卻可能叫作:

  • path/appendBook
  • path/delBook
  • path/modifyBook
  • path/getBook

接着有一天,項目負責人可能會說,要升級接口來滿足新的需求,於是我們又添加了:

  • path/addBook2
  • path/deleteBook2
  • path/updateBook2
  • path/getBook2

這樣用起來是沒有什麼問題,但是這些隨意的定義會增加數據接口維護難度和項目繼續開發的成本。

這時,我們或許會考慮使用文檔或規範,規定一定要使用add來添加,新的接口版本號放前面 path/v2/addBook,開發的人必須嚴格按照文檔規範去寫。這樣做很好,但依然不夠完善,原因有以下幾點:

  • 因爲項目工作常常排期緊張,你可能沒時間去寫文檔,或者後面接手的人不想去看文檔。
  • 開發修改功能後很可能來不及或忘記去更新文檔。
  • 無論文檔寫得多清楚,我們看起來效率總是很低。

這時如果有一個風格更好的通用規範來定義數據交互接口,就不用這麼麻煩了。所以我們完全可以利用RESTful設計的規範特性來解決上面遇到的問題。對於書籍記錄操作接口的命名可以如下操作:

HTTP方法 URI 描述
POST path/v1/book 新增書籍信息
DELETE path/v1/book 刪除書籍信息
PUT path/v1/book 更新書籍信息
GET path/v1/book 獲取書籍信息

使用RESTful規範來重新設計接口後,一切就變得很清晰自然,這樣新的工程師接手項目時,只要他足夠了解RESTful規範,幾乎沒有時間成本。即使他不瞭解RESTful規範,也可以很快地去了解,這就可以避免他去讀那份看似完善其實冗長雜的文檔。

這裏涉及到了幾個設計的原則:

  • “資源”表示一種實體,所以應該是名詞,URL不應該有動詞,動詞應該放在HTTP協議中。
  • 按照標準,不應該在URL中包含版本號,應該放在HTTP請求頭信息的Accept字段中,不過這樣不夠直觀,所以一般的方式還是把版本號放在URL中,算是一個反模式。

RESTful API 的主要設計原則就是這些,總結來說就是結合HTTP的固有方式來表徵資源的狀態變化描述,而不是通過動詞加名詞的方式來設計。

Github RESTful API:https://developer.github.com/v3/

GraphQL

GraphQL 對 API 中的數據提供了一套易於理解的完整描述,使得客戶端能夠準確地獲得它需要的數據,而且沒有任何冗餘,也讓 API 更容易地隨着時間推移而演進。

GraphQL 解決的最重要的3個問題分別是:

  • 需要進行多次往返以獲取所需的數據:典型的 REST API 請求多個資源時得載入多個 URL,而 GraphQL 可以通過一次請求就獲取你應用所需的所有數據。
  • 客戶端依賴於服務端:消除了服務器對數據內容進行硬編碼的需要。我們可以把客戶端與服務端分離開來,單獨進行維護和改進。前後端徹底分離。
  • 糟糕的前端開發體驗:使用 GraphQL,開發人員可以聲明式地來表達其用戶界面的數據需求,不用總是關注數據是如何獲取的。

RESTful APIs vs GraphQL APIs

一個簡單的示例:我們要做一個星球大戰人物信息展示的UI界面:需要顯示人物的姓名,出生年份,星球名稱以及所有他們參演的電影的名稱。

這個 UI 的 JSON 數據可能類似於:

{
  "data": {
    "person": {
      "name": "Darth Vader",
      "birthYear": "41.9BBY",
      "planet": {
        "name": "Tatooine"
      },
      "films": [
        { "title": "A New Hope" },
        { "title": "The Empire Strikes Back" },
        { "title": "Return of the Jedi" },
        { "title": "Revenge of the Sith" }
      ]
    }
  }
}

如果使用 React.js ,一般會這樣表示視圖:

// The Container Component:
<PersonProfile person={data.person} ></PersonProfile>

// The PersonProfile Component:
Name: {person.name}
Birth Year: {person.birthYear}
Planet: {person.planet.name}
Films: {person.films.map(film => film.title)}

如果使用 RESTful API,我們可能這樣請求數據:

1、獲取人物信息:

GET - /people/{id}
{
  "name": "Darth Vader",
  "birthYear": "41.9BBY",
  "planetId": 1
  "filmIds": [1, 2, 3, 6],
  ...
}

2、獲取星球信息:

GET - /planets/1

3、獲取所有電影信息:

GET - /films/1
GET - /films/2
GET - /films/3
GET - /films/6

演示

我們需要發送6個請求才能獲取到所有需要的數據,每個獲取數據的方法都是命令式的。每個接口返回的信息還有很多字段不是我們所需要的。爲了解決這個問題,我們可能會新增加一個接口,比如:

GET - /people/{id}/films

但是這樣就不是純粹的RESTful API了,而且後端要額外的創建這個接口,用來滿足前端的數據要求,如果增減字段或對象,後端還要添加接口或者重新編碼。

如果使用GraphQL,我們可以這樣來查詢:

GET or POST - /graphql?query={...}

比如參數使用:

{
  person(personID: 4) {
    name,
    birthYear,
    homeworld {
      name
    },
    filmConnection {
      films {
        title
      }
    }
  }
}

演示

一個請求就完成了所有數據的獲取。

GraphQL的靈活性也會帶來一些問題,比如增加複雜度,資源耗盡攻擊,N+1查詢等。FB針對N+1給出了 dataloader 的方案。

Github GraphQL API:https://developer.github.com/v4/

在線調試:https://developer.github.com/v4/explorer/

與Native交互

Hybrid App是在Native App應用的基礎上結合了Web App應用所形成的模式,一般稱之爲混合App。從技術開發上來看,相比於傳統的桌面瀏覽器端的Web App,它具有以下幾方面的特徵:

  • 可用的系統網絡資源更少。由於移動設備CPU、內存、網卡、網絡連接多方面的限制,HybridApp的前端頁面可用的系統資源遠遠小於桌面瀏覽器。就網絡連接來說,大部分移動設備的使用者使用的仍是3G、4G的網絡,帶寬和流量均有限制,和桌面瀏覽器的帶寬接入相比還是有着本質上的區別。
  • 支持更新的瀏覽器特性。目前智能設備瀏覽器種類相對較少,且隨着硬件設備的快速更新,主流的瀏覽器以WebKit內核居多,支持較新的瀏覽器特性。不像桌面瀏覽器那樣需要考慮較低版本Internet Explorer的兼容性問題。
  • 可實現離線應用。Hybrid的一個優勢是可以通過新的瀏覽器特性或Native的文件讀取機制進行文件級的文件緩存和離線更新。這是桌面瀏覽器,上較難做到的。這些離線機制常常可以用來彌補Hybrid App網絡系統資源不足的缺點,讓瀏覽器腳本更快從本地緩存中加載。
  • 較多的機型考慮。由於目前移動設備平臺不統一,而且不同設備機型系統的瀏覽器實現仍有一定的區別,因此Hybrid App應用需要考慮不同設備機型的兼容性問題。
  • 支持與Native交互。Hybrid App的另一個特點是結合了移動端Native特性,可以在前端頁面中調用客戶端Native的能力,例如攝像頭、定位、傳感器、本地文件訪問等。

Web調用Native

在HTML5中調用Native程序一般有幾種較通用的方法:

一、通過URI請求。

Native應用可在移動端系統中註冊一個Scheme協議的URI,這個URI可在系統的任意地方授權訪問來調起一段原生方法或一個原生的界面。同樣,Native 的WebView控件中的JavaScript腳本的請求也可以匹配調用這一通用的Scheme協議。例如我們通過對 window.location.href 賦值或使用iframe的方式發送一個URI的請求,這個請求可以被Native應用的系統捕獲並調起Native應用註冊匹配的這個Scheme協議內容。

比如微信的scheme爲(weixin://)。

image.png | center | 596x625

二、通過addJavascriptInterface(Android)或JavaScriptCore(iOS)注入方法到頁面中調用。

Android,原生Webview需要先註冊可供前端調用的JS函數:

// Android容器允許JS腳本,必須要
webSettings.setJavaScriptEnabled(true);
// Android容器設置僑連對象
mWebView.addJavascriptInterface(getJSBridge(), "JSBridge");

// Android4.2版本及以上,本地方法要加上註解@JavascriptInterface,否則會找不到方法。
private Object getJSBridge(){  
    Object insertObj = new Object(){  
        @JavascriptInterface
        public String foo(){  
            return "foo";  
        }  

        @JavascriptInterface
        public String foo2(final String param){  
            return "foo2:" + param;  
        }  

    };  
    return insertObj;  
}

然後H5中即可調用原生中註冊的函數:

// 調用方法一
window.JSBridge.foo(); // 返回:'foo'
// 調用方法二
window.JSBridge.foo2('test'); // 返回:'foo2:test'

iOS,需要引入JavaScriptCore庫:

#import <JavaScriptCore/JavaScriptCore.h>

然後原生需要註冊API:

//webview加載完畢後設置一些js接口
-(void)webViewDidFinishLoad:(UIWebView *)webView{
    [self hideProgress];
    [self setJSInterface];
}

-(void)setJSInterface{

    JSContext *context =[_wv valueForKeyPath:@"documentView.webView.mainFrame.javaScriptContext"];

    // 註冊名爲foo的api方法
    context[@"foo"] = ^() {

        //獲取參數
        NSArray *args = [JSContext currentArguments];
        NSString *title = [NSString stringWithFormat:@"%@",[args objectAtIndex:0]];
        //做一些自己的邏輯
        //返回一個值  'foo:'+title
        return [NSString stringWithFormat:@"foo:%@", title];
    };
    
}

之後前端就可以調用了:

// 調用方法,用top是確保調用到最頂級,因爲iframe要用top才能拿到頂級
window.top.foo('test'); // 返回:'foo:test'

三、改寫瀏覽器原有對象。

通過修改原來瀏覽器的window某些方法,然後攔截固定規則的參數,然後分發給Java對應的方法去處理。這裏常用的是以下四個方法:

  • alert,可以被webview的onJsAlert監聽
  • confirm,可以被webview的onJsConfirm監聽
  • onsole.log,可以被webview的onConsoleMessage監聽
  • prompt,可以被webview的onJsPrompt監聽

Native調用Web

需要先使用JavaScript 在HTML5頁面全局中聲明相對應的方法。

然後Native向HTML5發起調用,Android平臺一般通過loadUrl,iOS通常通過stringByEvaluatingJavaScriptFromString實現。

Android調HTML5:

// 異步執行JS代碼,並獲取返回值    
mWebView.evaluateJavascript("javascript: 方法名('參數,需要轉爲字符串')", new ValueCallback<String>() {
        @Override
        public void onReceiveValue(String value) {
            // 這裏的value即爲對應JS方法的返回值
        }
});

iOS調HTML5:

// 可以取得JS函數執行的返回值
// 方法必須是Html頁面綁定在最頂層的window上對象的
// 如window.top.foo
[webView stringByEvaluatingJavaScriptFromString:@"方法名(參數);"];

JSBridge

image.png | center | 690x167

JSBridge是HTML5與Native通信的橋樑,其作用是實現HTML5與Native間的雙向通信。JSBridge綜合了上面的技術,更多的是一種形式、一種思想,各家的實現方式也略有差異。比如微信JSSDK,就是基於WeixinJSBridge,微信瀏覽器中的頁面,通過WeixinJSBridge調用微信提供的一些原生功能。

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章