成人国产在线小视频_日韩寡妇人妻调教在线播放_色成人www永久在线观看_2018国产精品久久_亚洲欧美高清在线30p_亚洲少妇综合一区_黄色在线播放国产_亚洲另类技巧小说校园_国产主播xx日韩_a级毛片在线免费

資訊專欄INFORMATION COLUMN

史上最詳細(xì)的經(jīng)典面試題 從輸入U(xiǎn)RL到看到頁(yè)面發(fā)生了什么?

wfc_666 / 2475人閱讀

摘要:的輸入到瀏覽器解析的一系列事件很多大公司面試喜歡問這樣一道面試題,輸入到看見頁(yè)面發(fā)生了什么今天我們來總結(jié)一下。采用三次握手是為了防止失效的連接請(qǐng)求報(bào)文段突然又傳送到主機(jī),因而產(chǎn)生錯(cuò)誤。

首先說明,本文很長(zhǎng),請(qǐng)泡一杯咖啡,抽出至少半個(gè)小時(shí)來慢慢回味。

URL的輸入到瀏覽器解析的一系列事件

很多大公司面試喜歡問這樣一道面試題,輸入U(xiǎn)RL到看見頁(yè)面發(fā)生了什么?,今天我們來總結(jié)一下。 簡(jiǎn)單來說,共有以下幾個(gè)過程

DNS解析

發(fā)起TCP連接

發(fā)送HTTP請(qǐng)求

服務(wù)器處理請(qǐng)求并返回HTTP報(bào)文

瀏覽器解析渲染頁(yè)面

連接結(jié)束。

下面我們來看看具體的細(xì)節(jié)

DNS解析

DNS解析實(shí)際上就是尋找你所需要的資源的過程。假設(shè)你輸入www.baidu.com,而這個(gè)網(wǎng)址并不是百度的真實(shí)地址,互聯(lián)網(wǎng)中每一臺(tái)機(jī)器都有唯一標(biāo)識(shí)的IP地址,這個(gè)才是關(guān)鍵,但是它不好記,亂七八糟一串?dāng)?shù)字誰記得住啊,所以就需要一個(gè)網(wǎng)址和IP地址的轉(zhuǎn)換,也就是DNS解析。下面看看具體的解析過程

具體解析

DNS解析其實(shí)是一個(gè)遞歸的過程

輸入www.google.com網(wǎng)址后,首先在本地的域名服務(wù)器中查找,沒找到去根域名服務(wù)器查找,沒有再去com頂級(jí)域名服務(wù)器查找,,如此的類推下去,直到找到IP地址,然后把它記錄在本地,供下次使用。大致過程就是.-> .com ->google.com. -> www.google.com.。 (你可能覺得我多寫 .,并木有,這個(gè).對(duì)應(yīng)的就是根域名服務(wù)器,默認(rèn)情況下所有的網(wǎng)址的最后一位都是.,既然是默認(rèn)情況下,為了方便用戶,通常都會(huì)省略,瀏覽器在請(qǐng)求DNS的時(shí)候會(huì)自動(dòng)加上)

DNS優(yōu)化

既然已經(jīng)懂得了解析的具體過程,我們可以看到上述一共經(jīng)過了N個(gè)過程,每個(gè)過程有一定的消耗和時(shí)間的等待,因此我們得想辦法解決一下這個(gè)問題!

DNS緩存

DNS存在著多級(jí)緩存,從離瀏覽器的距離排序的話,有以下幾種: 瀏覽器緩存,系統(tǒng)緩存,路由器緩存,IPS服務(wù)器緩存,根域名服務(wù)器緩存,頂級(jí)域名服務(wù)器緩存,主域名服務(wù)器緩存。

在你的chrome瀏覽器中輸入:chrome://dns/,你可以看到chrome瀏覽器的DNS緩存。

系統(tǒng)緩存主要存在/etc/hosts(Linux系統(tǒng))中

DNS負(fù)載均衡

不知道你們有沒有注意這樣一件事,你訪問baidu.com的時(shí)候,每次響應(yīng)的并非是同一個(gè)服務(wù)器(IP地址不同),一般大公司都有成百上千臺(tái)服務(wù)器來支撐訪問,假設(shè)只有一個(gè)服務(wù)器,那它的性能和存儲(chǔ)量要多大才能支撐這樣大量的訪問呢?DNS可以返回一個(gè)合適的機(jī)器的IP給用戶,例如可以根據(jù)每臺(tái)機(jī)器的負(fù)載量,該機(jī)器離用戶地理位置的距離等等,這種過程就是DNS負(fù)載均衡

發(fā)起TCP連接

TCP提供一種可靠的傳輸,這個(gè)過程涉及到三次握手,四次揮手,下面我們?cè)敿?xì)看看 TCP提供一種面向連接的,可靠的字節(jié)流服務(wù)。 其首部的數(shù)據(jù)格式如下

字段分析

源端口:源端口和IP地址的作用是標(biāo)識(shí)報(bào)文的返回地址。

目的端口:端口指明接收方計(jì)算機(jī)上的應(yīng)用程序接口。

TCP報(bào)頭中的源端口號(hào)和目的端口號(hào)同IP數(shù)據(jù)報(bào)中的源IP與目的IP唯一確定一條TCP連接。

序號(hào):是TCP可靠傳輸?shù)年P(guān)鍵部分。序號(hào)是該報(bào)文段發(fā)送的數(shù)據(jù)組的第一個(gè)字節(jié)的序號(hào)。在TCP傳送的流中,每一個(gè)字節(jié)都有一個(gè)序號(hào)。比如一個(gè)報(bào)文段的序號(hào)為300,報(bào)文段數(shù)據(jù)部分共有100字節(jié),則下一個(gè)報(bào)文段的序號(hào)為400。所以序號(hào)確保了TCP傳輸?shù)挠行蛐浴?/p>

確認(rèn)號(hào):即ACK,指明下一個(gè)期待收到的字節(jié)序號(hào),表明該序號(hào)之前的所有數(shù)據(jù)已經(jīng)正確無誤的收到。確認(rèn)號(hào)只有當(dāng)ACK標(biāo)志為1時(shí)才有效。比如建立連接時(shí),SYN報(bào)文的ACK標(biāo)志位為0。

首部長(zhǎng)度/數(shù)據(jù)偏移:占4位,它指出TCP報(bào)文的數(shù)據(jù)距離TCP報(bào)文段的起始處有多遠(yuǎn)。由于首部可能含有可選項(xiàng)內(nèi)容,因此TCP報(bào)頭的長(zhǎng)度是不確定的,報(bào)頭不包含任何任選字段則長(zhǎng)度為20字節(jié),4位首部長(zhǎng)度字段所能表示的最大值為1111,轉(zhuǎn)化為10進(jìn)制為15,15*32/8=60,故報(bào)頭最大長(zhǎng)度為60字節(jié)。首部長(zhǎng)度也叫數(shù)據(jù)偏移,是因?yàn)槭撞块L(zhǎng)度實(shí)際上指示了數(shù)據(jù)區(qū)在報(bào)文段中的起始偏移值。

