什么是 web 框架扼鞋?

聲明:本文轉(zhuǎn)載自http://cizixs.com/2015/09/21/what-is-a-web-framework

Web 應(yīng)用框架艾扮,簡(jiǎn)稱為 web 框架谒主,是編寫 web 應(yīng)用程序的基石掉盅。不管簡(jiǎn)單的博客系統(tǒng)也拜,還是 Ajax 為主的應(yīng)用,網(wǎng)絡(luò)上所有的頁(yè)面都是代碼構(gòu)成的趾痘。進(jìn)來(lái)我發(fā)現(xiàn)慢哈,很多想學(xué)習(xí)諸如 Flask 或者 Django 等 web 框架的開(kāi)發(fā)者,并不很了解 web 框架是什么永票,它們的作用和工作原理卵贱。這篇文章,我將會(huì)講一下這個(gè)通常會(huì)被忽略的話題侣集。希望讀完這篇文章键俱,你能比較深刻地理解 web 框架到底是什么,還有為什么會(huì)有 web 框架世分。這些知識(shí)將有利于你學(xué)習(xí)新的 web 框架编振,而且在選擇 web 框架的時(shí)候有法可依。

WEB 是什么工作的臭埋?

在討論框架之前踪央,我們要先了解一下網(wǎng)頁(yè)是怎么工作。我們就從你在瀏覽器輸入一個(gè)網(wǎng)址瓢阴,摁下 enter 鍵說(shuō)起畅蹂。打開(kāi)你的瀏覽器,輸入 http://jeffknupp.com(譯者注:原作者的個(gè)人網(wǎng)站首頁(yè))炫掐,我們來(lái)看看你的瀏覽器做了那些事情(DNS 查詢的 buffer 就略過(guò))魁莉,才能顯示你看到的網(wǎng)頁(yè)。

web servers 和 web … servers …

瀏覽器接接收到的網(wǎng)頁(yè)都是 HTML 文件募胃,HTML 是一種描述網(wǎng)頁(yè)內(nèi)容和結(jié)構(gòu)的語(yǔ)言旗唁。負(fù)責(zé)給瀏覽器發(fā)送 HTML 的程序稱為 web server,容易混淆的是痹束,這個(gè)應(yīng)用程序所在的機(jī)器通常也被稱為 web server检疫。

最重要的一點(diǎn)是,所有的 web 應(yīng)用做的事情就是把 HTML 內(nèi)容發(fā)送給瀏覽器祷嘶。不論這個(gè) web 應(yīng)用有多么復(fù)雜屎媳,最終的任務(wù)都是把 HTML(我故意忽略掉其他格式的內(nèi)容夺溢,比如 JSON,CSS 文件烛谊,因?yàn)樵矶际且粯拥模┌l(fā)送給瀏覽器风响。

問(wèn)題來(lái)了:web 應(yīng)用如何知道要發(fā)送什么內(nèi)容給瀏覽器呢?答案:它會(huì)發(fā)送瀏覽器請(qǐng)求的內(nèi)容丹禀。

HTTP

瀏覽器從 web server 下載內(nèi)容所用的是 HTTP 協(xié)議(協(xié)議在計(jì)算機(jī)科學(xué)中状勤,指的是雙方通信所共同遵循的數(shù)據(jù)格式和通信步驟)。HTTP 協(xié)議的基礎(chǔ)是 請(qǐng)求-應(yīng)答 (request-response) 模型双泪〕炙眩客戶端(你的瀏覽器)請(qǐng)求 某臺(tái)物理機(jī)上 web 應(yīng)用的數(shù)據(jù),web server 則負(fù)責(zé) 應(yīng)答請(qǐng)求的數(shù)據(jù)焙矛。

