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

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

目前先後端的通訊通常都是經過協議來完成的,這裏介紹和前端開發相關的各種協議。javascript

HTTP

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

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通常包括如下幾部分:html5

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

好比:http://test.google.com:80/test/test.html?query=admin#homegit

請求消息Request

一般一個HTTP請求消息包含以下內容:請求行、請求頭、空行、消息主體。程序員

image.png | center | 466x165

好比:github

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請求:web

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請求消息包含以下內容:狀態行、消息報頭、空行、響應正文。typescript

好比:

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.");
};      複製代碼

在線演示: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訂閱消息]');
  });
});複製代碼

協議標準:github.com/meteor/mete…

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: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,咱們可能這樣請求數據:

一、獲取人物信息:

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

二、獲取星球信息:

GET - /planets/1複製代碼

三、獲取全部電影信息:

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:developer.github.com/v4/

在線調試: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程序通常有幾種較通用的方法:

1、經過URI請求。

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

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

image.png | center | 596x625

2、經過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'複製代碼

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

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

  • alert,能夠被webview的onJsAlert監聽
  • confirm,能夠被webview的onJsConfirm監聽
  • console.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調用微信提供的一些原生功能。

相關文章
相關標籤/搜索