前端程序員必知:?jiǎn)雾?yè)面應(yīng)用的核心

這幾年里跌捆,單頁(yè)面應(yīng)用的框架令人應(yīng)接不暇薄湿,各種新的概念也層出不窮叫倍。從過(guò)去的 jQuery Mobie偷卧、Backbone 到今天的 Angular 2、React吆倦、Vue 2听诸,除了版本號(hào)不同,他們還有很多的相同之處逼庞。

剛開始寫商業(yè)代碼的時(shí)候蛇更,我使用的是 jQuery瞻赶。使用 jQuery 來(lái)實(shí)現(xiàn)功能很容易赛糟,找到一個(gè)相應(yīng)的 jQuery 插件,再編寫相應(yīng)的功能即可砸逊。對(duì)于單頁(yè)面應(yīng)用亦是如此璧南,尋找一個(gè)相輔助的插件就可以了,如 jQuery Mobile师逸。

盡管在今天看來(lái)司倚,jQuery Mobile 已經(jīng)不適合于今天的多數(shù)場(chǎng)景了。這個(gè)主要原因是篓像,當(dāng)時(shí)的用戶對(duì)于移動(dòng) Web 應(yīng)用的理解和今天是不同的动知。他們覺(jué)得移動(dòng) Web 應(yīng)用就是針對(duì)移動(dòng)設(shè)備而訂制的,移動(dòng)設(shè)備的 UI员辩、更快的加載速度等等盒粮。而在今天,多數(shù)的移動(dòng) Web 應(yīng)用奠滑,幾乎都是單頁(yè)面應(yīng)用了丹皱。

過(guò)去,即使我們想創(chuàng)建一個(gè)單頁(yè)面應(yīng)用宋税,可能也沒(méi)有一個(gè)合適的方案摊崭。而在今天,可選擇的方案就多了(PS:參見(jiàn)《第四章:學(xué)習(xí)前端只需要三個(gè)月【框架篇】》)杰赛。每個(gè)人在不同類型的項(xiàng)目上呢簸,也會(huì)有不同的方案,沒(méi)有一個(gè)框架能解決所有的問(wèn)題

  • 對(duì)于工作來(lái)說(shuō)乏屯,我更希望的是一個(gè)完整的解決方案根时。
  • 對(duì)于編程體驗(yàn)來(lái)說(shuō),我喜歡一點(diǎn)點(diǎn)的去創(chuàng)造一些輪子瓶珊。

當(dāng)我們會(huì)用的框架越多的時(shí)候啸箫, 所花費(fèi)的時(shí)間抉擇也就越多。而單頁(yè)面應(yīng)用的都有一些相同的元素伞芹,對(duì)于這些基本元素的理解忘苛,可以讓我們更快的適合其他框架蝉娜。

單頁(yè)面應(yīng)用的演進(jìn)

我接觸到單頁(yè)面應(yīng)用的時(shí)候,它看起來(lái)就像是將所有的內(nèi)容放在一個(gè)頁(yè)面上么扎唾。只需要在一個(gè) HTML 寫好所需要的各個(gè)模板召川,并在不同的頁(yè)面上 data-role 表明這是個(gè)頁(yè)面(基于 jQuery Mobile)——每個(gè)定義的頁(yè)面都和今天的移動(dòng)應(yīng)用的模式相似,有 header胸遇、content荧呐、footer 三件套。再用 id 來(lái)定義好相應(yīng)的路由纸镊。

<div data-role="page" id="foo"> 
...
</div>

這樣我們就在一個(gè) HTML 里返回了所有的頁(yè)面了倍阐。隨后,只需要在在入口處的 href 里逗威,寫好相應(yīng)的 ID 即可峰搪。

<a href="#foo">跳轉(zhuǎn)到foo</a>

當(dāng)我們點(diǎn)擊相應(yīng)的鏈接時(shí),就會(huì)切換到 HTML 中相應(yīng)的 ID凯旭。這種簡(jiǎn)單的單頁(yè)面應(yīng)用基本上就是一個(gè)離線應(yīng)用了概耻,只適合于簡(jiǎn)單的場(chǎng)景,可是它帶有單頁(yè)面應(yīng)用的基本特性罐呼。而復(fù)雜的應(yīng)用鞠柄,則需要從服務(wù)器獲取數(shù)據(jù)。然而早期受限于移動(dòng)瀏覽器性能的影響嫉柴,只能從服務(wù)器獲取相應(yīng)的 HTML厌杜,并替換當(dāng)前的頁(yè)面。