有個(gè)重要的事情是:所有的通信都是客戶端(你的瀏覽器)發(fā)起的葫盼。服務(wù)端(web server)是不可能主動(dòng)連接你,發(fā)送沒(méi)有請(qǐng)求的數(shù)據(jù)的村斟。如果你收到了數(shù)據(jù)贫导,只是因?yàn)槟愕臑g覽器主動(dòng)請(qǐng)求了這些數(shù)據(jù)。

HTTP 方法

HTTP 協(xié)議的每條消息都有對(duì)應(yīng)的方法(method)蟆盹,不同的方法對(duì)應(yīng)了客戶端能發(fā)起的不同請(qǐng)求脱盲,也對(duì)應(yīng)了客戶端不同的意圖。比如日缨,請(qǐng)求 網(wǎng)頁(yè)的 HTML 和提交一個(gè)表格在邏輯上是不同的,所以這兩種方法需要兩種不同的方法掖看。

HTTP GET

顧名思義匣距,GET 方法就是從 web server 獲取(get)數(shù)據(jù)哎壳,GET 請(qǐng)求也是目前最常用的 HTTP 請(qǐng)求毅待。 處理 GET 請(qǐng)求的過(guò)程中,web 應(yīng)用只需要返回請(qǐng)求的數(shù)據(jù)归榕,無(wú)需其他操作尸红。尤其是,不應(yīng)該修改應(yīng)用的狀態(tài)(比如刹泄, GET 請(qǐng)求不應(yīng)該導(dǎo)致一個(gè)新用戶被創(chuàng)建)外里。因?yàn)檫@個(gè)原因,GET 請(qǐng)求通常被看做是 安全 的特石。

HTTP POST

和網(wǎng)站的交互盅蝗,明顯不只是查看網(wǎng)頁(yè)的。我們還會(huì)通過(guò)表格等形式發(fā)送數(shù)據(jù)給 web 應(yīng)用姆蘸,這些操作需要用到另外一種請(qǐng)求:POST墩莫。POST 請(qǐng)求通常會(huì)傳遞用戶創(chuàng)建的信息芙委,導(dǎo)致 web 應(yīng)用執(zhí)行某些動(dòng)作。輸入自己的信息狂秦,來(lái)注冊(cè)某個(gè)網(wǎng)站就會(huì)用到 POST 請(qǐng)求灌侣,請(qǐng)求中會(huì)包含你輸入的數(shù)據(jù)。

和 GET 請(qǐng)求不同的是裂问, POST 請(qǐng)求通常會(huì)導(dǎo)致 web 應(yīng)用狀態(tài)的改變侧啼。上面提及的例子中,表單被提交后愕秫,一個(gè)新的用戶會(huì)被創(chuàng)建慨菱。還有一點(diǎn)不同,POST 請(qǐng)求的結(jié)果可能不會(huì)返回 HTML 數(shù)據(jù)給客戶端戴甩,客戶端需要通過(guò) response code 來(lái)判斷操作是否成功符喝。

HTTP response code

正常情況下,web server 會(huì)返回 200 的 response code甜孤,意思是:我已經(jīng)完成了你要我做的事情协饲,并且一切都沒(méi)有問(wèn)題。response code 是三位的數(shù)字缴川,每次應(yīng)答都要包含一個(gè) response code茉稠,來(lái)標(biāo)識(shí)請(qǐng)求的結(jié)果。200 表示 OK把夸,是 GET 方法常見(jiàn)的返回值而线。POST 請(qǐng)求經(jīng)常會(huì)返還 204(No contnet),表示:一切正常恋日,但是我沒(méi)有數(shù)據(jù)可以展示給你膀篮。

還需要注意的是:POST 請(qǐng)求發(fā)送給的 url,可能和數(shù)據(jù)發(fā)送出去的 url 不同岂膳。繼續(xù)以我們的注冊(cè)頁(yè)面為例誓竿,注冊(cè)表可能位于 http://foo.com/signup,點(diǎn)擊 submit 之后谈截,包含著注冊(cè)數(shù)據(jù)的 POST 請(qǐng)求可能被發(fā)送到 http://foo.com/process_signup筷屡。POST 請(qǐng)求要發(fā)送到的地址,一般在注冊(cè)表格的 HTML 源碼里指定簸喂。