保留:占6位,保留今后使用,但目前應(yīng)都位0。

控制位:URG ACK PSH RST SYN FIN,共6個(gè),每一個(gè)標(biāo)志位表示一個(gè)控制功能。

緊急URG:當(dāng)URG=1,表明緊急指針字段有效。告訴系統(tǒng)此報(bào)文段中有緊急數(shù)據(jù)

確認(rèn)ACK:僅當(dāng)ACK=1時(shí),確認(rèn)號(hào)字段才有效。TCP規(guī)定,在連接建立后所有報(bào)文的傳輸都必須把ACK置1。

推送PSH:當(dāng)兩個(gè)應(yīng)用進(jìn)程進(jìn)行交互式通信時(shí),有時(shí)在一端的應(yīng)用進(jìn)程希望在鍵入一個(gè)命令后立即就能收到對(duì)方的響應(yīng),這時(shí)候就將PSH=1。

復(fù)位RST:當(dāng)RST=1,表明TCP連接中出現(xiàn)嚴(yán)重差錯(cuò),必須釋放連接,然后再重新建立連接。

同步SYN:在連接建立時(shí)用來同步序號(hào)。當(dāng)SYN=1,ACK=0,表明是連接請(qǐng)求報(bào)文,若同意連接,則響應(yīng)報(bào)文中應(yīng)該使SYN=1,ACK=1。

終止FIN:用來釋放連接。當(dāng)FIN=1,表明此報(bào)文的發(fā)送方的數(shù)據(jù)已經(jīng)發(fā)送完畢,并且要求釋放。

窗口:滑動(dòng)窗口大小,用來告知發(fā)送端接受端的緩存大小,以此控制發(fā)送端發(fā)送數(shù)據(jù)的速率,從而達(dá)到流量控制。窗口大小時(shí)一個(gè)16bit字段,因而窗口大小最大為65535。

校驗(yàn)和:奇偶校驗(yàn),此校驗(yàn)和是對(duì)整個(gè)的 TCP 報(bào)文段,包括 TCP 頭部和 TCP 數(shù)據(jù),以 16 位字進(jìn)行計(jì)算所得。由發(fā)送端計(jì)算和存儲(chǔ),并由接收端進(jìn)行驗(yàn)證。

緊急指針:只有當(dāng) URG 標(biāo)志置 1 時(shí)緊急指針才有效。緊急指針是一個(gè)正的偏移量,和順序號(hào)字段中的值相加表示緊急數(shù)據(jù)最后一個(gè)字節(jié)的序號(hào)。 TCP 的緊急方式是發(fā)送端向另一端發(fā)送緊急數(shù)據(jù)的一種方式。

選項(xiàng)和填充:最常見的可選字段是最長(zhǎng)報(bào)文大小,又稱為MSS(Maximum Segment Size),每個(gè)連接方通常都在通信的第一個(gè)報(bào)文段(為建立連接而設(shè)置SYN標(biāo)志為1的那個(gè)段)中指明這個(gè)選項(xiàng),它表示本端所能接受的最大報(bào)文段的長(zhǎng)度。選項(xiàng)長(zhǎng)度不一定是32位的整數(shù)倍,所以要加填充位,即在這個(gè)字段中加入額外的零,以保證TCP頭是32的整數(shù)倍。

數(shù)據(jù)部分: TCP 報(bào)文段中的數(shù)據(jù)部分是可選的。在一個(gè)連接建立和一個(gè)連接終止時(shí),雙方交換的報(bào)文段僅有 TCP 首部。如果一方?jīng)]有數(shù)據(jù)要發(fā)送,也使用沒有任何數(shù)據(jù)的首部來確認(rèn)收到的數(shù)據(jù)。在處理超時(shí)的許多情況中,也會(huì)發(fā)送不帶任何數(shù)據(jù)的報(bào)文段。

需要注意的是: (A)不要將確認(rèn)序號(hào)Ack與標(biāo)志位中的ACK搞混了。 (B)確認(rèn)方Ack=發(fā)起方Req+1,兩端配對(duì)。

三次握手

第一次握手:

客戶端發(fā)送syn包(Seq=x)到服務(wù)器,并進(jìn)入SYN_SEND狀態(tài),等待服務(wù)器確認(rèn);

第二次握手:

服務(wù)器收到syn包,必須確認(rèn)客戶的SYN(ack=x+1),同時(shí)自己也發(fā)送一個(gè)SYN包(Seq=y),即SYN+ACK包,此時(shí)服務(wù)器進(jìn)入SYN_RECV狀態(tài);

第三次握手:

客戶端收到服務(wù)器的SYN+ACK包,向服務(wù)器發(fā)送確認(rèn)包ACK(ack=y+1),此包發(fā)送完畢,客戶端和服務(wù)器進(jìn)入ESTABLISHED狀態(tài),完成三次握手。

握手過程中傳送的包里不包含數(shù)據(jù),三次握手完畢后,客戶端與服務(wù)器才正式開始傳送數(shù)據(jù)。理想狀態(tài)下,TCP連接一旦建立,在通信雙方中的任何一方主動(dòng)關(guān)閉連接之前,TCP 連接都將被一直保持下去。

為什么會(huì)采用三次握手,若采用二次握手可以嗎? 四次呢?

建立連接的過程是利用客戶服務(wù)器模式,假設(shè)主機(jī)A為客戶端,主機(jī)B為服務(wù)器端。

采用三次握手是為了防止失效的連接請(qǐng)求報(bào)文段突然又傳送到主機(jī)B,因而產(chǎn)生錯(cuò)誤。失效的連接請(qǐng)求報(bào)文段是指:主機(jī)A發(fā)出的連接請(qǐng)求沒有收到主機(jī)B的確認(rèn),于是經(jīng)過一段時(shí)間后,主機(jī)A又重新向主機(jī)B發(fā)送連接請(qǐng)求,且建立成功,順序完成數(shù)據(jù)傳輸??紤]這樣一種特殊情況,主機(jī)A第一次發(fā)送的連接請(qǐng)求并沒有丟失,而是因?yàn)榫W(wǎng)絡(luò)節(jié)點(diǎn)導(dǎo)致延遲達(dá)到主機(jī)B,主機(jī)B以為是主機(jī)A又發(fā)起的新連接,于是主機(jī)B同意連接,并向主機(jī)A發(fā)回確認(rèn),但是此時(shí)主機(jī)A根本不會(huì)理會(huì),主機(jī)B就一直在等待主機(jī)A發(fā)送數(shù)據(jù),導(dǎo)致主機(jī)B的資源浪費(fèi)。

采用兩次握手不行,原因就是上面說的失效的連接請(qǐng)求的特殊情況。而在三次握手中, client和server都有一個(gè)發(fā)syn和收ack的過程, 雙方都是發(fā)后能收, 表明通信則準(zhǔn)備工作OK.