在這樣的應(yīng)用中差凹,我們可以看到單頁(yè)面應(yīng)用的基本元素: 頁(yè)面路由期奔,通過(guò)某種方式,如 URL hash 來(lái)說(shuō)明表明當(dāng)前所在的頁(yè)面危尿,并擁有從一個(gè)頁(yè)面跳轉(zhuǎn)到另外一個(gè)頁(yè)面的入口呐萌。

當(dāng)移動(dòng)設(shè)備的性能越來(lái)越好時(shí),開發(fā)者們開始在瀏覽器里渲染頁(yè)面:

  • 使用 jQuery 來(lái)做頁(yè)面交互
  • 使用 jQuery Ajax 來(lái)從服務(wù)端獲取數(shù)據(jù)
  • 使用 Backbone 來(lái)負(fù)責(zé)路由及 Model
  • 使用 Mustache 作為模板引擎來(lái)渲染頁(yè)面
  • 使用 Require.js 來(lái)管理不同的模板
  • 使用 LocalStorage 來(lái)存儲(chǔ)用戶的數(shù)據(jù)

通過(guò)結(jié)合這一系列的工具谊娇,我們終于可以實(shí)現(xiàn)一個(gè)復(fù)雜的單頁(yè)面應(yīng)用肺孤。而這些,也就是今天我們看到的單頁(yè)面應(yīng)用的基本元素济欢。我們可以在 Angular 應(yīng)用赠堵、React 應(yīng)用、Vue.js 應(yīng)用 看到這些基本要素的影子法褥,如:Vue Router茫叭、React Router、Angular 2 RouterModule 都是負(fù)責(zé)路由(頁(yè)面跳轉(zhuǎn)及模塊關(guān)系)的半等。在 Vue 和 React 里揍愁,它們都是由輔助模塊來(lái)實(shí)現(xiàn)的呐萨。因?yàn)?React 只是層 UI 層,而 Vue.js 也是用于構(gòu)建用戶界面的框架莽囤。

路由:頁(yè)面跳轉(zhuǎn)與模塊關(guān)系

要說(shuō)起路由谬擦,那可是有很長(zhǎng)的故事。當(dāng)我們?cè)跒g覽器上輸入網(wǎng)址的時(shí)候朽缎,我們就已經(jīng)開始了各種路由的旅途了惨远。

  1. 瀏覽器會(huì)檢查有沒(méi)有相應(yīng)的域名緩存,沒(méi)有的話就會(huì)一層層的去向 DNS服務(wù)器 尋向话肖,最后返回對(duì)應(yīng)的服務(wù)器的 IP 地址北秽。
  2. 接著,我們請(qǐng)求的網(wǎng)站將會(huì)將由對(duì)應(yīng) IP 的 HTTP 服務(wù)器處理狼牺,HTTP 服務(wù)器會(huì)根據(jù)請(qǐng)求來(lái)交給對(duì)應(yīng)的應(yīng)用容器來(lái)處理羡儿。
  3. 隨后,我們的應(yīng)用將根據(jù)用戶請(qǐng)求的路徑是钥,將請(qǐng)求交給相應(yīng)的函數(shù)來(lái)處理。最后缅叠,返回相應(yīng)的 HTML 和資源文化

當(dāng)我們做后臺(tái)應(yīng)用的時(shí)候悄泥,我們只需要關(guān)心上述過(guò)程中的最后一步。即肤粱,將對(duì)應(yīng)的路由交給對(duì)應(yīng)的函數(shù)來(lái)處理弹囚。這一點(diǎn),在不同的后臺(tái)框架的表現(xiàn)形式都是相似的领曼。

如 Python 語(yǔ)言里的 Web 開發(fā)框架 Django 的 URLConf鸥鹉,使用正規(guī)表達(dá)式來(lái)表正

url(r'^articles/2003/$', views.special_case_2003),

而在 Laravel 里,則是通過(guò)參數(shù)的形式來(lái)呈現(xiàn)

Route::get('posts/{post}/comments/{comment}', function ($postId, $commentId) {
    //
});