Web 應(yīng)用

掌握 GET 和 POST 方法就能做很多事情毙死,因?yàn)樗鼈兪?web 上最常用的兩個(gè)方法闯参√猎遥總結(jié)一下,web 應(yīng)用就是接收 HTTP 請(qǐng)求乃沙,然后返回 HTTP 應(yīng)答诽表,一般是包含請(qǐng)求數(shù)據(jù)的 HTML唉锌。POST 方法會(huì)導(dǎo)致 web 應(yīng)用執(zhí)行某些動(dòng)作隅肥,例如在數(shù)據(jù)庫(kù)添加一條記錄。當(dāng)然還有其他的 HTTP 方法袄简,但目前我們只需要關(guān)心 GET 和 POST 就足夠啦腥放。

最簡(jiǎn)單的 web 應(yīng)用長(zhǎng)什么樣呢?我們就來(lái)寫一個(gè)監(jiān)聽(tīng)在 80 端口的 web 應(yīng)用绿语,一旦和客戶端建立連接秃症,就等待客戶端發(fā)起請(qǐng)求,并返回非常簡(jiǎn)單的 HTML吕粹。

這個(gè)程序是這樣的:

import socket

HOST = ''
PORT = 80
listen_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("""HTTP/1.1 200 OK
Content-type: text/html


<html>
    <body>
        <h1>Hello, World!</h1>
    </body>
</html>""")
connection.close()

(如果上面的程序報(bào)端口錯(cuò)誤种柑,可以把 PORT 的值修改成其他值,比如 8080匹耕。)

上面的代碼只會(huì)接收一個(gè)連接和一個(gè)請(qǐng)求聚请,不管請(qǐng)求的 URL 是什么,都會(huì)返回同樣的 HTTP 內(nèi)容稳其,response code 是 200驶赏。(很明顯,這不算真正的 web server)既鞠。在這個(gè)例子煤傍,我們告訴客戶端,返回的數(shù)據(jù)格式為 HTML嘱蛋,而不是其他的格式蚯姆,比如 JSON。

request 請(qǐng)求解析

如果看一下上面例子中發(fā)送的 HTTP 請(qǐng)求(譯者注:可以使用 chrome 的 inspect elements -> Network洒敏,或者抓包工具 tcpdump 等工具查看發(fā)送的 HTTP 請(qǐng)求)蒋失,就會(huì)發(fā)現(xiàn)它和應(yīng)答很相似。請(qǐng)求的第一行是:

<HTTP Method> <URL> <HTTP version>

在這個(gè)例子里就是 GET / HTTP/1.1桐玻。第一行后面跟著的是請(qǐng)求的頭部(headers),比如 Accept: /(表示可以接收任何格式的內(nèi)容作為應(yīng)答)荆萤∧餮ィ基本上就這么多。

我們發(fā)送的應(yīng)答也是類似的格式链韭,第一行是:

<HTTP version> <HTTP Status-Code> <Status-Code Reason-Phrase>

這個(gè)例子中就是 HTTP/1.1 200 OK偏竟。然后是頭部,和請(qǐng)求的頭部一樣敞峭。最后是應(yīng)答的實(shí)際內(nèi)容踊谋。注意:應(yīng)答也可能是字符串或者二進(jìn)制對(duì)象,頭部的 Content-typ 就是來(lái)標(biāo)識(shí)應(yīng)答內(nèi)容旋讹,讓客戶端來(lái)解析的殖蚕。

web server 更多瑣碎的細(xì)節(jié)