為什么不是四次握手呢? 大家應(yīng)該知道通信中著名的藍(lán)軍紅軍約定, 這個(gè)例子說明, 通信不可能100%可靠, 而上面的三次握手已經(jīng)做好了通信的準(zhǔn)備工作, 再增加握手, 并不能顯著提高可靠性, 而且也沒有必要。

四次揮手

數(shù)據(jù)傳輸完畢后,雙方都可釋放連接。最開始的時(shí)候,客戶端和服務(wù)器都是處于ESTABLISHED狀態(tài),假設(shè)客戶端主動(dòng)關(guān)閉,服務(wù)器被動(dòng)關(guān)閉。

第一次揮手:

客戶端發(fā)送一個(gè)FIN,用來關(guān)閉客戶端到服務(wù)器的數(shù)據(jù)傳送,也就是客戶端告訴服務(wù)器:我已經(jīng)不 會(huì)再給你發(fā)數(shù)據(jù)了(當(dāng)然,在fin包之前發(fā)送出去的數(shù)據(jù),如果沒有收到對(duì)應(yīng)的ack確認(rèn)報(bào)文,客戶端依然會(huì)重發(fā)這些數(shù)據(jù)),但是,此時(shí)客戶端還可 以接受數(shù)據(jù)。 FIN=1,其序列號(hào)為seq=u(等于前面已經(jīng)傳送過來的數(shù)據(jù)的最后一個(gè)字節(jié)的序號(hào)加1),此時(shí),客戶端進(jìn)入FIN-WAIT-1(終止等待1)狀態(tài)。 TCP規(guī)定,F(xiàn)IN報(bào)文段即使不攜帶數(shù)據(jù),也要消耗一個(gè)序號(hào)。

第二次揮手:

服務(wù)器收到FIN包后,發(fā)送一個(gè)ACK給對(duì)方并且?guī)献约旱男蛄刑?hào)seq,確認(rèn)序號(hào)為收到序號(hào)+1(與SYN相同,一個(gè)FIN占用一個(gè)序號(hào))。此時(shí),服務(wù)端就進(jìn)入了CLOSE-WAIT(關(guān)閉等待)狀態(tài)。TCP服務(wù)器通知高層的應(yīng)用進(jìn)程,客戶端向服務(wù)器的方向就釋放了,這時(shí)候處于半關(guān)閉狀態(tài),即客戶端已經(jīng)沒有數(shù)據(jù)要發(fā)送了,但是服務(wù)器若發(fā)送數(shù)據(jù),客戶端依然要接受。這個(gè)狀態(tài)還要持續(xù)一段時(shí)間,也就是整個(gè)CLOSE-WAIT狀態(tài)持續(xù)的時(shí)間。

此時(shí),客戶端就進(jìn)入FIN-WAIT-2(終止等待2)狀態(tài),等待服務(wù)器發(fā)送連接釋放報(bào)文(在這之前還需要接受服務(wù)器發(fā)送的最后的數(shù)據(jù))。

第三次揮手:

服務(wù)器發(fā)送一個(gè)FIN,用來關(guān)閉服務(wù)器到客戶端的數(shù)據(jù)傳送,也就是告訴客戶端,我的數(shù)據(jù)也發(fā)送完了,不會(huì)再給你發(fā)數(shù)據(jù)了。由于在半關(guān)閉狀態(tài),服務(wù)器很可能又發(fā)送了一些數(shù)據(jù),假定此時(shí)的序列號(hào)為seq=w,此時(shí),服務(wù)器就進(jìn)入了LAST-ACK(最后確認(rèn))狀態(tài),等待客戶端的確認(rèn)。

第四次揮手:

主動(dòng)關(guān)閉方收到FIN后,發(fā)送一個(gè)ACK給被動(dòng)關(guān)閉方,確認(rèn)序號(hào)為收到序號(hào)+1,此時(shí),客戶端就進(jìn)入了TIME-WAIT(時(shí)間等待)狀態(tài)。注意此時(shí)TCP連接還沒有釋放,必須經(jīng)過2?MSL(最長(zhǎng)報(bào)文段壽命)的時(shí)間后,當(dāng)客戶端撤銷相應(yīng)的TCB后,才進(jìn)入CLOSED狀態(tài)。

服務(wù)器只要收到了客戶端發(fā)出的確認(rèn),立即進(jìn)入CLOSED狀態(tài)。同樣,撤銷TCB后,就結(jié)束了這次的TCP連接??梢钥吹剑?wù)器結(jié)束TCP連接的時(shí)間要比客戶端早一些。

至此,完成四次揮手。

為什么客戶端最后還要等待2MSL?

MSL(Maximum Segment Lifetime),TCP允許不同的實(shí)現(xiàn)可以設(shè)置不同的MSL值。

第一,保證客戶端發(fā)送的最后一個(gè)ACK報(bào)文能夠到達(dá)服務(wù)器,因?yàn)檫@個(gè)ACK報(bào)文可能丟失,站在服務(wù)器的角度看來,我已經(jīng)發(fā)送了FIN+ACK報(bào)文請(qǐng)求斷開了,客戶端還沒有給我回應(yīng),應(yīng)該是我發(fā)送的請(qǐng)求斷開報(bào)文它沒有收到,于是服務(wù)器又會(huì)重新發(fā)送一次,而客戶端就能在這個(gè)2MSL時(shí)間段內(nèi)收到這個(gè)重傳的報(bào)文,接著給出回應(yīng)報(bào)文,并且會(huì)重啟2MSL計(jì)時(shí)器。
第二,防止類似與“三次握手”中提到了的“已經(jīng)失效的連接請(qǐng)求報(bào)文段”出現(xiàn)在本連接中??蛻舳税l(fā)送完最后一個(gè)確認(rèn)報(bào)文后,在這個(gè)2MSL時(shí)間中,就可以使本連接持續(xù)的時(shí)間內(nèi)所產(chǎn)生的所有報(bào)文段都從網(wǎng)絡(luò)中消失。這樣新的連接中不會(huì)出現(xiàn)舊連接的請(qǐng)求報(bào)文。

為什么建立連接是三次握手,關(guān)閉連接確是四次揮手呢?

建立連接的時(shí)候, 服務(wù)器在LISTEN狀態(tài)下,收到建立連接請(qǐng)求的SYN報(bào)文后,把ACK和SYN放在一個(gè)報(bào)文里發(fā)送給客戶端。 而關(guān)閉連接時(shí),服務(wù)器收到對(duì)方的FIN報(bào)文時(shí),僅僅表示對(duì)方不再發(fā)送數(shù)據(jù)了但是還能接收數(shù)據(jù),而自己也未必全部數(shù)據(jù)都發(fā)送給對(duì)方了,所以己方可以立即關(guān)閉,也可以發(fā)送一些數(shù)據(jù)給對(duì)方后,再發(fā)送FIN報(bào)文給對(duì)方來表示同意現(xiàn)在關(guān)閉連接,因此,己方ACK和FIN一般都會(huì)分開發(fā)送,從而導(dǎo)致多了一次。