雖然表現(xiàn)形式有一些差別庶骄,但是總體來(lái)說(shuō)也是差不多的毁渗。而對(duì)于前端應(yīng)用來(lái)說(shuō),也是如此单刁,將對(duì)應(yīng)的 URL 的邏輯交由對(duì)應(yīng)的函數(shù)來(lái)處理灸异。

React Router 使用了類似形式來(lái)處理路由,代碼如下所示:

 <Route path="blog" component={BlogList} />
 <Route path="blog/:id" component={BlogDetail} />

當(dāng)頁(yè)面跳轉(zhuǎn)到 blog 的時(shí)候羔飞,會(huì)將控制權(quán)將給 BlogList 組件來(lái)處理肺樟。

當(dāng)頁(yè)面跳轉(zhuǎn)到 blog/fasfasf-asdfsafd 的時(shí)候,將匹配到這二個(gè)路由逻淌,并交給 BlogDetail 組件 來(lái)處理么伯。而路由中的 id 值,也將作為參數(shù) BlogDetail 組件來(lái)處理卡儒。

相似的田柔,而 Angular 2 的形式則是:

{ path: 'blog',      component: BlogListComponent },
{ path: 'blog/:id',      component: BlogDetailComponent },

相似的誓篱,這里的 BlogDetailComponent 是一個(gè)組件,path 中的 id 值將會(huì)傳遞給 BlogDetailComponent 組件凯楔。

從上面來(lái)看窜骄,盡管表現(xiàn)形式上有所差異,但是其行為是一致的:使用規(guī)則引擎來(lái)處理路由與函數(shù)的關(guān)系摆屯。稍有不同的是邻遏,后臺(tái)的路由完全交由服務(wù)器端來(lái)控制,而前端的請(qǐng)求則都是在本地改變其狀態(tài)虐骑。

并且同時(shí)在不同的前端框架上准验,他們?cè)谛袨樯线€有一些區(qū)別。這取決于我們是否需要后臺(tái)渲染廷没,即刷新當(dāng)前頁(yè)面時(shí)的表現(xiàn)形式糊饱。

  • 使用 Hash (#)或者 Hash Bang (#!) 的形式。即 # 開頭的參數(shù)形式颠黎,諸如 ued.party/#/blog另锋。當(dāng)我們?cè)L問(wèn) blog/12 時(shí),URL 的就會(huì)變成 ued.party/#/blog/12
  • 使用新的 HTML 5 的 history API狭归。用戶看到的 URL 和正常的 URL 是一樣的夭坪。當(dāng)用戶點(diǎn)擊某個(gè)鏈接進(jìn)入到新的頁(yè)面時(shí),會(huì)通過(guò) history 的 pushState 來(lái)填入新的地址过椎。當(dāng)我們?cè)L問(wèn) blog/12 時(shí)室梅,URL 的就會(huì)變成 ued.party/blog/12。當(dāng)用戶刷新頁(yè)面的時(shí)候疚宇,請(qǐng)通過(guò)新的 URL 來(lái)向服務(wù)器請(qǐng)求內(nèi)容亡鼠。

幸運(yùn)的是,大部分的最新 Router 組件都會(huì)判斷是否支持 history API敷待,再來(lái)決定先用哪一個(gè)方案间涵。

數(shù)據(jù):獲取與鑒權(quán)

實(shí)現(xiàn)路由的時(shí)候,只是將對(duì)應(yīng)的控制權(quán)交給控制器(或稱組件)來(lái)處理讼撒。而作為一個(gè)單頁(yè)面應(yīng)用的控制器浑厚,當(dāng)執(zhí)行到相應(yīng)的控制器的時(shí)候,就可以根據(jù)對(duì)應(yīng)的 blog/12 來(lái)獲取到用戶想要的 ID 是 12根盒。這個(gè)時(shí)候钳幅,控制器將需要在頁(yè)面上設(shè)置一個(gè) loading 的狀態(tài),然后發(fā)送一個(gè)請(qǐng)求到后臺(tái)服務(wù)器炎滞。

對(duì)于數(shù)據(jù)獲取來(lái)說(shuō)敢艰,我們可以通過(guò)封裝過(guò) XMLHttpRequest 的 Ajax 來(lái)獲取數(shù)據(jù),也可以通過(guò)新的册赛、支持 Promise 的 Fetch API 來(lái)獲取數(shù)據(jù)钠导,等等震嫉。Fetch API 與經(jīng)過(guò) Promise 封裝的 Ajax 并沒(méi)有太大的區(qū)別,我們?nèi)匀皇菍戭愃朴诘男问剑?/p>