要在上面的例子基礎(chǔ)上繼續(xù)擴(kuò)展的話轿衔,還有很多需要我們來(lái)解決的問(wèn)題:

  1. 怎么查看請(qǐng)求的 URL,然后返回不同的頁(yè)面睦疫?
  2. 除了 GET 請(qǐng)求害驹,怎么處理 POST 請(qǐng)求?
  3. 怎么處理很復(fù)雜的概念蛤育,比如 sessions 和 cookies宛官?
  4. 如何擴(kuò)展這個(gè)應(yīng)用,讓它可以同時(shí)處理數(shù)千條連接瓦糕?

可以想象底洗,沒(méi)人會(huì)愿意每次編寫 web 應(yīng)用的時(shí)候都要自己處理這些問(wèn)題。為了解決這個(gè)難題咕娄,就會(huì)存在很多的軟件包幫你處理這些煩人的細(xì)節(jié)亥揖,讓開(kāi)發(fā)者可以把心思放到業(yè)務(wù)邏輯上。記住谭胚,不管 web 框架多么負(fù)責(zé)徐块,其最核心的功能和我們上面的例子是一樣的:監(jiān)聽(tīng)客戶端請(qǐng)求,然后返回 HTML 給客戶端灾而。

NOTE:客戶端的框架和上面的內(nèi)容迥然不同胡控。

解決兩個(gè)難題:路由(routing)和模板(templates)

在構(gòu)建 web 應(yīng)用的所有的問(wèn)題中,有兩個(gè)比較突出:

  1. 怎么把請(qǐng)求 URL 和處理它的那部分代碼對(duì)應(yīng)起來(lái)旁趟?
  2. 怎么動(dòng)態(tài)地生產(chǎn)請(qǐng)求內(nèi)容昼激?包括所有要計(jì)算的值,和從數(shù)據(jù)庫(kù)獲取的信息锡搜?

每個(gè) web 框架解決這兩個(gè)問(wèn)題的方法都不太相同橙困,我們就舉 Flask 和 Django 的例子來(lái)說(shuō)明這個(gè)問(wèn)題。首先耕餐,我們還要來(lái)說(shuō)一下 MVC 模式凡傅。

Django 中的 MVC

Django 采用 MVC 模式, 所以要求使用這個(gè)框架的代碼都遵循這個(gè)模式肠缔。MVC夏跷,是 Model-View-Controller 的縮寫,用來(lái)分離應(yīng)用的不同責(zé)任明未。數(shù)據(jù)庫(kù)表所代表的資源用 models 來(lái)表示槽华,controllers 負(fù)責(zé)應(yīng)用的業(yè)務(wù)邏輯和操作 models。Views 則負(fù)責(zé)動(dòng)態(tài)生成代表頁(yè)面的 HTML趟妥。

不過(guò)容易讓人混淆的是猫态,django 中 controllers 被稱作 views,而 views 被稱為 templates。除了命名外亲雪,django 算是比較直接的 MVC 架構(gòu)勇凭。

Django 的路由(routing)機(jī)制

這里說(shuō)的路由(routing)就是把請(qǐng)求的 URL 對(duì)應(yīng)到處理生成相關(guān) HTML 的代碼。最簡(jiǎn)單的例子匆光,所有的請(qǐng)求都是相同的代碼處理(就是我們之前編寫的代碼)套像。復(fù)雜一點(diǎn)呢,每個(gè)的 URL 都對(duì)應(yīng)一個(gè)不同的 view function终息。比如夺巩,有個(gè)地方的邏輯是接收到 www.foo.com/bar 請(qǐng)求,就把它交給 handle_bar() 函數(shù)處理周崭。我們可以這樣依次編寫出所有 url 對(duì)應(yīng)的處理函數(shù)柳譬。