發(fā)送HTTP請(qǐng)求

首先科補(bǔ)一個(gè)小知識(shí),HTTP的端口為80/8080,而HTTPS的端口為443

發(fā)送HTTP請(qǐng)求的過程就是構(gòu)建HTTP請(qǐng)求報(bào)文并通過TCP協(xié)議中發(fā)送到服務(wù)器指定端口 請(qǐng)求報(bào)文由請(qǐng)求行,請(qǐng)求抱頭請(qǐng)求正文組成。

請(qǐng)求行

請(qǐng)求行的格式為Method Request-URL HTTP-Version CRLF eg: GET index.html HTTP/1.1 常用的方法有: GET,POST, PUT, DELETE, OPTIONS, HEAD。

常見的請(qǐng)求方法區(qū)別

這里主要展示POSTGET的區(qū)別

常見的區(qū)別

GET在瀏覽器回退時(shí)是無害的,而POST會(huì)再次提交請(qǐng)求。

GET產(chǎn)生的URL地址可以被Bookmark,而POST不可以。

GET請(qǐng)求會(huì)被瀏覽器主動(dòng)cache,而POST不會(huì),除非手動(dòng)設(shè)置。

GET請(qǐng)求只能進(jìn)行url編碼,而POST支持多種編碼方式。

GET請(qǐng)求參數(shù)會(huì)被完整保留在瀏覽器歷史記錄里,而POST中的參數(shù)不會(huì)被保留。

GET請(qǐng)求在URL中傳送的參數(shù)是有長(zhǎng)度限制的,而POST么有。

對(duì)參數(shù)的數(shù)據(jù)類型,GET只接受ASCII字符,而POST沒有限制。

GET比POST更不安全,因?yàn)閰?shù)直接暴露在URL上,所以不能用來傳遞敏感信息。

GET參數(shù)通過URL傳遞,POST放在Request body中。

注意一點(diǎn)你也可以在GET里面藏body,POST里面帶參數(shù)

重點(diǎn)區(qū)別

GET會(huì)產(chǎn)生一個(gè)TCP數(shù)據(jù)包,而POST會(huì)產(chǎn)生兩個(gè)TCP數(shù)據(jù)包。

詳細(xì)的說就是:

對(duì)于GET方式的請(qǐng)求,瀏覽器會(huì)把http header和data一并發(fā)送出去,服務(wù)器響應(yīng)200(返回?cái)?shù)據(jù));

而對(duì)于POST,瀏覽器先發(fā)送header,服務(wù)器響應(yīng)100 continue,瀏覽器再發(fā)送data,服務(wù)器響應(yīng)200 ok(返回?cái)?shù)據(jù))。

注意一點(diǎn),并不是所有的瀏覽器都會(huì)發(fā)送兩次數(shù)據(jù)包,F(xiàn)irefox就發(fā)送一次

請(qǐng)求報(bào)頭

請(qǐng)求報(bào)頭允許客戶端向服務(wù)器傳遞請(qǐng)求的附加信息和客戶端自身的信息。

從圖中可以看出,請(qǐng)求報(bào)頭中使用了Accept, Accept-Encoding, Accept-Language, Cache-Control, Connection, Cookie等字段。Accept用于指定客戶端用于接受哪些類型的信息,Accept-Encoding與Accept類似,它用于指定接受的編碼方式。Connection設(shè)置為Keep-alive用于告訴客戶端本次HTTP請(qǐng)求結(jié)束之后并不需要關(guān)閉TCP連接,這樣可以使下次HTTP請(qǐng)求使用相同的TCP通道,節(jié)省TCP連接建立的時(shí)間。

請(qǐng)求正文

當(dāng)使用POST, PUT等方法時(shí),通常需要客戶端向服務(wù)器傳遞數(shù)據(jù)。這些數(shù)據(jù)就儲(chǔ)存在請(qǐng)求正文中。在請(qǐng)求包頭中有一些與請(qǐng)求正文相關(guān)的信息,例如: 現(xiàn)在的Web應(yīng)用通常采用Rest架構(gòu),請(qǐng)求的數(shù)據(jù)格式一般為json。這時(shí)就需要設(shè)置Content-Type: application/json。

更重要的事情-HTTP緩存

HTTP屬于客戶端緩存,我們常認(rèn)為瀏覽器有一個(gè)緩存數(shù)據(jù)庫(kù),用來保存一些靜態(tài)文件,下面我們分為以下幾個(gè)方面來簡(jiǎn)單介紹HTTP緩存

緩存的規(guī)則

緩存的方案

緩存的優(yōu)點(diǎn)

不同刷新的請(qǐng)求執(zhí)行過程

緩存的規(guī)則

緩存規(guī)則分為強(qiáng)制緩存協(xié)商緩存

強(qiáng)制緩存

當(dāng)緩存數(shù)據(jù)庫(kù)中有客戶端需要的數(shù)據(jù),客戶端直接將數(shù)據(jù)從其中拿出來使用(如果數(shù)據(jù)未失效),當(dāng)緩存服務(wù)器沒有需要的數(shù)據(jù)時(shí),客戶端才會(huì)向服務(wù)端請(qǐng)求。

協(xié)商緩存

又稱對(duì)比緩存。客戶端會(huì)先從緩存數(shù)據(jù)庫(kù)拿到一個(gè)緩存的標(biāo)識(shí),然后向服務(wù)端驗(yàn)證標(biāo)識(shí)是否失效,如果沒有失效服務(wù)端會(huì)返回304,這樣客戶端可以直接去緩存數(shù)據(jù)庫(kù)拿出數(shù)據(jù),如果失效,服務(wù)端會(huì)返回新的數(shù)據(jù)

強(qiáng)制緩存的優(yōu)先級(jí)高于協(xié)商緩存,若兩種緩存皆存在,且強(qiáng)制緩存命中目標(biāo),則協(xié)商緩存不再驗(yàn)證標(biāo)識(shí)。

緩存的方案

上面的內(nèi)容讓我們大概了解了緩存機(jī)制是怎樣運(yùn)行的,但是,服務(wù)器是如何判斷緩存是否失效呢?我們知道瀏覽器和服務(wù)器進(jìn)行交互的時(shí)候會(huì)發(fā)送一些請(qǐng)求數(shù)據(jù)和響應(yīng)數(shù)據(jù),我們稱之為HTTP報(bào)文。報(bào)文中包含首部header和主體部分body。與緩存相關(guān)的規(guī)則信息就包含在header中。boby中的內(nèi)容是HTTP請(qǐng)求真正要傳輸?shù)牟糠帧Ee個(gè)HTTP報(bào)文header部分的例子如下:

我們依舊分為強(qiáng)制緩存和協(xié)商緩存來分析。

強(qiáng)制緩存

