「不畏懼,不講究,將來的日子好好努力」 ;你們好,我是小芝麻😄javascript
本文腦圖較多,方便梳理,適合:前端小白php
今天小芝麻主要想闡述的就是一個問題:css
request請求階段html
- 一、URL 地址解析
- 二、DNS 域名解析
- 三、和服務器創建 TCP 鏈接
- 四、發送 HTTP 請求
response 響應階段前端
- 五、服務器獲得並處理請求
斷開鏈接java
- 六、和服務器斷開 TCP 鏈接
渲染頁面編程
- 七、客戶端渲染服務器返回的內容
下面咱們會詳細闡述以上幾個方面具體都是啥;json
在開始正題以前,咱們先了解如下,這兩個概念;後端
具體交互如圖: 跨域
好的下面進入正題
前面的文章裏寫過關於 URL參數處理的問題,那到底什麼是URL呢?
一個完整的URL所包含的內容
給服務器通網後,會有兩個 IP 地址:
- 內網IP:局域網內訪問
- 外網IP:外部用戶基於外網IP訪問到服務器(例如:125.39.174.200)
一級、二級、三級...域名都是由頂級域名衍生出來的,因此,公司在註冊域名的時候,只要買下頂級域名就能夠了,就例如 qq.com ,只須要買下 qq.com 就能夠用 www.qq.com、sports.qq.com、kbs.sports.qq.com...等域名了。
若是項目採用的就是默認端口號,咱們在書寫地址的時候,不用加端口號,瀏覽器在發送請求的時候會幫咱們默認給加上
默認的路徑或者名稱:xxx.com/stu/ 不指定資源名,服務器會找默認的資源,通常默認資源名是default.html、index.html... 固然這些能夠在服務器端本身配置
注意僞URL地址的處理:
僞 URL 的目的:URL重寫技術是爲了增長SEO搜索引擎優化的,動態的網址通常不能被搜索引擎收錄(因此咱們要把動態網址靜態化,此時須要的是重寫URL)
請求的地址中若是出現非有效 UNICODE 編碼內容,現代版瀏覽器會默認的進行編碼
編碼方式:
客戶端和服務器端進行信息傳輸的時候,若是須要把請求的地址和信息編碼,咱們則基於以上兩種方式處理, 服務器端也存在這些方法,這樣就能夠統一編碼解碼了
三、escape/unescape
例如:從列表跳轉到詳情,咱們能夠把傳遞的中文信息基於這個編碼,詳情頁獲取編碼後的信息再解碼,再好比咱們在客戶端種的cookie信息,若是信息是中文,咱們也基於這種辦法編碼...
而咱們發送請求時候所謂的DNS解析,其實就是根據域名,在DNS服務器上查找到對應服務器的外網IP
本地解析查找過程:
非本地解析:
DNS優化:
DNS緩存:通常瀏覽器會在第一次解析後,默認創建緩存,時間很短,只有一分鐘左右
SYN:同步序列編號(Synchronize Sequence Numbers)
。以上是說的太過正式,下面說個白話版的👇:其實就相似於兩我的打招呼的過程;
到此雙方試探成功,創建友好的關係👬;(這條路算是打通了)
沒有錯,不要懷疑,就是這麼簡單~~
有了路以後,怎麼傳遞數據呢,咱們還須要個快遞小哥;(HTTP 就是快遞小哥了)
HTTP 報文是由 請求報文+響應報文 共同組成
至於報文是啥?在哪查看咱們一會再說,先看下經常使用的狀態碼有哪些;
1~5開頭,三位數字
=>谷歌瀏覽器F12
=>Network(全部客戶端和服務器端的交互信息在這裏均可以看到)
=>點擊某一條信息,在右側能夠看到全部的HTTP報文信息
上圖就是了😄,接下來咱們看幾個經常使用的
主要分兩種:
- GET 系列請求
- POST 系列請求
不論GET和POST均可以把信息傳遞給服務器,也能從服務器獲取到結果
GET 與 POST 系列請求的區別(所謂的區別都是約定俗成的,並無必定要這樣作的規定)
本質區別
GET系列傳遞給服務器信息的方式通常採用:問號傳參
POST系列傳遞給服務器信息的方式通常採用:設置請求主體
本質區別所致使的問題
好了,HTTP請求就先說到這裏😄
服務器接收到請求後:
上面咱們說 創建 TCP 鏈接,就是修路,HTTP 就是快遞小哥,在小哥送完快遞後,咱們就要把以前修好的路拆掉,就有了 TCP 四次揮手
白話文翻譯👇:
爲何創建鏈接協議是三次握手,而關閉鏈接倒是四次握手呢?
這篇咱們以前寫過瀏覽器渲染頁面的主體流程,此次咱們把主要的步驟在寫一下。
渲染了全部的 HTML 標籤
W3C
規範(第五代版本的規範H5)的規則,轉換成咱們能看懂的標籤DOM
節點DOM
節點之間的關係,生成DOM
樹
請求回來 CSS 後,渲染完 CSS
同生成DOM
樹同樣的過程生成CSSOM
樹
按照
RENDER-TREE
在設備的視口中進行結構和位置的相關計算=>佈局(Layout)或 重排/迴流(reflow)
根據渲染樹以及迴流獲得的幾何信息,獲得節點的絕對像素=>繪製(painting)或 柵格化(rasterizing)
到此 「當用戶在地址欄輸入了網址,到看到頁面中間都經歷了什麼?」這道題咱們算是解答完了。
做爲前端咱們在寫代碼的時候,都是經過 AJAX 向服務器發送請求,下面咱們在說一下 AJAX 的基礎知識和操做
全稱:async javascript and xml(異步的 JS 和 XML)
此處的異步指的是:局部刷新(對應的是全局刷新)
全局刷新:服務器渲染
局部刷新:客戶端渲染
XML:可擴展的標記語言,用本身自定義的標籤來存儲數據的
XML 格式
<?xml version="1.0" encoding="UTF-8"?>
<root>
<student>
<name>張三</name>
<age>25</age>
<score>
<english>95</english>
</score>
</student>
<student>
<name>張三</name>
<age>25</age>
</student>
<student>
<name>張三</name>
<age>25</age>
</student>
</root>
複製代碼
JSON 格式
[{
"name": "張三",
"age": 25,
"score": {
"english": 95
}
}, {
"name": "張三",
"age": 25
}, {
"name": "張三",
"age": 25
}]
複製代碼
AJAX任務:發送一個請求給服務器,從服務器獲取到對應的內容是從SEND後開始到XHR.READYSTATE===4的時候算任務結束
核心四步
1.建立AJAX實例
2.打開URL(配置發送請求的信息)
3.監聽AJAX狀態
4.發送請求
//1.建立AJAX實例
let xhr=new XMLHttpRequest;
//2.打開URL(配置發送請求的信息)
xhr.open('GET','./json/xxx.json',true);
//3.監聽AJAX狀態,在狀態爲X的時候,獲取服務器響應的內容
xhr.onreadystatechange=function(){
if(xhr.readyState===4 && /^(2|3)\d{2}$/.test(xhr.status)){
let result = xhr.responseText;
}
}
//4.發送請求
//SEND中放的是請求主體的內容
xhr.send(null);
//=> AJAX任務(發送一個請求給服務器,從服務器獲取到對應的內容)
//=> 從SEND後開始,到XHR.READYSTATE===4的時候算任務結束
複製代碼
客戶端怎麼把信息傳遞給服務器?
服務器怎麼把信息返回給客戶端?
/* * 倒計時搶購要注意的細節知識點: * 1.計算剩餘多久須要的時間不能是客戶端本機的時間,須要獲取服務器的時間 * (基於AJAX請求向服務器獲取,可是也不能每隔1秒都去請求一次 * 【咱們是第一次加載頁面的時候,從服務器獲取服務器時間,存儲起來,後期咱們把 * 這個時間自動的每隔1秒進行累加,頁面刷新還須要從服務器獲取】) * 2.爲了保證絕對安全,在購買的時候,服務器須要二次校驗時間的合法性 * 3.下降服務器返回時間和真實時間的偏差(減小服務器的響應時間) * 1)請求方式基於HEAD,只獲取響應頭信息便可 * 2)在AJAX狀態爲2的時候處理,無需等到狀態爲4 */
複製代碼
HTML
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<meta http-equiv="X-UA-Compatible" content="ie=edge">
<meta name="viewport" content="width=device-width, user-scalable=no, initial-scale=1.0">
<title>Document</title>
<!-- IMPORT CSS -->
<style> * { margin: 0; padding: 0; } #box { box-sizing: border-box; padding: 20px; width: 200px; margin: 20px auto; background: lightblue; line-height: 50px; } </style>
</head>
<body>
<div id="box">
距離秒殺還差:
<span id="content">00:00:00</span>
</div>
<!-- IMPORT JS -->
<script src="1.js"></script>
</body>
</html>
複製代碼
JS
let box = document.querySelector('#box'),
content = document.querySelector('#content'),
timer = null;
// 獲取服務器時間
function getServerTime() {
return new Promise(resolve => {
let xhr = new XMLHttpRequest;
xhr.open('head', './data.json?_=' + Math.random());
xhr.onreadystatechange = () => {
if (xhr.readyState === 2 && /^(2|3)\d{2}$/.test(xhr.status)) {
let time = xhr.getResponseHeader('date');
// 獲取的TIME是格林尼治時間 GMT(北京時間 GMT+0800)
time = new Date(time);
resolve(time);
}
};
xhr.send(null);
});
}
// 根據服務器時間計算倒計時
function computed(time) {
// time從服務器獲取的當時間
// target是搶購的目標時間
// spanTime兩個時間的毫秒差
let target = new Date('2020/05/13 15:57:35'),
spanTime = target - time;
if (spanTime <= 0) {
// 已經到達搶購的時間節點了
box.innerHTML = "開始搶購吧!";
clearInterval(timer);
return;
}
// 計算出毫秒差中包含多少小時、多少分鐘、多少秒
let hours = Math.floor(spanTime / (60 * 60 * 1000));
spanTime = spanTime - hours * 60 * 60 * 1000;
let minutes = Math.floor(spanTime / (60 * 1000));
spanTime = spanTime - minutes * 60 * 1000;
let seconds = Math.floor(spanTime / 1000);
hours < 10 ? hours = '0' + hours : null;
minutes < 10 ? minutes = '0' + minutes : null;
seconds < 10 ? seconds = '0' + seconds : null;
content.innerHTML = `${hours}:${minutes}:${seconds}`;
}
getServerTime().then(time => {
// 獲取到服務器時間後,計算倒計時
computed(time);
// 每間隔1秒中,讓獲取的時間累加1秒,在從新計算倒計時結果
timer = setInterval(() => {
time = new Date(time.getTime() + 1000);
computed(time);
}, 1000);
});
複製代碼
今天的文章到此結束,小芝麻自知還有不少不足,提高空間還有很大很大,感謝打什麼批評指正🙏