不過(guò),這個(gè)方法有個(gè)致命傷:沒(méi)有辦法處理帶有動(dòng)態(tài)數(shù)據(jù)的 URL续镇,比如說(shuō)資源的 ID(例如 http://www.foo.com/users/3)美澳。我們?cè)趺窗堰@個(gè) URL 映射到函數(shù),同時(shí)能傳過(guò)去用戶 ID 信息呢摸航?

Django 采用的方法是利用正則表達(dá)式:用正則表達(dá)式匹配 URL制跟,然后把匹配的數(shù)據(jù)作為參數(shù)傳遞給處理函數(shù)。比如酱虎,我可以說(shuō)匹配 ^/users/(?P<id>\d+)/$ 的 URL 會(huì)調(diào)用 display_user(id) 函數(shù)雨膨,其中 id 就是正則表達(dá)式括號(hào)里匹配的內(nèi)容。利用這種方式读串,任何 /users/<some number> 類型的 URL 都能對(duì)應(yīng)到 display_user 函數(shù)聊记,并且正則表達(dá)式可以無(wú)限復(fù)雜,包含任意的關(guān)鍵字和未知參數(shù)恢暖。

Flask 的 路由機(jī)制

Flask 采用的是另外一種方法排监。把 url 對(duì)應(yīng)到函數(shù)參照的是 route() 裝飾器。下面的 Flask 代碼和上面提到的正則表達(dá)式代碼功能相同:

@app.route('/users/<id:int>/')
def display_user(id):
    # ...

如你所見(jiàn)杰捂,裝飾器使用的是簡(jiǎn)化版的正則表達(dá)式來(lái)傳遞參數(shù)舆床,參數(shù)被 route 參數(shù)中 <name:type> 的指令捕獲。要路由 /info/about.html 這樣的頁(yè)面嫁佳,就需要 @app.route('/info/about_us.html')挨队。

根據(jù)模板生成 HTML

繼續(xù)上面的例子,一旦我們知道怎么把 URL 對(duì)應(yīng)到邏輯代碼脱拼,那么要怎么動(dòng)態(tài)地生成 HTML,并且方便開(kāi)發(fā)者手動(dòng)編輯呢坷备?Django 和 Flask 兩者這次方法一樣熄浓,那就是 —— HTML 模板。

HTML 模板 有點(diǎn)像 string.format():預(yù)期的輸出首先要用站位標(biāo)識(shí),然后再填入動(dòng)態(tài)的數(shù)據(jù)赌蔑「┰冢可以把這個(gè)網(wǎng)頁(yè)想象成一個(gè)字符串,里面用括號(hào)標(biāo)識(shí)動(dòng)態(tài)的數(shù)據(jù)娃惯,最后調(diào)用 str.format() 生成最終的結(jié)果跷乐。Django 的 模板引擎和 Flask 采用的 jinja2 都是這個(gè)原理。

不過(guò)趾浅,并不是所有的模板引擎地位都一樣愕提。Django 的模板只支持簡(jiǎn)單的變成,而 Jinja2 卻能讓你執(zhí)行任意的代碼(當(dāng)然并發(fā)完全可以皿哨,不過(guò)已經(jīng)很近似)浅侨。Jinja2 很會(huì) cache 渲染的結(jié)果,下次有同樣的參數(shù)傳過(guò)來(lái)的時(shí)候证膨,就會(huì)直接從 cache 獲取結(jié)果如输,而不需要重新渲染。

數(shù)據(jù)庫(kù)集成

Django央勒,宣稱“自帶電池”(batteries included)不见,然后也會(huì)包含 ORM(Object Relational Mapper)。ORM 的目的有兩個(gè):把 python 的類映射到數(shù)據(jù)的表結(jié)構(gòu)崔步,和通過(guò)封裝隱藏不同數(shù)據(jù)庫(kù)之間的差異(第一點(diǎn)是它更主要的功能)稳吮。沒(méi)有人喜歡 ORM(因?yàn)椴煌蛑g的 mapping 從不完美),不過(guò)這些缺點(diǎn)都是可以接受的刷晋。 Django 功能比較全面盖高,F(xiàn)lask 作為一個(gè)微框架,并不自帶 ORM(不過(guò)它很好兼容 SQLAlchemy眼虱,Django ORM 最大的競(jìng)爭(zhēng)者)喻奥。