fetch(url).then(response => response.json())
  .then(data => console.log(data))
  .catch(e => console.log("Oops, error", e))

對(duì)于復(fù)雜一點(diǎn)的數(shù)據(jù)交互來(lái)說(shuō)牡属,我們可以通過(guò) RxJS 來(lái)解決類似的問(wèn)題票堵。整個(gè)過(guò)程中,比較復(fù)雜的地方是對(duì)數(shù)據(jù)的鑒權(quán)與模型(Model)的處理逮栅。

模型麻煩的地方在于:轉(zhuǎn)變成想要的形式悴势。后臺(tái)返回的值是可變的,它有可能不返回措伐,有可能是 null特纤,又或者是與我們要顯示的值不一樣——想要展示的是 54%,而后臺(tái)返回的是 0.54侥加。與此同時(shí)捧存,我們可能還需要對(duì)數(shù)值進(jìn)行簡(jiǎn)單的計(jì)算,顯示一個(gè)范圍担败、區(qū)間昔穴,又或者是不同的兩種展示。

同時(shí)在必要的時(shí)候氢架,我們還需要將這些值存儲(chǔ)在本地傻咖,或者內(nèi)存里。當(dāng)我們重新進(jìn)入這個(gè)頁(yè)面的時(shí)候岖研,我們?cè)偃プx取這些值。

一旦談?wù)摰綌?shù)據(jù)的時(shí)候警检,不可避免的我們就需要關(guān)心安全因素孙援。對(duì)于普通的 Web 應(yīng)用來(lái)說(shuō),我們可以做兩件事來(lái)保證數(shù)據(jù)的安全:

  1. 采用 HTTPS:在傳輸?shù)倪^(guò)程中保證數(shù)據(jù)是加密的扇雕。
  2. 鑒權(quán):確保指定的用戶只能可以訪問(wèn)指定的數(shù)據(jù)拓售。

目前,流行的前端鑒權(quán)方式是 Token 的形式镶奉,可以是普通的定制 Token础淤,也可以是 JSON Web Token。獲取 Token 的形式哨苛,則是通過(guò) Basic 認(rèn)證——將用戶輸入的用戶名和密碼鸽凶,經(jīng)過(guò) BASE64 加密發(fā)送給服務(wù)器。服務(wù)器解密后驗(yàn)證是否是正常的用戶名和密碼建峭,再返回一個(gè)帶有時(shí)期期限的 Token 給前端玻侥。

隨后,當(dāng)用戶去獲取需要權(quán)限的數(shù)據(jù)時(shí)亿蒸,需要在 Header 里鑒定這個(gè) Token 是否有限凑兰,再返回相應(yīng)的數(shù)據(jù)掌桩。如果 Token 已經(jīng)過(guò)期了,則返回 401 或者類似的標(biāo)志姑食,客戶端就在這個(gè)時(shí)候清除 Token波岛,并讓用戶重新登錄。

數(shù)據(jù)展示:模板引擎

現(xiàn)在音半,我們已經(jīng)獲取到這些數(shù)據(jù)了则拷,下一步所需要做的就是顯示這些數(shù)據(jù)。與其他內(nèi)容相比祟剔,顯示數(shù)據(jù)就是一件簡(jiǎn)單的事隔躲,無(wú)非就是:

  • 依據(jù)條件來(lái)顯示、隱藏某些數(shù)據(jù)
  • 在模板中對(duì)數(shù)據(jù)進(jìn)行遍歷顯示
  • 在模板中執(zhí)行方法來(lái)獲取相應(yīng)的值物延,可以是函數(shù)宣旱,也可以是過(guò)濾器。
  • 依據(jù)不同的數(shù)值來(lái)動(dòng)態(tài)獲取樣式
  • 等等

不同的框架會(huì)存在一些差異叛薯。并且現(xiàn)代的前端框架都可以支持單向或者雙向的數(shù)據(jù)綁定浑吟。當(dāng)相應(yīng)的數(shù)據(jù)發(fā)生變化時(shí),它就可以自動(dòng)地顯示在 UI 上耗溜。

最后组力,在相應(yīng)需要處理的 UI 上,綁上相應(yīng)的事件來(lái)處理抖拴。