對(duì)于強(qiáng)制緩存,服務(wù)器響應(yīng)的header中會(huì)用兩個(gè)字段來表明——Expires和Cache-Control。

Expires

Exprires的值為服務(wù)端返回的數(shù)據(jù)到期時(shí)間。當(dāng)再次請(qǐng)求時(shí)的請(qǐng)求時(shí)間小于返回的此時(shí)間,則直接使用緩存數(shù)據(jù)。但由于服務(wù)端時(shí)間和客戶端時(shí)間可能有誤差,這也將導(dǎo)致緩存命中的誤差,另一方面,Expires是HTTP1.0的產(chǎn)物,故現(xiàn)在大多數(shù)使用Cache-Control替代。

Cache-Control

Cache-Control有很多屬性,不同的屬性代表的意義也不同。

private:客戶端可以緩存

public:客戶端和代理服務(wù)器都可以緩存

max-age=t:緩存內(nèi)容將在t秒后失效

no-cache:需要使用協(xié)商緩存來驗(yàn)證緩存數(shù)據(jù)

no-store:所有內(nèi)容都不會(huì)緩存。

協(xié)商緩存

協(xié)商緩存需要進(jìn)行對(duì)比判斷是否可以使用緩存。瀏覽器第一次請(qǐng)求數(shù)據(jù)時(shí),服務(wù)器會(huì)將緩存標(biāo)識(shí)與數(shù)據(jù)一起響應(yīng)給客戶端,客戶端將它們備份至緩存中。再次請(qǐng)求時(shí),客戶端會(huì)將緩存中的標(biāo)識(shí)發(fā)送給服務(wù)器,服務(wù)器根據(jù)此標(biāo)識(shí)判斷。若未失效,返回304狀態(tài)碼,瀏覽器拿到此狀態(tài)碼就可以直接使用緩存數(shù)據(jù)了。

對(duì)于協(xié)商緩存來說,緩存標(biāo)識(shí)我們需要著重理解一下,下面我們將著重介紹它的兩種緩存方案。

Last-Modified

Last-Modified:服務(wù)器在響應(yīng)請(qǐng)求時(shí),會(huì)告訴瀏覽器資源的最后修改時(shí)間。

if-Modified-Since:瀏覽器再次請(qǐng)求服務(wù)器的時(shí)候,請(qǐng)求頭會(huì)包含此字段,后面跟著在緩存中獲得的最后修改時(shí)間。服務(wù)端收到此請(qǐng)求頭發(fā)現(xiàn)有if-Modified-Since,則與被請(qǐng)求資源的最后修改時(shí)間進(jìn)行對(duì)比,如果一致則返回304和響應(yīng)報(bào)文頭,瀏覽器只需要從緩存中獲取信息即可。 從字面上看,就是說:從某個(gè)時(shí)間節(jié)點(diǎn)算起,是否文件被修改了

如果真的被修改:那么開始傳輸響應(yīng)一個(gè)整體,服務(wù)器返回:200 OK

如果沒有被修改:那么只需傳輸響應(yīng)header,服務(wù)器返回:304 Not Modified

if-Unmodified-Since:從字面上看, 就是說: 從某個(gè)時(shí)間點(diǎn)算起, 是否文件沒有被修改