因?yàn)榘?ORM,Django 能夠創(chuàng)建功能齊全的 CRUD 應(yīng)用捏悬。 CRUD(Create Read Update Delete)是 web 框架(服務(wù)器端)最美好的地方撞蚕,Django 和 Flask-SQLAlchemy 使得 CRUD 操作很直接。

Web 框架總結(jié)

寫到這过牙,web 框架出現(xiàn)的目的也比較明確了:隱藏基礎(chǔ)而又煩人的處理 HTTP 請(qǐng)求和應(yīng)答的代碼甥厦。至于要隱藏多少內(nèi)容,就要看框架啦寇钉。Django 和 Flask 代表了兩個(gè)極端刀疙。Django 每種情況都有涉及,而 Flask 標(biāo)榜自己是“微框架“扫倡,只處理 web 程序最核心的功能谦秧,依賴其他三方插件來(lái)完成其他不常用的工作。

寫了這么多,記住疚鲤,所有的 python web 框架功能方式都一樣:它們接收 HTTP 請(qǐng)求锥累,然后分發(fā)任務(wù),并生成 HTML集歇,然后返回包含 HTML 的 HTTP 應(yīng)答桶略。事實(shí)上,所有的 server 端框架(除了 Javascript 框架)都是這么工作的诲宇。希望际歼,看完這篇文章,你已經(jīng)知道 web 框架的目的焕窝,也知道怎么去選擇 web 框架啦蹬挺。

?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末,一起剝皮案震驚了整個(gè)濱河市它掂,隨后出現(xiàn)的幾起案子巴帮,更是在濱河造成了極大的恐慌,老刑警劉巖虐秋,帶你破解...
    沈念sama閱讀 206,378評(píng)論 6 481
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件榕茧,死亡現(xiàn)場(chǎng)離奇詭異,居然都是意外死亡客给,警方通過(guò)查閱死者的電腦和手機(jī)用押,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 88,356評(píng)論 2 382
  • 文/潘曉璐 我一進(jìn)店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來(lái)靶剑,“玉大人蜻拨,你說(shuō)我怎么就攤上這事∽” “怎么了缎讼?”我有些...
    開(kāi)封第一講書人閱讀 152,702評(píng)論 0 342
  • 文/不壞的土叔 我叫張陵,是天一觀的道長(zhǎng)坑匠。 經(jīng)常有香客問(wèn)我血崭,道長(zhǎng),這世上最難降的妖魔是什么厘灼? 我笑而不...
    開(kāi)封第一講書人閱讀 55,259評(píng)論 1 279
  • 正文 為了忘掉前任夹纫,我火速辦了婚禮,結(jié)果婚禮上设凹,老公的妹妹穿的比我還像新娘舰讹。我一直安慰自己,他們只是感情好闪朱,可當(dāng)我...
    茶點(diǎn)故事閱讀 64,263評(píng)論 5 371
  • 文/花漫 我一把揭開(kāi)白布月匣。 她就那樣靜靜地躺著匈睁,像睡著了一般。 火紅的嫁衣襯著肌膚如雪桶错。 梳的紋絲不亂的頭發(fā)上,一...
    開(kāi)封第一講書人閱讀 49,036評(píng)論 1 285
  • 那天胀蛮,我揣著相機(jī)與錄音院刁,去河邊找鬼。 笑死粪狼,一個(gè)胖子當(dāng)著我的面吹牛退腥,可吹牛的內(nèi)容都是我干的再榄。 我是一名探鬼主播狡刘,決...
    沈念sama閱讀 38,349評(píng)論 3 400
  • 文/蒼蘭香墨 我猛地睜開(kāi)眼,長(zhǎng)吁一口氣:“原來(lái)是場(chǎng)噩夢(mèng)啊……” “哼困鸥!你這毒婦竟也來(lái)了嗅蔬?” 一聲冷哼從身側(cè)響起,我...
    開(kāi)封第一講書人閱讀 36,979評(píng)論 0 259
  • 序言:老撾萬(wàn)榮一對(duì)情侶失蹤疾就,失蹤者是張志新(化名)和其女友劉穎澜术,沒(méi)想到半個(gè)月后,有當(dāng)?shù)厝嗽跇?shù)林里發(fā)現(xiàn)了一具尸體猬腰,經(jīng)...
    沈念sama閱讀 43,469評(píng)論 1 300
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡鸟废,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 35,938評(píng)論 2 323
  • 正文 我和宋清朗相戀三年,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了姑荷。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片盒延。...
    茶點(diǎn)故事閱讀 38,059評(píng)論 1 333
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡,死狀恐怖鼠冕,靈堂內(nèi)的尸體忽然破棺而出添寺,到底是詐尸還是另有隱情,我是刑警寧澤供鸠,帶...
    沈念sama閱讀 33,703評(píng)論 4 323
  • 正文 年R本政府宣布畦贸,位于F島的核電站,受9級(jí)特大地震影響楞捂,放射性物質(zhì)發(fā)生泄漏薄坏。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 39,257評(píng)論 3 307
  • 文/蒙蒙 一寨闹、第九天 我趴在偏房一處隱蔽的房頂上張望胶坠。 院中可真熱鬧,春花似錦繁堡、人聲如沸沈善。這莊子的主人今日做“春日...
    開(kāi)封第一講書人閱讀 30,262評(píng)論 0 19
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽(yáng)闻牡。三九已至净赴,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間罩润,已是汗流浹背玖翅。 一陣腳步聲響...
    開(kāi)封第一講書人閱讀 31,485評(píng)論 1 262
  • 我被黑心中介騙來(lái)泰國(guó)打工, 沒(méi)想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留割以,地道東北人金度。 一個(gè)月前我還...
    沈念sama閱讀 45,501評(píng)論 2 354
  • 正文 我出身青樓,卻偏偏與公主長(zhǎng)得像严沥,于是被迫代替她去往敵國(guó)和親猜极。 傳聞我的和親對(duì)象是個(gè)殘疾皇子,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 42,792評(píng)論 2 345

