其實普通web應用,實際上就是對http的應用,http是一種基於TCP協議的網絡傳輸協議,工作在應用層,作為web開發者,我主要從http的角度來看待這個問題:
首先,對於http肯定是有客戶端和服務器的,在這個語境中,客戶端和服務器本質上也都是一個軟件,實現了http協議相關標准的軟件。客戶端一般由都是由瀏覽器充當,也就是說,在瀏覽器中實現了http客戶端的相關功能。而服務器的實現就多種多樣啦,我們可以用java寫servlet,c#寫ASP.net,還有php,ruby,Python,nodejs等。實際上我想,http服務在操作系統底層應該有實現,而這些語言只不過是利用操作系統的http服務封裝成自己的接口供開發人員編寫web服務器程序。而我們熟悉的IIS,Tomcat,Apache,Web logic,都是能夠作為某些web服務器容器的大型服務器平台,它們都會包括很多更為強大的功能。一般來說,我們這里所說的服務器指的是自己用特定語言寫的web應用服務器程序。nodejs不需要web容器,本身就有對http的直接應用模塊,所以用nodejs創建一個web服務器是很方便的。
整體通信
有了客戶端和服務器,就可以開始通信了,整體上分為3個步驟:
- 因為http是構建在TCP之上,那么自然是要經過3次握手創建連接。
- 創建連接后,服務器會根據url請求中的信息進行處理,作出響應,一般來說是找到一個html文件返回給客戶端。
- 客戶端即瀏覽器得到html,進行渲染。
下面詳細說下這3個步驟
創建連接
這個跟網絡關聯多一些,我網絡學的馬馬虎虎,只能大體說一下。對於http的客戶端,它的輸入就是一個url,而對於創建連接,它需要的只是url的host(主機)部分,而主機地址一般是網站的域名,所以第一步肯定是是域名解析,也就是要通過DNS服務器進行域名解析得到網站的ip地址,然后向這個ip地址發送一個連接建立的請求,如果服務器接收到請求會返回一個確認,客戶端得到確認再次發送確認,連接建立成功。當然在這個過程中還會涉及到很多細節,這是網絡中的知識,在這里不多講。
服務器處理
建立好連接后,客戶端就會發送http請求,請求信息包含一個頭部和一個請求體,
一般的web技術都會把請求進行封裝然后交給我們的服務器進行處理,比如servlet會把請求封裝成httpservletrequest對象,把響應封裝成httpsevletresponse對象。nodejs的http模塊,當你創建服務器的時候會寫一個回調函數,回調的參數用來接受http請求對象和響應對象,然后在回調函數中對請求進行處理。
在請求對象中我們可以得到path(路徑),queryString(查詢字符串),body(post請求中提交的數據)等。對請求的處理就可以很復雜,也可以很簡單。我們可以根據path找到客戶端想要的文件,讀取這個文件,然后通過響應對象把內容返回給客戶端,這個過程,不同的技術提供的api可能不同,尤其是用慣了MVC框架的人,可能只是指定一個文件,或者在配置文件中設置一下就好了。但是最終的實現肯定是符合http響應標准的,也就是要有一個響應頭和一個響應體。我一般接觸到的設置響應頭就是設置content-type來決定MIME類型,設置Cache-Control,last-modify等緩存內容。一般來說返回給客戶端的內容是一個html字符串,然后content-type設為text/html。當然也可能客戶端請求的是一個image文件,那么就是讀取image文件后,content-type可能設為image/png,image/jpg等,然后把內容返回給客戶端。這樣一次對請求的處理就結束了。
當然這個過程太單一,而且處理過程也可能很復雜,又有數據的操作,又有頁面的構建,又有路徑的查找匹配,又有文件的讀取等等,於是就出現了MVC框架以及后來演變出的各種MV*框架。這里不細講MVC的內容,因為需要很長的篇幅。只是概述一下MVC主要做了什么,在我看來最重要的就是解耦和模塊化。我認為MVC實現最重要的有兩點:
- 路由匹配,http請求的path中就不需要指定到具體的視圖位置,而是按照我們制定的規則進行匹配,這樣就有了很大的靈活性,可編程性。
- 模板技術,一般來說我們最后返回給客戶端的是一個html字符串,而有時候這個字符串往往不是靜態單一的,有的時候需要和數據進行結合,需要拼接。這就帶來了很大的麻煩,模板技術為解決這個問題帶來很大的便利性,同時又能夠把視圖和數據進行解耦。
客戶端渲染
客戶端接收到服務器傳來的響應對象,從中得到html字符串和MIME,根據MIME知道了要用頁面渲染引擎來處理內容即html字符串,於是進入頁面渲染階段,這又是一個很龐雜的體系。我只能大體上說一下:
從瀏覽器的角度講,它包含幾大組件,網絡功能(比如http的實現)算是其中之一,渲染引擎也是其中之一,還有其它的一些比如自己UI界面,javascript解釋器,客戶端數據存儲等等。在這里我們主要關注渲染引擎和javascript解釋器,對於web開發者來說,這才是瀏覽器的核心。
我們能夠在瀏覽器中看到一個頁面,那么這個頁面是怎么出現的呢?實際上就是調用底層繪圖API給畫出來的。不同的渲染引擎,它的實現也不同,主流的引擎包括IE的Trident,chrome和safary的webkit,firefox的Gecko,chrome又出了一個Blink,放棄webkit。於是乎才有了讓人頭疼的各種兼容性問題。
整體上頁面渲染的過程大致是這樣的:
渲染引擎得到html字符串作為輸入,然后對html進行轉換,轉化成能夠被DOM處理的形式,接着轉換成一個dom樹,在解析html的過程,解析到<link>,<script>,<img>等一些請求標簽時,會發送請求把對應的內容獲取到。這時又會同步進行css的解析,構建出css樣式規則應用到dom樹上,然后進行一定的布局處理,比如標記節點塊在瀏覽器中的坐標等形成最終的渲染樹,最后根據這棵渲染樹在瀏覽器窗口中進行繪制。
最終我們就看到了頁面的樣子。
當然在頁面渲染過程中還會同步進行javascript的解析,而且這兩者是在同一個線程中的,所以一旦javascript死循環,頁面的渲染也就進行不下去了。
以上是我從一個web開發者的角度思考的整個過程。如果從別的角度更細化的去想,還包括許多內容:
比如整個網絡通信中協議的封裝:
在本機中,把要傳輸的內容即請求對象在應用層上加上App首部,傳遞到傳輸層加上TCP首部,到網絡層加上IP首部,數據鏈路層加上以太網的首部和尾部,然后轉換成bit流進入網絡環境中。到達主機后在一層層解封裝,最后把內容交給服務器程序。
再比如這個過程中的認證,加密,安全,編碼等問題都會有一定的處理,不過這些內容我就不是很了解。