咱們經常使用的web前端框架其實簡單稱呼叫web框架,現階段web前端技術成熟,從視覺體驗到用戶體驗都是比較好的,這也是從簡單到複雜的web前端框架技術實現的,在國內前端技術開發人員也是很是的多,市面上的前端框架能夠說是眼花繚亂,這裏寫這篇文章就是讓你在使用不一樣的前端框架的時候可以明確的知道本身的選擇。html
Web前端框架工做原理:前端
在咱們討論框架以前,咱們須要理解 Web 如何「工做」的。爲此,咱們將深刻挖掘你在瀏覽器裏輸入一個 URL 按下 Enter 以後都發生了什麼。在你的瀏覽器中打開一個新的標籤,瀏覽http://www.uileader.com。咱們討論爲了顯示這個頁面,瀏覽器都作了什麼事情(不關心 DNS 查詢)。程序員
Web 服務器web
每一個頁面都以 HTML 的形式傳送到你的瀏覽器中,HTML 是一種瀏覽器用來描述頁面內容和結構的語言。那些負責發送 HTML 到瀏覽器的應用稱之爲「Web 服務器」,會讓你迷惑的是,這些應用運行的機器一般也叫作 web 服務器。正則表達式
然而,最重要的是要理解,到最後全部的 web 應用要作的事情就是發送 HTML 到瀏覽器。無論應用的邏輯多麼複雜,最終的結果老是將 HTML 發送到瀏覽器(我故意將應用能夠響應像 JSON 或者 CSS 等不一樣類型的數據忽略掉,由於在概念上是相同的)。數據庫
HTTP編程
瀏覽器從 web 服務器(或者叫應用服務器)上使用 HTTP 協議下載網站,HTTP 協議是基於一種 請求-響應(request-response)模型的。客戶端(你的瀏覽器)從運行在物理機器上的 web 應用請求數據,web 應用反過來對你的瀏覽器請求進行響應。設計模式
重要的一點是,要記住通訊老是由客戶端(你的瀏覽器)發起的,服務器(也就是 web 服務器)沒有辦法建立一個連接,發送沒有通過請求的數據給你的瀏覽器。若是你從 web 服務器上接收到數據,必定是由於你的瀏覽器顯示地發送了請求。瀏覽器
HTTP Methods緩存
在 HTTP 協議中,每條報文都關聯方法(method 或者 verb),不一樣的 HTTP 方法對應客戶端能夠發送的邏輯上不一樣類型的請求,反過來也表明了客戶端的不一樣意圖。例如,請求一個 web 頁面的 HTML,與提交一個表單在邏輯上是不一樣的,因此這兩種行爲就須要使用不一樣的方法。
HTTP GET
GET 方法就像其聽起來的那樣,從 web 服務器上 get(請求)數據。GET 請求是到目前位置最多見的一種 HTTP 請求,在一次 GET 請求過程當中,web 應用對請求頁面的 HTML 進行響應以外,就不須要作任何事情了。特別的,web 應用在 GET 請求的結果中,不該該改變應用的狀態(好比,不能基於 GET 請求建立一個新賬號)。正是由於這個緣由,GET 請求一般認爲是「安全」的,由於他們不會致使應用的改變。
HTTP POST
顯然,除了簡單的查看頁面以外,應該還有更多與網站進行交互的操做。咱們也可以嚮應用發送數據,例如經過表單。爲了達到這樣的目的,就須要一種不一樣類型的請求方法:POST。POST 請求一般攜帶由用戶輸入的數據,web 應用收到以後會產生一些行爲。經過在表單裏輸入你的信息登陸一個網站,就是 POST 表單的數據給 web 應用的。
不一樣於 GET 請求,POST 請求一般會致使應用狀態的改變。在咱們的例子中,當表單 POST 以後,一個新的帳戶被建立。不一樣於 GET 請求,POST 請求不老是生成一個新的 HTML 頁面發送到客戶端,而是客戶端使用響應的響應碼(response code)來決定對應用的操做是否成功。
HTTTP Response Codes
一般來講,web 服務器返回 200 的響應碼,意思是,「我已經完成了你要求我作的事情,一切都正常」。響應碼老是一個三位數字的代號,web 應用在每一個響應的同時都發送一個這樣的代號,代表給定的請求的結果。響應碼 200 字面意思是「OK」,是響應一個 GET 請求大多狀況下都使用的代號。然而對於 POST 請求, 可能會有 204(「No Content」)發送回來,意思是「一切都正常,可是我不許備向你顯示任何東西」。
Web 應用
你能夠僅僅使用 HTTP GET 和 POST 作不少事情。一個應用程序負責去接收一個 HTTP 請求,同時給以 HTTP 響應,一般包含了請求頁面的 HTML。POST 請求會引發 web 應用作出一些行爲,多是往數據庫中添加一條記錄這樣的。還有不少其它的 HTTP 方法,可是咱們目前只關注 GET 和 POST。
那麼最簡單的 web 應用是什麼樣的呢?咱們能夠寫一個應用,讓它一直監聽 80 端口(著名的 HTTP 端口,幾乎全部 HTTP 都發送到這個端口上)。一旦它接收到等待的客戶端發送的請求鏈接,而後它就會回覆一些簡單的 HTML。
下面是程序的代碼:
import socketHOST = ''PORT = 80listen_socket = socket.socket(socket.AF_INET, socket.SOCK_STREAM)listen_socket.bind((HOST, PORT))listen_socket.listen(1)connection, address = listen_socket.accept()request = connection.recv(1024)connection.sendall(b"""HTTP/1.1 200 OKContent-type: text/html
<html> <body> <h1>Hello, World!</h1> </body></html>""")connection.close()
(若是上面的代碼不工做,試着將 PORT 改成相似 8080 這樣的端口。)
這個代碼接收簡單的連接和簡單的請求,無論請求的 URL 是什麼,它都會響應 HTTP 200(因此,這不是一個真正意義上的 web 服務器)。Content-type:text/html 行代碼的是 header 字段,header 用來提供請求或者響應的元信息。這樣,咱們就告訴了客戶端接下來的數據是 HTML。
請求的剖析
若是看一下測試上面程序的 HTTP 請求,你會發現它和 HTTP 響應很是相似。第一行<HTTP Method> <URL> <HTTP version>,在這個例子中是 GET / HTTP/1.0。第一行以後就是一些相似Accept: */* 這樣的頭(意思是咱們但願在響應中接收任何內容)。
咱們響應和請求有着相似的第一行,格式是<HTTP version> <HTTP Status-code> <Status-code Reason Phrase>,在外面的例子中是HTTP/1.1 200 OK 。接下來是頭部,與請求的頭部有着相同的格式。最後是響應的實際包含的內容。注意,這會被解釋爲一個字符串或者二進制文件, Content-type 頭告訴客戶端怎樣去解釋響應。
解決路由和模板兩大問題
圍繞創建 web 應用的全部問題中,兩個問題尤爲突出:
1.咱們如何將請求的 URL 映射處處理它的代碼上?
2.咱們怎樣動態地構造請求的 HTML 返回給客戶端,HTML 中帶有計算獲得的值或者從數據庫中取出來的信息?
每一個 web 框架都以某種方法來解決這些問題,也有不少不一樣的解決方案。用例子來講明更容易理解,因此我將針對這些問題討論 Django 和 Flask 的解決方案。可是,首先咱們還須要簡單討論一下 MVC 。
Django 中的 MVC
Django 充分利用 MVC 設計模式。 MVC,也就是 Model-View-Controller (模型-視圖-控制器),是一種將應用的不一樣功能從邏輯上劃分開。models 表明的是相似數據庫表的資源(與 Python 中用 class 來對真實世界目標建模使用的方法大致相同)。controls 包括應用的業務邏輯,對 models 進行操做。爲了動態生成表明頁面的 HTML,須要 views 給出全部要動態生成頁面的 HTML 的信息。
在 Django 中有點讓人困惑的是,controllers 被稱作 views,而 views 被稱爲 templates。除了名字上的有點奇怪,Django 很好地實現了 MVC 的體系架構。
Django 中的路由
路由是處理請求 URL 到負責生成相關的 HTML 的代碼之間映射的過程。在簡單的情形下,全部的請求都是有相同的代碼來處理(就像咱們以前的例子那樣)。變得稍微複雜一點,每一個 URL 對應一個 view function 。舉例來講,若是請求 www.foo.com/bar 這樣的 URL,調用 handler_bar() 這樣的函數來產生響應。咱們能夠創建這樣的映射表,枚舉出咱們應用支持的全部 URL 與它們相關的函數。
然而,當 URL 中包含有用的數據,例如資源的 ID(像這樣 www.foo.com/users/3/) ,那麼這種方法將變得很是臃腫。咱們如何將 URL 映射到一個 view 函數,同時如何利用咱們想顯示 ID 爲 3 的用戶?
Django 的答案是,將 URL 正則表達式映射到能夠帶參數的 view 函數。例如,我假設匹配^/users/(?P<id>\d+)/$ 的 URL 調用 display_user(id) 這樣的函數,這兒參數 id 是正則表達式中匹配的 id。這種方法,任何 /users/<some_number>/ 這樣的 URL 都會映射到 display_user 函數。這些正則表達式能夠很是複雜,包含關鍵字和參數。
Flask 中的路由
Flask 採起了一點不一樣的方法。將一個函數和請求的 URL 關聯起來的標準方法是經過使用 route() 裝飾器。下面是 Flask 代碼,在功能上和上面正則表達式方法相同:
@app.route('/users/<id:int>/')
def display_user(id):
# ...
就像你看到的這樣,裝飾器使用幾乎最簡單的正則表達式的形式來將 URL 映射到參數。經過傳遞給route() 的 URL 中包含的 <name:type> 指令,能夠提取到參數。路由像 /info/about_us.html 這樣的靜態 URL,能夠像你預想的這樣 @app.route('/info/about_us.html') 處理。
經過 Templates 產生 HTML
繼續上面的例子,一旦咱們有合適的代碼映射到正確的 URL,咱們如何動態生成 HTML?對於 Django 和 Flask,答案都是經過 HTML Templating。
HTML Templating 和使用 str.format() 相似:須要動態輸出值的地方使用佔位符填充,這些佔位符後來經過 str.format() 函數用參數替換掉。想象一下,整個 web 頁面就是一個字符串,用括號標明動態數據的位置,最後再調用 str.format() 。Django 模板和 Flask 使用的模板引擎 Jinja2 都使用的是這種方法。
然而,不是全部的模板引擎都能相同的功能。Django 支持在模板裏基本的編程,而 Jinja2 只能讓你執行特定的代碼(不是真正意義上的代碼,但也差很少)。Jinja2 能夠緩存渲染以後的模板,讓接下來具備相同參數的請求能夠直接從緩存中返回結果,而不是用再次花大力氣渲染。
數據庫交互
Django 有着「功能齊全」的設計哲學,其中包含了一個 ORM(Object Realational Mapper,對象關係映射),ORM 的目的有兩方面:一是將 Python 的 class 與數據庫表創建映射,而是剝離出不一樣數據庫引擎直接的差別。沒人喜歡 ORM,由於在不一樣的域之間映射永遠不完美,然而這還在承受範圍以內。Django 是功能齊全的,而 Flask 是一個微框架,不包括 ORM,儘管它對 SQLAlchemy 兼容性很是好,SQLAlchemy 是 Django ORM 的最大也是惟一的競爭對手。
內嵌 ORM 讓 Django 有能力建立一個功能豐富的 CRUD 應用,從服務器端角度來看,CRUD(CreateRead Update Delete)應用很是適合使用 web 框架技術。Django 和 Flask-SQLchemy 能夠直接對每一個 model 進行不一樣的 CRUD 操做。
總結:
到如今爲止,web前端框架的目的應該很是清晰了:向程序員隱藏了處理 HTTP 請求和響應相關的基礎代碼。至於隱藏多少這取決於不一樣的框架,Django 和 Flask 走向了兩個極端:Django 包括了每種情形,幾乎成了它致命的一點;Flask 立足於「微框架」,僅僅實現 web 應用須要的最小功能,其它的不經常使用的 web 框架任務交由第三方庫來完成。
可是最後要記住的是,Python web 框架都以相同的方式工做的:它們接收 HTTP 請求,分派代碼,產生 HTML,建立帶有內容的 HTTP 響應。事實上,全部主流的服務器端框架都以這種方式工做的( JavaScript 框架除外)。希望瞭解了這些框架的目的,你可以在不一樣的框架之間選擇適合你應用的框架進行開發。