推薦閱讀更多精彩內(nèi)容

  • 本文系轉(zhuǎn)載于網(wǎng)絡(luò)。 Web 應(yīng)用框架翩瓜,或者簡(jiǎn)單的說(shuō)是“Web 框架”酬姆,其實(shí)是建立 web 應(yīng)用的一種方式。從簡(jiǎn)單的...
    莫莫33閱讀 281評(píng)論 0 2
  • 本文系轉(zhuǎn)載于網(wǎng)絡(luò)。 Web 應(yīng)用框架浮定,或者簡(jiǎn)單的說(shuō)是“Web 框架”相满,其實(shí)是建立 web 應(yīng)用的一種方式。從簡(jiǎn)單的...
    everfight閱讀 281評(píng)論 0 3
  • 我們常用的web前端框架其實(shí)簡(jiǎn)單稱呼叫web框架,現(xiàn)階段web前端技術(shù)成熟方灾,從視覺(jué)體驗(yàn)到用戶體驗(yàn)都是比較好的建蹄,這也...
    UIleader閱讀 1,670評(píng)論 0 2
  • 22年12月更新:個(gè)人網(wǎng)站關(guān)停,如果仍舊對(duì)舊教程有興趣參考 Github 的markdown內(nèi)容[https://...
    tangyefei閱讀 35,160評(píng)論 22 257
  • 可不要被名字迷惑,它可不是web網(wǎng)頁(yè)的框架嘿棘,而是服務(wù)器用來(lái)產(chǎn)生web網(wǎng)頁(yè)時(shí)用到的工具劲腿。 Web應(yīng)用框架(簡(jiǎn)稱Web...
    約伯閱讀 3,748評(píng)論 0 3