只是在數(shù)據(jù)顯示的時(shí)候燎字,又會(huì)涉及到另外一個(gè)問(wèn)題,即組件化阿宅。對(duì)于一些需要重用的元素候衍,我們會(huì)將其抽取為一個(gè)通用的組件,以便于我們可以復(fù)用它們洒放。

<my-sizer [(size)]="fontSizePx"></my-sizer>

并且在這些組件里蛉鹿,也會(huì)涉及到相應(yīng)的參數(shù)變化即狀態(tài)改變。

交互:事件與狀態(tài)管理

完成一步步的渲染之后往湿,我們還需要做的事情是:交互妖异。交互分為兩部分:用戶交互、組件間的交互——共享狀態(tài)领追。

組件交互:狀態(tài)管理

用戶從 A 頁(yè)面跳轉(zhuǎn)到 B 頁(yè)面的時(shí)候他膳,為了解耦組件間的關(guān)系,我們不會(huì)使用組件的參數(shù)來(lái)傳入值蔓腐。而是將這些值存儲(chǔ)在內(nèi)存里矩乐,在適當(dāng)?shù)臅r(shí)候調(diào)出這些值。當(dāng)我們處理用戶是否登錄的時(shí)候,我們需要一個(gè) isLogined 的方法來(lái)獲取用戶的狀態(tài)散罕;在用戶登錄的時(shí)候分歇,我們還需要一個(gè) setLogin 的方法;用戶登出的時(shí)候欧漱,我們還需要更新一下用戶的登錄狀態(tài)职抡。

在沒(méi)有 Redux 之前,我都會(huì)寫一個(gè) service 來(lái)管理應(yīng)用的狀態(tài)误甚。在這個(gè)模塊里寫上些 setter缚甩、getter 方法來(lái)存儲(chǔ)狀態(tài)的值,并根據(jù)業(yè)務(wù)功能寫上一些來(lái)操作這個(gè)值窑邦。然而擅威,使用 service 時(shí),我們很難跟蹤到狀態(tài)的變化情況冈钦,還需要做一些額外的代碼來(lái)特別處理郊丛。

有時(shí)候也會(huì)犯懶一下,直接寫一個(gè)全局變量瞧筛。這個(gè)時(shí)候維護(hù)起代碼來(lái)就是一場(chǎng)噩夢(mèng)厉熟,需要全局搜索相應(yīng)的變量。如果是調(diào)用某個(gè)特定的 Service 就比較容易找到調(diào)用的地方较幌。

用戶交互:事件

事實(shí)上揍瑟,對(duì)于用戶交互來(lái)說(shuō)也只是改變狀態(tài)的值,即對(duì)狀態(tài)進(jìn)行操作乍炉。