如果沒有被修改:則開始`繼續(xù)"傳送文件: 服務(wù)器返回: 200 OK

如果文件被修改:則不傳輸,服務(wù)器返回: 412 Precondition failed (預(yù)處理錯(cuò)誤)

這兩個(gè)的區(qū)別是一個(gè)是修改了才下載一個(gè)是沒修改才下載。

Last-Modified 說好卻也不是特別好,因?yàn)槿绻诜?wù)器上,一個(gè)資源被修改了,但其實(shí)際內(nèi)容根本沒發(fā)生改變,會(huì)因?yàn)長(zhǎng)ast-Modified時(shí)間匹配不上而返回了整個(gè)實(shí)體給客戶端(即使客戶端緩存里有個(gè)一模一樣的資源)。為了解決這個(gè)問題,HTTP1.1推出了Etag。

Etag

Etag:服務(wù)器響應(yīng)請(qǐng)求時(shí),通過此字段告訴瀏覽器當(dāng)前資源在服務(wù)器生成的唯一標(biāo)識(shí)(生成規(guī)則由服務(wù)器決定)

If-None-Match:再次請(qǐng)求服務(wù)器時(shí),瀏覽器的請(qǐng)求報(bào)文頭部會(huì)包含此字段,后面的值為在緩存中獲取的標(biāo)識(shí)。服務(wù)器接收到次報(bào)文后發(fā)現(xiàn)If-None-Match則與被請(qǐng)求資源的唯一標(biāo)識(shí)進(jìn)行對(duì)比。

不同,說明資源被改動(dòng)過,則響應(yīng)整個(gè)資源內(nèi)容,返回狀態(tài)碼200。

相同,說明資源無心修改,則響應(yīng)header,瀏覽器直接從緩存中獲取數(shù)據(jù)信息。返回狀態(tài)碼304.

但是實(shí)際應(yīng)用中由于Etag的計(jì)算是使用算法來得出的,而算法會(huì)占用服務(wù)端計(jì)算的資源,所有服務(wù)端的資源都是寶貴的,所以就很少使用Etag了。

緩存的優(yōu)點(diǎn)

減少了冗余的數(shù)據(jù)傳遞,節(jié)省寬帶流量

減少了服務(wù)器的負(fù)擔(dān),大大提高了網(wǎng)站性能

加快了客戶端加載網(wǎng)頁(yè)的速度 這也正是HTTP緩存屬于客戶端緩存的原因。

不同刷新的請(qǐng)求執(zhí)行過程

瀏覽器地址欄中寫入U(xiǎn)RL,回車

瀏覽器發(fā)現(xiàn)緩存中有這個(gè)文件了,不用繼續(xù)請(qǐng)求了,直接去緩存拿。(最快)

F5

F5就是告訴瀏覽器,別偷懶,好歹去服務(wù)器看看這個(gè)文件是否有過期了。于是瀏覽器就戰(zhàn)戰(zhàn)兢兢的發(fā)送一個(gè)請(qǐng)求帶上If-Modify-since。

Ctrl+F5

告訴瀏覽器,你先把你緩存中的這個(gè)文件給我刪了,然后再去服務(wù)器請(qǐng)求個(gè)完整的資源文件下來。于是客戶端就完成了強(qiáng)行更新的操作.

服務(wù)器處理請(qǐng)求并返回HTTP報(bào)文

它會(huì)對(duì)TCP連接進(jìn)行處理,對(duì)HTTP協(xié)議進(jìn)行解析,并按照?qǐng)?bào)文格式進(jìn)一步封裝成HTTP Request對(duì)象,供上層使用。這一部分工作一般是由Web服務(wù)器去進(jìn)行,我使用過的Web服務(wù)器有Tomcat, Nginx和Apache等等 HTTP報(bào)文也分成三份,狀態(tài)碼 ,響應(yīng)報(bào)頭響應(yīng)報(bào)文

狀態(tài)碼

狀態(tài)碼是由3位數(shù)組成,第一個(gè)數(shù)字定義了響應(yīng)的類別,且有五種可能取值:

1xx:指示信息–表示請(qǐng)求已接收,繼續(xù)處理。

2xx:成功–表示請(qǐng)求已被成功接收、理解、接受。

3xx:重定向–要完成請(qǐng)求必須進(jìn)行更進(jìn)一步的操作。

4xx:客戶端錯(cuò)誤–請(qǐng)求有語(yǔ)法錯(cuò)誤或請(qǐng)求無法實(shí)現(xiàn)。

5xx:服務(wù)器端錯(cuò)誤–服務(wù)器未能實(shí)現(xiàn)合法的請(qǐng)求。 平時(shí)遇到比較常見的狀態(tài)碼有:200, 204, 301, 302, 304, 400, 401, 403, 404, 422, 500

常見狀態(tài)碼區(qū)別

200 成功

請(qǐng)求成功,通常服務(wù)器提供了需要的資源。

204 無內(nèi)容

服務(wù)器成功處理了請(qǐng)求,但沒有返回任何內(nèi)容。

301 永久移動(dòng)

請(qǐng)求的網(wǎng)頁(yè)已永久移動(dòng)到新位置。 服務(wù)器返回此響應(yīng)(對(duì) GET 或 HEAD 請(qǐng)求的響應(yīng))時(shí),會(huì)自動(dòng)將請(qǐng)求者轉(zhuǎn)到新位置。

302 臨時(shí)移動(dòng)

服務(wù)器目前從不同位置的網(wǎng)頁(yè)響應(yīng)請(qǐng)求,但請(qǐng)求者應(yīng)繼續(xù)使用原有位置來進(jìn)行以后的請(qǐng)求。

304 未修改

自從上次請(qǐng)求后,請(qǐng)求的網(wǎng)頁(yè)未修改過。 服務(wù)器返回此響應(yīng)時(shí),不會(huì)返回網(wǎng)頁(yè)內(nèi)容。

400 錯(cuò)誤請(qǐng)求

服務(wù)器不理解請(qǐng)求的語(yǔ)法。

401 未授權(quán)

請(qǐng)求要求身份驗(yàn)證。 對(duì)于需要登錄的網(wǎng)頁(yè),服務(wù)器可能返回此響應(yīng)。

403 禁止

服務(wù)器拒絕請(qǐng)求。

404 未找到

服務(wù)器找不到請(qǐng)求的網(wǎng)頁(yè)。

422 無法處理

請(qǐng)求格式正確,但是由于含有語(yǔ)義錯(cuò)誤,無法響應(yīng)

500 服務(wù)器內(nèi)部錯(cuò)誤

服務(wù)器遇到錯(cuò)誤,無法完成請(qǐng)求。

響應(yīng)報(bào)頭

常見的響應(yīng)報(bào)頭字段有: Server, Connection...。

響應(yīng)報(bào)文

你從服務(wù)器請(qǐng)求的HTML,CSS,JS文件就放在這里面

瀏覽器解析渲染頁(yè)面

這個(gè)圖就是Webkit解析渲染頁(yè)面的過程。

解析HTML形成DOM樹

解析CSS形成CSSOM 樹

合并DOM樹和CSSOM樹形成渲染樹

瀏覽器開始渲染并繪制頁(yè)面 這個(gè)過程涉及兩個(gè)比較重要的概念回流重繪,DOM結(jié)點(diǎn)都是以盒模型形式存在,需要瀏覽器去計(jì)算位置和寬度等,這個(gè)過程就是回流。等到頁(yè)面的寬高,大小,顏色等屬性確定下來后,瀏覽器開始繪制內(nèi)容,這個(gè)過程叫做重繪。瀏覽器剛打開頁(yè)面一定要經(jīng)過這兩個(gè)過程的,但是這個(gè)過程非常非常非常消耗性能,所以我們應(yīng)該盡量減少頁(yè)面的回流和重繪

性能優(yōu)化之回流重繪

回流

當(dāng)Render Tree中部分或全部元素的尺寸、結(jié)構(gòu)、或某些屬性發(fā)生改變時(shí),瀏覽器重新渲染部分或全部文檔的過程稱為回流。

會(huì)導(dǎo)致回流的操作:

頁(yè)面首次渲染

瀏覽器窗口大小發(fā)生改變

元素尺寸或位置發(fā)生改變

元素內(nèi)容變化(文字?jǐn)?shù)量或圖片大小等等)

元素字體大小變化

添加或者刪除可見的DOM元素

激活CSS偽類(例如::hover)

查詢某些屬性或調(diào)用某些方法

一些常用且會(huì)導(dǎo)致回流的屬性和方法:

clientWidth、clientHeight、clientTop、clientLeft

offsetWidth、offsetHeight、offsetTop、offsetLeft

scrollWidth、scrollHeight、scrollTop、scrollLeft

scrollIntoView()、scrollIntoViewIfNeeded()

getComputedStyle()

getBoundingClientRect()

scrollTo()

重繪

當(dāng)頁(yè)面中元素樣式的改變并不影響它在文檔流中的位置時(shí)(例如:color、background-color、visibility等),瀏覽器會(huì)將新樣式賦予給元素并重新繪制它,這個(gè)過程稱為重繪。

優(yōu)化

CSS

避免使用table布局。

盡可能在DOM樹的最末端改變class。

避免設(shè)置多層內(nèi)聯(lián)樣式。

將動(dòng)畫效果應(yīng)用到position屬性為absolute或fixed的元素上。

避免使用CSS表達(dá)式(例如:calc())。

JavaScript

避免頻繁操作樣式,最好一次性重寫style屬性,或者將樣式列表定義為class并一次性更改class屬性。

避免頻繁操作DOM,創(chuàng)建一個(gè)documentFragment,在它上面應(yīng)用所有DOM操作,最后再把它添加到文檔中。

也可以先為元素設(shè)置display: none,操作結(jié)束后再把它顯示出來。因?yàn)樵赿isplay屬性為none的元素上進(jìn)行的DOM操作不會(huì)引發(fā)回流和重繪。

避免頻繁讀取會(huì)引發(fā)回流/重繪的屬性,如果確實(shí)需要多次使用,就用一個(gè)變量緩存起來。

對(duì)具有復(fù)雜動(dòng)畫的元素使用絕對(duì)定位,使它脫離文檔流,否則會(huì)引起父元素及后續(xù)元素頻繁回流。

JS的解析

JS的解析是由瀏覽器的JS引擎完成的。由于JavaScript是單進(jìn)程運(yùn)行,也就是說一個(gè)時(shí)間只能干一件事,干這件事情時(shí)其他事情都有排隊(duì),但是有些人物比較耗時(shí)(例如IO操作),所以將任務(wù)分為同步任務(wù)異步任務(wù),所有的同步任務(wù)放在主線程上執(zhí)行,形成執(zhí)行棧,而異步任務(wù)等待,當(dāng)執(zhí)行棧被清空時(shí)才去看看異步任務(wù)有沒有東西要搞,有再提取到主線程執(zhí)行,這樣往復(fù)循環(huán)(冤冤相報(bào)何時(shí)了,阿彌陀佛),就形成了Event Loop事件循環(huán),下面來看看大人物

Event Loop

先看一段代碼

setTimeout(function(){
    console.log("定時(shí)器開始啦")
});

new Promise(function(resolve){
    console.log("馬上執(zhí)行for循環(huán)啦");
    for(var i = 0; i < 10000; i++){
        i == 99 && resolve();
    }
}).then(function(){
    console.log("執(zhí)行then函數(shù)啦")
});

console.log("代碼執(zhí)行結(jié)束");

結(jié)果我想大家都應(yīng)該知道。主要來介紹JavaScript的解析,至于Promise等下一節(jié)再說

JavaScript

JavaScript是一門單線程語(yǔ)言,盡管H5中提出了Web-Worker,能夠模擬實(shí)現(xiàn)多線程,但本質(zhì)上還是單線程,說它是多線程就是扯淡。

事件循環(huán)

既然是單線程,每個(gè)事件的執(zhí)行就要有順序,比如你去銀行取錢,前面的人在進(jìn)行,后面的就得等待,要是前面的人弄個(gè)一兩個(gè)小時(shí),估計(jì)后面的人都瘋了,因此,瀏覽器的JS引擎處理JavaScript時(shí)分為同步任務(wù)異步任務(wù)

這張圖我們可以清楚看到

同步和異步任務(wù)分別進(jìn)入不同的執(zhí)行"場(chǎng)所",同步的進(jìn)入主線程,異步的進(jìn)入Event Table并注冊(cè)函數(shù)。

當(dāng)指定的事情完成時(shí),Event Table會(huì)將這個(gè)函數(shù)移入Event Queue。

主線程內(nèi)的任務(wù)執(zhí)行完畢為空,會(huì)去Event Queue讀取對(duì)應(yīng)的函數(shù),進(jìn)入主線程執(zhí)行。

上述過程會(huì)不斷重復(fù),也就是常說的Event Loop(事件循環(huán))。

js引擎存在monitoring process進(jìn)程,會(huì)持續(xù)不斷的檢查主線程執(zhí)行棧是否為空,一旦為空,就會(huì)去Event Queue那里檢查是否有等待被調(diào)用的函數(shù)。 估計(jì)看完這些你對(duì)事件循環(huán)有一定的了解,但是事實(shí)上我們看對(duì)的沒這么簡(jiǎn)單,通常我們會(huì)看到Promise,setTimeout,process.nextTick(),這個(gè)時(shí)候你和我就懵逼。

除了同步任務(wù)和異步任務(wù),我們還分為宏任務(wù)和微任務(wù),常見的有以下幾種

macro-task(宏任務(wù)):包括整體代碼script,setTimeout,setInterval

micro-task(微任務(wù)):Promise,process.nextTick 不同任務(wù)會(huì)進(jìn)入不同的任務(wù)隊(duì)列來執(zhí)行。 JS引擎開始工作后,先在宏任務(wù)中開始第一次循環(huán)(script里面先執(zhí)行,不過我喜歡把它拎出來,直接稱其進(jìn)入執(zhí)行棧),當(dāng)主線程執(zhí)行棧全部任務(wù)被清空后去微任務(wù)看看,如果有等待執(zhí)行的任務(wù),執(zhí)行全部的微任務(wù)(其實(shí)將其回調(diào)函數(shù)推入執(zhí)行棧來執(zhí)行),再去宏任務(wù)找最先進(jìn)入隊(duì)列的任務(wù)執(zhí)行,執(zhí)行這個(gè)任務(wù)后再去主線程執(zhí)行任務(wù)(例如執(zhí)行```console.log("hello world")這種任務(wù)),執(zhí)行棧被清空后再去微任務(wù),這樣往復(fù)循環(huán)(冤冤相報(bào)何時(shí)了)

