網(wǎng)絡(luò)公司+網(wǎng)站建設(shè)+小程序百度企業(yè)官網(wǎng)
文章目錄
- 瀏覽器對象詳解
- 一、參考資料
- 二、認(rèn)識瀏覽器運(yùn)行態(tài)下的 js
- 1.問:是否了解瀏覽器的執(zhí)行態(tài)(分層設(shè)計(jì))?
- 2.BOM
- 1.[location](https://developer.mozilla.org/zh-CN/docs/Web/API/Location)
- 拓展方向:
- 2.[History](https://developer.mozilla.org/zh-CN/docs/Web/API/History)
- 3.[navigator](https://developer.mozilla.org/zh-CN/docs/Web/API/Navigator)
- 4.[screen](https://developer.mozilla.org/zh-CN/docs/Web/API/Screen)
- 三、瀏覽器事件
- 1.addEventListener 第三個參數(shù)
- 2.阻斷事件傳播
- 3.阻止默認(rèn)行為
- 拓展方向
- 性能方向
- 兼容性方向
- 四、網(wǎng)絡(luò)請求
- 1.XMLHttpRequest
- 2.fetch
- 3.Http 狀態(tài)碼和 Header
- 1.狀態(tài)碼
- [1|2|3]xx
- 4xx
- 5xx
- 2.字段
- 4.拓展方向
- 如何應(yīng)對網(wǎng)絡(luò)不穩(wěn)定(波動)的情況?
- 如何處理并發(fā)請求?
- 往期精彩文章
瀏覽器對象詳解
一、參考資料
- 瀏覽器對象模型
- 聊聊 H5 的 pushState 與 replaceState
- 用 Javascript 獲取頁面元素的位置
- js 獲取操作元素位置
二、認(rèn)識瀏覽器運(yùn)行態(tài)下的 js
1.問:是否了解瀏覽器的執(zhí)行態(tài)(分層設(shè)計(jì))?
- ECMAScript - 基礎(chǔ)邏輯、數(shù)據(jù)處理,js 語法塊
- BOM - 瀏覽器本身的能力操作
- Browser Object Model(瀏覽器對象模型)
- 瀏覽器模型提供了獨(dú)立于內(nèi)容的、可以與瀏覽器窗口進(jìn)行滑動的對象結(jié)構(gòu),就是瀏覽器提供的 API
- DOM - 瀏覽器文本的操作
2.BOM
1.location
提供當(dāng)前窗口中的加載的文檔有關(guān)的信息和一些導(dǎo)航功能。
既是 window 對象屬性,也是 document 的對象屬性
window.location === document.location; //true// https://www.zhihu.com/search?type=content&q=123location.href ='https://www.zhihu.com/search?type=content&q=123'.origin = // 完整的url'https://www.zhihu.com'.host = // 頁面的標(biāo)準(zhǔn)域名'www.zhihu.com'.hash = // 服務(wù)器名稱+端口 例如:‘www.zhihu.com:8080’'#hash'.pathname = // url中#號后面的哈希值'/search'.search = // url中[/]后面內(nèi)容'?type=content&q=123'.protocol = // url中[?]后面內(nèi)容'https:'.port = // 協(xié)議[http:]''; //端口號:[ 80 | 8080 | ... ]
方法:
- assign()
不會打開新窗口
,把請求 url 中的資源,加載到當(dāng)前窗口- 會給瀏覽器的
History
中增加一條歷史記錄
- replace(url)
- 用 url 中的內(nèi)容,替換掉當(dāng)前的 location 資源
- 不會在瀏覽器的
History
中增加記錄,意味著用戶不能使用回退
按鈕
- reload()
- 重新加載當(dāng)前 url 的內(nèi)容
- 當(dāng) reload(true)時,會
強(qiáng)制從服務(wù)器獲取
所有內(nèi)容 - 若沒有參數(shù),重新加載時,
可能從瀏覽器緩存
加載頁面
拓展方向:
- location 本身 api 操作
- assign VS replace 的區(qū)別
- 解析 url 中的查詢字符串,返回一個對象
- 思路:
- 可以通過
正則
的方式獲取,或者通過字符串分割
的方式 - 通過
location.search
獲取查詢字符串內(nèi)容,如果有[?]
就截取[?]
后面的內(nèi)容 - 然后通過
[&]
進(jìn)行分割成為['key=val1','key=val2']
的形式 - 通過
[=]
對數(shù)組進(jìn)行分割,使用decodeURIComponent
對 key 和 val 進(jìn)行解碼,存放到對象中
- 路由相關(guān): 跳轉(zhuǎn)、參數(shù)、操作
- 頁面能否
返回
(history) - 頁面是否
刷新
(hash) - reload、assign、replace 參數(shù)等
- 頁面能否
- URI or URL 的區(qū)別?
- URI
Uniform Resource Identifier - 統(tǒng)一資源標(biāo)識符
- URL
Uniform Resource Locator - 統(tǒng)一資源定位符
- URI
2.History
history.state.length; // 當(dāng)前頁面的狀態(tài) // 返回當(dāng)前 `會話` 中的 history 個數(shù)
方法:
- pushState(state, title, url)
- 給當(dāng)前的 history 中添加一個狀態(tài)
- 瀏覽器地址會改變,但是不會加載頁面,本質(zhì)上頁面還是停留在原來的頁面
- replaceState()
- 與 pushState 方法類似,但是
不會添加
一個新的 state 到 history 中,而是直接修改當(dāng)前
state
- 與 pushState 方法類似,但是
相關(guān)聯(lián)的方法
- window.onpopstate()
- 每當(dāng)處于激活狀態(tài)的歷史記錄條目發(fā)生變化時,
popstate
事件就會在對應(yīng) window 對象上觸發(fā) - 調(diào)用
history.pushState()
或者history.replaceState()
不會
觸發(fā) popstate 事件。 - popstate 事件只會在
瀏覽器
某些行為下觸發(fā)
- 比如點(diǎn)擊后退、前進(jìn)按鈕(或者在 JavaScript 中調(diào)用 history.back()、history.forward()、history.go()方法)
- 每當(dāng)處于激活狀態(tài)的歷史記錄條目發(fā)生變化時,
例子:
window.onpopstate = function (event) {alert('location: ' +document.location +', state: ' +JSON.stringify(event.state),);
};
//綁定事件處理函數(shù).
history.pushState({ page: 1 }, 'title 1', '?page=1'); //添加并激活一個歷史記錄條目 http://example.com/example.html?page=1,條目索引為1
history.pushState({ page: 2 }, 'title 2', '?page=2'); //添加并激活一個歷史記錄條目 http://example.com/example.html?page=2,條目索引為2
history.replaceState({ page: 3 }, 'title 3', '?page=3'); //修改當(dāng)前激活的歷史記錄條目 http://ex..?page=2 變?yōu)?http://ex..?page=3,條目索引為3
history.back(); // 彈出 "location: http://example.com/example.html?page=1, state: {"page":1}"
history.back(); // 彈出 "location: http://example.com/example.html, state: null
history.go(2); // 彈出 "location: http://example.com/example.html?page=3, state: {"page":3}
3.navigator
瀏覽器系統(tǒng)信息大集合
- clipboard
- 系統(tǒng)剪切板相關(guān)信息
- userAgent
- 當(dāng)前用戶的設(shè)備信息
- onLine
- 返回瀏覽器的在線狀態(tài)
- serial
- 返回串口對象,Web Serial API 的入口點(diǎn)
- bluetooth
- 系統(tǒng)藍(lán)牙相關(guān)
…
- 系統(tǒng)藍(lán)牙相關(guān)
4.screen
用來表示瀏覽器窗口外部的顯示器的信息等
window.screen.deviceXDPI/deviceYDPI 屏幕實(shí)際的水平 DPI、垂直 DPI
三、瀏覽器事件
瀏覽器事件模型主要分為三個階段:
- 捕獲階段(IE)
- 事件由最外層元素(window),層層向內(nèi)傳遞,直到最具體的元素
- 目標(biāo)階段
- 冒泡階段(網(wǎng)景)
- 事件由最具體的元素(事件的觸發(fā)者),層層向外傳遞(傳遞給父節(jié)點(diǎn)),直到 window 對象停止
1.addEventListener 第三個參數(shù)
el.addEventListener(event, function, useCapture)
// useCapture默認(rèn)值false,也就是默認(rèn)冒泡
// true為捕獲階段
2.阻斷事件傳播
- event.stopPropagation()
阻斷
事件的傳播- 但是無法阻止默認(rèn)事件
- event.stopImmediatePropagation()
- 如果有多個
相同類型
的事件監(jiān)聽函數(shù)綁定到同一個
元素 - 當(dāng)該類型的事件觸發(fā)時,它們會按照被
添加的順序
執(zhí)行 - 如果其中某個監(jiān)聽函數(shù)執(zhí)行了此方法,則當(dāng)前元素
剩下
的監(jiān)聽函數(shù)將不會
被執(zhí)行
- 如果有多個
3.阻止默認(rèn)行為
- e.preventDefault()
- e.preventDefault()可以阻止事件的默認(rèn)行為發(fā)生
- 默認(rèn)行為是指:點(diǎn)擊 a 標(biāo)簽就轉(zhuǎn)跳到其他頁面、拖拽一個圖片到瀏覽器會自動打開、點(diǎn)擊表單的提交按鈕會提交表單等等,因?yàn)橛械臅r候我們并不希望發(fā)生這些事情,所以需要阻止默認(rèn)行為
拓展方向
性能方向
- 如:事件委托的運(yùn)用,一個 ul 和多個 li,點(diǎn)擊 li 時,改變背景顏色
{/*
<ul class="list"><li>1</li><li>2</li><li>3</li>
</ul>
*/
}
var list = document.querySelector('list');function onClick(e) {var e = e || window.event;if (e.target.tagName.toLowerCase() === 'li') {// 業(yè)務(wù)邏輯...e.target.style.backgroundColor = 'pink';}
}list.addEventListener('click', onClick, false);
兼容性方向
- 如:寫一個兼容 IE 的事件綁定
先區(qū)別 IE 的不同之處
- 綁定事件的函數(shù)和傳參不同: 使用
attachEvent
綁定,事件名要加on
- 解綁時使用的函數(shù)和參數(shù)不同: 使用
detachEvent
解綁 - 阻斷時的不同:
event.cancelBubble = true
- 阻止默認(rèn)行為的不同:
event.returnValue = false
class BindEvent {constructor(el) {this.el = el;}addEventListener(type, handler) {if (this.el.addEventListener) {this.el.addEventListener(type, handler, false);} else if (this.el.attachEvent) {this.el.attachEvent('on' + type, handler);} else {this.el['on' + type] = handler;}}removeEventListener(type, handler) {if (this.el.removeEventListener) {this.el.removeEventListener(type, handler, false);} else if (this.el.detachEvent) {this.el.detachEvent('on' + type, handler);} else {this.el['on' + type] = null;}}static stopPropagation() {if (e.stopPropagation) {e.stopPropagation();} else {e.cancelBubble = true;}}static preventDefault() {if (e.preventDefault) {e.preventDefault();} else {e.returnValue = false;}}
}
四、網(wǎng)絡(luò)請求
1.XMLHttpRequest
- 屬性
- responseText(服務(wù)端響應(yīng)的文本數(shù)據(jù))
- responseXML(服務(wù)點(diǎn)響應(yīng)的 xml 或者 html 類型數(shù)據(jù))
- status(響應(yīng) HTTP 狀態(tài))
- statusText(響應(yīng) HTTP 狀態(tài)描述)
- readyState(發(fā)出請求的狀態(tài)碼)對應(yīng)
onreadystatechange
事件- 0:創(chuàng)建成功,但是沒有調(diào)用 open 方法
- 1:open 方法被調(diào)用
- 2:send 方法被調(diào)用
- 3:loading,下載中
- 4:下載操作完成
- timeout(超時時間,對應(yīng)超時事件
ontimeout
,ontimeout 事件被棄用) - upload(上傳進(jìn)度)
- 方法
- open() 初始化請求(method, url, async),async 表示是否異步操作,默認(rèn) true
- send() 發(fā)送請求數(shù)據(jù),get 請求時,send 可以不傳或者 null
- abort() 中止已經(jīng)發(fā)出的請求
- setRequestHeader() 設(shè)置請求頭信息
- getRequestHeader() 獲取指定響應(yīng)頭信息
- getAllResponseHeaders() 獲取所有響應(yīng)頭信息
封裝 XMLHttpRequest 請求
function ajax({ method = 'get', url = '', params = undefined }) {return new Promise((resolve, reject) => {let xhr = new XMLHttpRequest();if (method.toLowerCase() === 'get' && params !== undefined) {url = url + '?' + params;}xhr.open(method, url, true);xhr.onreadystatechange = function () {if (xhr.readyState === 4) {if (xhr.status === 200) {resolve(xhr.responseText);} else {reject(xhr.status);}}};if (method.toLocaleLowerCase() === 'get') {xhr.send();} else {xhr.setRequestHeader('Content-Type', 'application/x-www-form-urlencoded');xhr.send(params);}xhr.onerror = (err) => reject(err);xhr.timeout = () => reject('timeout');// progress事件可以報(bào)告長時間運(yùn)行的文件上傳xhr.upload.onprogress = (p) => {console.log(Math.round((p.loaded / p.total) * 100) + '%');};});
}
2.fetch
- 方法:fetch(url,{}| init 對象 ),返回 Promise 對象,只支持異步
- 響應(yīng)通過 response 對象獲取:
fetch().then((response)=>{}).catch(()=>{})
- response 對象混入了 body,提供了 5 個方法,將 ReadableStream 轉(zhuǎn)存到緩沖區(qū)的內(nèi)存里,將緩沖區(qū)轉(zhuǎn)換為 js 對象,通過 Promise 返回
- response.text() //轉(zhuǎn)為 text
- response.json() //轉(zhuǎn)為 json
- response.formData()
- response.arrayBuffer()
- response.blob()
不支持超時設(shè)置
- 但是可以使用
promise+setTimeout
進(jìn)行控制
- 但是可以使用
- 默認(rèn)
不攜帶cookies
,但是可以設(shè)置fetch(url, {credentials: 'include'});
- omit 不發(fā)送 cookie
- same-origin 同源發(fā)送 cookie(默認(rèn))
- include 都發(fā)送 cookie
- ?resolve 若發(fā)生在網(wǎng)絡(luò)通信正常(404,500)也是 resolve
- .catch()也不會被執(zhí)行。
- ?reject 只發(fā)生在網(wǎng)絡(luò)通信異常
- 想要精確的判斷 fetch 是否成功
- 需要包含 promise resolved 的情況,此時再判斷 response.ok 是不是為 true
- 需要借用 AbortController 中止 fetch
// ?resolve若發(fā)生在網(wǎng)絡(luò)通信正常(404,500)也是resolve
fetch('http://domain/service', {method: 'GET',
}).then((response) => {// 想要精確的判斷 fetch是否成功// 需要包含 promise resolved 的情況,此時再判斷 response.ok是不是為 trueif (response.ok) {return response.json();}throw new Error('Network response was not ok.');}).then((json) => console.log(json))// .catch()也不會被執(zhí)行.catch((error) => console.error('error:', error));// ************************************
// 不支持直接設(shè)置超時, 可以用promise
function fetchTimeout(url, init, timeout = 3000) {return new Promise((resolve, reject) => {fetch(url, init).then(resolve).catch(reject);setTimeout(reject, timeout);});
}
// ************************************
// 中止fetch
// signal用于支持AbortController中斷請求
const controller = new AbortController();
// AbortController接口表示一個控制器對象
// 允許你根據(jù)需要中止一個或多個 Web請求
fetch('http://domain/service', {method: 'GET',signal: controller.signal,
}).then((response) => response.json()).then((json) => console.log(json)).catch((error) => console.error('Error:', error));
controller.abort();
3.Http 狀態(tài)碼和 Header
1.狀態(tài)碼
100 信息性|200 成功|300 重定向|400 客戶端錯誤|500 服務(wù)器錯誤
[1|2|3]xx
- 101 切換協(xié)議
- 200 Ok
- 301 永久重定向(設(shè)備會緩存)
- 302 臨時重定向(以前是臨時轉(zhuǎn)移,已經(jīng)不推薦使用了,建議使用 307)
- 307 臨時重定向
- 如果是 POST/DELETE 過來的會繼續(xù)使用
- 302 只會使用 get
- 304 (Not Modified)服務(wù)器文件未修改,協(xié)商緩存
- 308 永久重定向
4xx
- 400 客戶端請求有語法錯誤,不能被服務(wù)器識別
- 403 服務(wù)器受到請求,但是拒絕提供服務(wù),可能是跨域也可是權(quán)限不夠
- 404 請求的資源不存在
- 405 請求的 method 不允許
5xx
- 500 服務(wù)器發(fā)生不可預(yù)期的錯誤
2.字段
- Content-Length:? 發(fā)送給接收者給 Body 內(nèi)容長度(字節(jié))
- ? 一個 Byte 是 8bit
- utf-8 編碼的字符是 1-4 個字節(jié)
- ?User-Agent?:客戶端特性的字符串
- ?Content-Type:? 媒體類型
- ?Origin?:描述請求來源地址
- ?Accept
- 建議服務(wù)端返回何種媒體類型
- ?*/*表示所有類型(默認(rèn))
- ?text/html,application/json?
- 建議服務(wù)端返回何種媒體類型
- Accept-Encoding
- ? 建議服務(wù)端發(fā)送什么編碼(壓縮算法)
- ?deflate,gzip;q=1.0,*;q=0.5
- ?Accept-Language:? 建議服務(wù)端傳遞那種語言
- ?Referer
- 告訴服務(wù)端打開當(dāng)前頁面的上一張頁面的 URL;
- 如果是 ajax 請求那么就告訴服務(wù)端發(fā)送請求的 URL 的什么 ?
- 非瀏覽器環(huán)境有時候不發(fā)生 Referer(或者虛擬 Referer,通常是爬蟲)
- 常用來做用戶行為分析
- ?Connection?
- 決定鏈接是否在當(dāng)前事務(wù)完成后關(guān)閉
- ?http1.0 默認(rèn)是 close
- ?http1.1 默認(rèn)是 keep-alive
4.拓展方向
如何應(yīng)對網(wǎng)絡(luò)不穩(wěn)定(波動)的情況?
- 失敗了重發(fā),指數(shù)補(bǔ)償
const request = (url) => {let resolved = false;let t = 1;return new Promise((resolve, reject) => {// Promise.race([// fetch(url),// wait(100, () => fetch(url)),// wait(200, () => fetch(url)),// wait(400, () => fetch(url)),// wait(800, () => fetch(url)),// wait(1600, () => fetch(url)),// ])function doFetch() {if (resolved || t > 16) return;fetch(url).then((resp) => resp.text()).then((data) => {if (!resolved) {resolved = true;resolve(data);}});setTimeout(() => {doFetch();t *= 2;}, t * 100);}doFetch();});
};
如何處理并發(fā)請求?
- 若
相同
的請求,多次
發(fā)送,會照成帶寬
的浪費(fèi) - 處理思路:做
緩存
- 因?yàn)檎埱缶哂?code>時效性,所以
不能做永久緩存
- 相同的請求在
一定的時間
內(nèi)只發(fā)生一次
- 使用
w
保存請求信息,若同一時間發(fā)送多次相同對請求 - 使用
w[hash].resolves
保存 resolve 函數(shù)- 當(dāng)請求函數(shù)
w[hash].func
發(fā)送成功,執(zhí)行 resolves 中的函數(shù),保證每次請求都有響應(yīng)信息
- 當(dāng)請求函數(shù)
- 使用
import fetch from 'node-fetch';
function hash(...args) {return args.join(',');
}
function window_it(fn, time = 50) {let w = {}; // 時間窗口let flag = false;return (...args) => {return new Promise((resolve, reject) => {if (!w[hash(args)]) {w[hash(args)] = {resolves: [],func: fn,args,};}if (!flag) {flag = true;setTimeout(() => {Object.keys(w).forEach((key) => {console.log(`run ${key}`);const { func, args, resolves } = w[key];func(...args).then((res) => res.text()).then((data) => {resolves.forEach((r) => {console.log(`resolve ${key}`);r(data);});flag = false;delete w[key];});});}, time);}w[hash(args)].resolves.push(resolve);});};
}const request = window_it(fetch, 20);
request('http://www.baidu.com').then((txt) => console.log(txt.length));
request('http://www.baidu.com').then((txt) => console.log(txt.length));
request('http://www.baidu.com').then((txt) => console.log(txt.length));request('http://www.zhihu.com').then((txt) => console.log(txt.length));
request('http://www.zhihu.com').then((txt) => console.log(txt.length));
request('http://www.zhihu.com').then((txt) => console.log(txt.length));
往期精彩文章
- leetcode-js刷題記錄&數(shù)據(jù)結(jié)構(gòu)
- docker下YApi部署教程-支持swagger數(shù)據(jù)導(dǎo)入
- 帶你深入理解什么叫js閉包
- 使用Object.defineProperty進(jìn)行數(shù)據(jù)劫持,實(shí)現(xiàn)響應(yīng)式原理-剖析vue2.0
- 前端性能優(yōu)化之rel=“prefetch“預(yù)/懶加載功能
- 前端喚起相機(jī)的方法H5+JS