舉一個(gè)例子绢片,當(dāng)用戶點(diǎn)擊登錄的時(shí)候,發(fā)送數(shù)據(jù)到后臺(tái)岛琼,由后臺(tái)返回這個(gè)值杉畜。由控制器一一的去修改這些狀態(tài),最后確認(rèn)這個(gè)用戶登錄衷恭,并發(fā)一個(gè)用戶已經(jīng)登錄的廣播,又或者修改全局的用戶值纯续。

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末随珠,一起剝皮案震驚了整個(gè)濱河市,隨后出現(xiàn)的幾起案子猬错,更是在濱河造成了極大的恐慌窗看,老刑警劉巖,帶你破解...
    沈念sama閱讀 206,126評(píng)論 6 481
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件倦炒,死亡現(xiàn)場(chǎng)離奇詭異显沈,居然都是意外死亡,警方通過(guò)查閱死者的電腦和手機(jī),發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 88,254評(píng)論 2 382
  • 文/潘曉璐 我一進(jìn)店門拉讯,熙熙樓的掌柜王于貴愁眉苦臉地迎上來(lái)涤浇,“玉大人,你說(shuō)我怎么就攤上這事魔慷≈欢В” “怎么了?”我有些...
    開封第一講書人閱讀 152,445評(píng)論 0 341
  • 文/不壞的土叔 我叫張陵院尔,是天一觀的道長(zhǎng)蜻展。 經(jīng)常有香客問(wèn)我,道長(zhǎng)邀摆,這世上最難降的妖魔是什么纵顾? 我笑而不...
    開封第一講書人閱讀 55,185評(píng)論 1 278
  • 正文 為了忘掉前任,我火速辦了婚禮栋盹,結(jié)果婚禮上施逾,老公的妹妹穿的比我還像新娘。我一直安慰自己贞盯,他們只是感情好音念,可當(dāng)我...
    茶點(diǎn)故事閱讀 64,178評(píng)論 5 371
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著躏敢,像睡著了一般闷愤。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上件余,一...
    開封第一講書人閱讀 48,970評(píng)論 1 284
  • 那天讥脐,我揣著相機(jī)與錄音,去河邊找鬼啼器。 笑死旬渠,一個(gè)胖子當(dāng)著我的面吹牛,可吹牛的內(nèi)容都是我干的端壳。 我是一名探鬼主播告丢,決...
    沈念sama閱讀 38,276評(píng)論 3 399
  • 文/蒼蘭香墨 我猛地睜開眼,長(zhǎng)吁一口氣:“原來(lái)是場(chǎng)噩夢(mèng)啊……” “哼损谦!你這毒婦竟也來(lái)了岖免?” 一聲冷哼從身側(cè)響起,我...
    開封第一講書人閱讀 36,927評(píng)論 0 259
  • 序言:老撾萬(wàn)榮一對(duì)情侶失蹤照捡,失蹤者是張志新(化名)和其女友劉穎颅湘,沒(méi)想到半個(gè)月后,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體栗精,經(jīng)...
    沈念sama閱讀 43,400評(píng)論 1 300
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡闯参,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 35,883評(píng)論 2 323
  • 正文 我和宋清朗相戀三年瞻鹏,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片鹿寨。...
    茶點(diǎn)故事閱讀 37,997評(píng)論 1 333
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡新博,死狀恐怖,靈堂內(nèi)的尸體忽然破棺而出释移,到底是詐尸還是另有隱情叭披,我是刑警寧澤,帶...
    沈念sama閱讀 33,646評(píng)論 4 322
  • 正文 年R本政府宣布玩讳,位于F島的核電站涩蜘,受9級(jí)特大地震影響,放射性物質(zhì)發(fā)生泄漏熏纯。R本人自食惡果不足惜同诫,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 39,213評(píng)論 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望樟澜。 院中可真熱鬧误窖,春花似錦、人聲如沸秩贰。這莊子的主人今日做“春日...
    開封第一講書人閱讀 30,204評(píng)論 0 19
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽(yáng)毒费。三九已至丙唧,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間觅玻,已是汗流浹背想际。 一陣腳步聲響...
    開封第一講書人閱讀 31,423評(píng)論 1 260
  • 我被黑心中介騙來(lái)泰國(guó)打工, 沒(méi)想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留溪厘,地道東北人胡本。 一個(gè)月前我還...
    沈念sama閱讀 45,423評(píng)論 2 352
  • 正文 我出身青樓,卻偏偏與公主長(zhǎng)得像畸悬,于是被迫代替她去往敵國(guó)和親侧甫。 傳聞我的和親對(duì)象是個(gè)殘疾皇子,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 42,722評(píng)論 2 345

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

  • 這幾年里蹋宦,單頁(yè)面應(yīng)用的框架令人應(yīng)接不暇闺骚,各種新的概念也層出不窮。從過(guò)去的 jQuery Mobie妆档、Backbon...
    vv源vv閱讀 1,090評(píng)論 0 47
  • Spring Cloud為開發(fā)人員提供了快速構(gòu)建分布式系統(tǒng)中一些常見(jiàn)模式的工具(例如配置管理,服務(wù)發(fā)現(xiàn)虫碉,斷路器贾惦,智...
    卡卡羅2017閱讀 134,599評(píng)論 18 139
  • Android 自定義View的各種姿勢(shì)1 Activity的顯示之ViewRootImpl詳解 Activity...
    passiontim閱讀 171,499評(píng)論 25 707
  • 在NODE中,應(yīng)用需要處理網(wǎng)絡(luò)協(xié)議、操作系統(tǒng)數(shù)據(jù)庫(kù)须板、處理圖片碰镜、接受上傳文件等,在網(wǎng)絡(luò)流和文件的操作中习瑰,需要處理大量...
    TaoGeNet閱讀 2,080評(píng)論 0 2
  • Few months ago, I have met a new friend Kelly, she is a v...
    Flora唐閱讀 538評(píng)論 0 1