Tip:微任務(wù)會(huì)全部執(zhí)行,而宏任務(wù)會(huì)一個(gè)一個(gè)來執(zhí)行

下面來看一段代碼

setTimeout(function() {
    console.log("setTimeout");
})

new Promise(function(resolve) {
    console.log("promise");
}).then(function() {
    console.log("then");
})

console.log("console");

我們看看它的執(zhí)行情況

第一輪

這段代碼進(jìn)入主線程

遇到setTimeout,將其回調(diào)函數(shù)注冊(cè)后分發(fā)到宏任務(wù)

第二輪

遇到Promise,new Promise立即執(zhí)行(這個(gè)不解釋,想了解的我后續(xù)文章會(huì)介紹),輸出promise,遇到then,將其分發(fā)到微任務(wù)

第三輪

遇到console.log("console"),直接輸出console

第四輪

主線程執(zhí)行棧已經(jīng)清空,先去微任務(wù)看看,執(zhí)行then函數(shù),輸出then

第五輪

微任務(wù)執(zhí)行完了,看看宏任務(wù),有個(gè)setTimeout,輸出setTimeout,整體執(zhí)行完畢。 具體的執(zhí)行過程大致就是這樣,可能我有疏忽的地方,還望指正。

再來看看一段復(fù)雜的代碼

console.log("1");

setTimeout(function() {
    console.log("2");
    process.nextTick(function() {
        console.log("3");
    })
    new Promise(function(resolve) {
        console.log("4");
        resolve();
    }).then(function() {
        console.log("5")
    })
})
process.nextTick(function() {
    console.log("6");
})
new Promise(function(resolve) {
    console.log("7");
    resolve();
}).then(function() {
    console.log("8")
})

setTimeout(function() {
    console.log("9");
    process.nextTick(function() {
        console.log("10");
    })
    new Promise(function(resolve) {
        console.log("11");
        resolve();
    }).then(function() {
        console.log("12")
    })
})

我們來分析一下

整體script進(jìn)入主線程,遇到console.log("1"),直接輸出

遇到setTimeout,將其回調(diào)函數(shù)分發(fā)到宏任務(wù)事件隊(duì)列,暫時(shí)標(biāo)記為setTimeout1

遇到process.nextTick(),將其回調(diào)函數(shù)分發(fā)到微任務(wù)事件隊(duì)列,標(biāo)記為process.nextTick1(這個(gè)地方有點(diǎn)出入,我一般認(rèn)為```process.nextTick()推入主線程執(zhí)行棧棧底,作為執(zhí)行棧最后一個(gè)任務(wù)執(zhí)行)

遇到Promise,立即執(zhí)行,輸出7,then函數(shù)分發(fā)的微任務(wù)事件隊(duì)列,標(biāo)記為Promise1。

遇到setTimeout,將其回調(diào)函數(shù)分發(fā)到微任務(wù)事件隊(duì)列,標(biāo)記為setTimeout2。

現(xiàn)在已經(jīng)輸出了1,7,宏任務(wù)和微任務(wù)的事件隊(duì)列 情況如下

我們接著來看

現(xiàn)在主線程執(zhí)行棧被清空,去微任務(wù)看看,發(fā)現(xiàn)有兩個(gè)事件等待,由于隊(duì)列是先進(jìn)先出,執(zhí)行process.nextTick1,輸出6,接著執(zhí)行Promise1,輸出8。

至此,第一輪循環(huán)已經(jīng)結(jié)束,輸出了1,7,6,8,接下來執(zhí)行第二輪循環(huán) ,先從宏任務(wù)的setTimeout1開始

遇到console.log("2"),執(zhí)行輸出。

遇到process.nextTick(),將其回調(diào)函數(shù)分發(fā)到微任務(wù),標(biāo)記為process.nextTick2,又遇到 Promise,立即執(zhí)行,輸出4,將then函數(shù)推入微任務(wù)事件隊(duì)列,標(biāo)記為Promise2

到此宏任務(wù)的一個(gè)任務(wù)執(zhí)行完畢,輸出了2,4,來看看事件隊(duì)列

去微任務(wù)看看,我們先處理process.nextTick2,輸出3,接著再來執(zhí)行Promise2,輸出5。 第二輪循環(huán)執(zhí)行完畢?,F(xiàn)在一共輸出了1,7,6,8,2,4,3,5

setTimeout2開始第三輪循環(huán) ,先直接輸出9,遇到process.nextTick(),將其回調(diào)函數(shù)分發(fā)到微任務(wù)事件隊(duì)列,標(biāo)記為process.nextTick3,又遇到惡心的Promise,立即執(zhí)行輸出11,將then函數(shù)分發(fā)到微任務(wù),標(biāo)記為Promise3。

執(zhí)行微任務(wù),看看還有撒子事件

居然還有事件,能咋辦,接著執(zhí)行唄,輸出10,12。 至此,全部任務(wù)執(zhí)行完畢,輸出順序?yàn)?b>1,7,6,8,2,4,3,5,9,11,10,12.

注意,這段代碼執(zhí)行結(jié)果可能與node等環(huán)境不同而發(fā)生變化。

我想說的也說完了,不知道您懂了嘛

總結(jié)

這篇文章由一個(gè)簡(jiǎn)單的問題扯出了很多前端工程師必學(xué)也是很重要的東西,但是由于我本人水平較低,很多地方都是一筆帶過,甚至有些地方還有錯(cuò)誤,望各位同仁指正批評(píng)。

文章版權(quán)歸作者所有,未經(jīng)允許請(qǐng)勿轉(zhuǎn)載,若此文章存在違規(guī)行為,您可以聯(lián)系管理員刪除。

轉(zhuǎn)載請(qǐng)注明本文地址:http://systransis.cn/yun/7371.html

相關(guān)文章

  • Java3y文章目錄導(dǎo)航

    摘要:前言由于寫的文章已經(jīng)是有點(diǎn)多了,為了自己和大家的檢索方便,于是我就做了這么一個(gè)博客導(dǎo)航。 前言 由于寫的文章已經(jīng)是有點(diǎn)多了,為了自己和大家的檢索方便,于是我就做了這么一個(gè)博客導(dǎo)航。 由于更新比較頻繁,因此隔一段時(shí)間才會(huì)更新目錄導(dǎo)航哦~想要獲取最新原創(chuàng)的技術(shù)文章歡迎關(guān)注我的公眾號(hào):Java3y Java3y文章目錄導(dǎo)航 Java基礎(chǔ) 泛型就這么簡(jiǎn)單 注解就這么簡(jiǎn)單 Druid數(shù)據(jù)庫(kù)連接池...

    KevinYan 評(píng)論0 收藏0
  • 面試集 - 收藏集 - 掘金

    摘要:計(jì)算數(shù)組的極值微信面試題獲取元素的最終前端掘金一題目用代碼求出頁(yè)面上一個(gè)元素的最終的,不考慮瀏覽器,不考慮元素情況。 Excuse me?這個(gè)前端面試在搞事! - 前端 - 掘金金三銀四搞事季,前端這個(gè)近年的熱門領(lǐng)域,搞事氣氛特別強(qiáng)烈,我朋友小偉最近就在瘋狂面試,遇到了許多有趣的面試官,有趣的面試題,我來幫這個(gè)搞事 boy 轉(zhuǎn)述一下。 以下是我一個(gè)朋友的故事,真的不是我。 ... ja...

    crossea 評(píng)論0 收藏0
  • 前端面試url頁(yè)面展現(xiàn),這之中發(fā)生什么?

    摘要:比如對(duì)于的,瀏覽器實(shí)際上不知道到底是什么東西,需要查找網(wǎng)站所在服務(wù)器的地址,才能找到目標(biāo),這就是下文要說的域名解析。二域名解析當(dāng)用戶在瀏覽器中輸入后你使用的電腦會(huì)發(fā)出一個(gè)請(qǐng)求到本地服務(wù)器。 showImg(https://segmentfault.com/img/remote/1460000009317499?w=700&h=466); 這里markdown格式跟簡(jiǎn)書不太一樣,排版可能...

    wdzgege 評(píng)論0 收藏0
  • 2019春招前端實(shí)習(xí)面經(jīng)總結(jié)

    摘要:春招前端實(shí)習(xí)面試記錄從就開始漸漸的進(jìn)行復(fù)習(xí),月末開始面試,到現(xiàn)在四月中旬基本宣告結(jié)束。上海愛樂奇一面盒模型除之外的面向?qū)ο笳Z(yǔ)言繼承因?yàn)槭且曨l面試,只記得這么多,只感覺考察的面很廣,前端后端移動(dòng)端都問了,某方面也有深度。 春招前端實(shí)習(xí)面試記錄(2019.3 ~ 2019.5) 從2019.1就開始漸漸的進(jìn)行復(fù)習(xí),2月末開始面試,到現(xiàn)在四月中旬基本宣告結(jié)束。在3月和4月經(jīng)歷了無數(shù)次失敗,沮...

    atinosun 評(píng)論0 收藏0
  • 上最詳細(xì)Python學(xué)習(xí)路線-入門精通,只需90天

    摘要:針對(duì)的初學(xué)者,從無到有的語(yǔ)言如何入門,主要包括了的簡(jiǎn)介,如何下載,如何安裝,如何使用終端,等各種開發(fā)環(huán)境進(jìn)行開發(fā),中的語(yǔ)法和基本知識(shí)概念和邏輯,以及繼續(xù)深入學(xué)習(xí)的方法。 ...

    gghyoo 評(píng)論0 收藏0

發(fā)表評(píng)論

0條評(píng)論

最新活動(dòng)
閱讀需要支付1元查看
<