1. <strong id="7actg"></strong>
    2. <table id="7actg"></table>

    3. <address id="7actg"></address>
      <address id="7actg"></address>
      1. <object id="7actg"><tt id="7actg"></tt></object>

        「查缺補漏」高頻考點瀏覽器面試題

        共 26765字,需瀏覽 54分鐘

         ·

        2020-08-15 21:01

        前言

        想要成為一名合格的前端工程師,掌握相關(guān)瀏覽器的工作原理是必備的,這樣子才會有一個完整知識體系,要是「能參透瀏覽器的工作原理,你就能解決80%的前端難題」。

        這篇梳理的話,更多的是對瀏覽器工作原理篇的查缺補漏,對于一些沒有涉及到的知識點,準(zhǔn)備梳理梳理,也正好回顧之前梳理的內(nèi)容。

        感謝掘友的鼓勵與支持???,往期文章都在最后梳理出來了(●'?'●)


        「接下來以問題形式展開梳理」

        1. 常見的瀏覽器內(nèi)核有哪些?

        瀏覽器/RunTime內(nèi)核(渲染引擎)JavaScript 引擎
        Chromewebkit->blinkV8
        FireFoxGeckoSpiderMonkey
        SafariWebkitJavaScriptCore
        EdgeEdgeHTMLChakra(for JavaScript)
        IETridentJScript(IE3.0-IE8.0)
        OperaPresto->blinkLinear A(4.0-6.1)/ Linear B(7.0-9.2)/ Futhark(9.5-10.2)/ Carakan(10.5-)
        Node.js-V8

        2. 瀏覽器的主要組成部分是什么?

        1. 「用戶界面」 - 包括地址欄、前進(jìn)/后退按鈕、書簽菜單等。
        2. 「瀏覽器引擎」 - 在用戶界面和呈現(xiàn)引擎之間傳送指令。
        3. 「呈現(xiàn)引擎」 - 負(fù)責(zé)顯示請求的內(nèi)容。如果請求的內(nèi)容是 HTML,它就負(fù)責(zé)解析 HTML 和 CSS 內(nèi)容,并將解析后的內(nèi)容顯示在屏幕上。
        4. 「網(wǎng)絡(luò)」 - 用于網(wǎng)絡(luò)調(diào)用,比如 HTTP 請求。
        5. 「用戶界面后端」 -用于繪制基本的窗口小部件,比如組合框和窗口。
        6. 「JavaScript 解釋器」- 用于解析和執(zhí)行 JavaScript 代碼。
        7. 「數(shù)據(jù)存儲」 - 這是持久層。瀏覽器需要在硬盤上保存各種數(shù)據(jù),例如 Cookie。新的 HTML 規(guī)范 (HTML5) 定義了“網(wǎng)絡(luò)數(shù)據(jù)庫”,這是一個完整(但是輕便)的瀏覽器內(nèi)數(shù)據(jù)庫。

        值得注意的是,和大多數(shù)瀏覽器不同,Chrome 瀏覽器的每個標(biāo)簽頁都分別對應(yīng)一個呈現(xiàn)引擎實例。每個標(biāo)簽頁都是一個獨立的進(jìn)程。


        3. 為什么JavaScript是單線程的,與異步?jīng)_突嗎

        補充:JS中其實是沒有線程概念的,所謂的單線程也只是相對于多線程而言。JS的設(shè)計初衷就沒有考慮這些,針對JS這種不具備并行任務(wù)處理的特性,我們稱之為“單線程”。

        JS的單線程是指一個瀏覽器進(jìn)程中只有一個JS的執(zhí)行線程,同一時刻內(nèi)只會有一段代碼在執(zhí)行。

        舉個通俗例子,假設(shè)JS支持多線程操作的話,JS可以操作DOM,那么一個線程在刪除DOM,另外一個線程就在獲取DOM數(shù)據(jù),這樣子明顯不合理,這算是證明之一。

        來看段代碼?

        function foo() {    console.log("first");    setTimeout(( function(){        console.log( 'second' );    }),5);} for (var i = 0; i < 1000000; i++) {    foo();}

        打印結(jié)果就是首先是很多個first,然后再是second。

        異步機(jī)制是瀏覽器的兩個或以上常駐線程共同完成的,舉個例子,比如異步請求由兩個常駐線程,JS執(zhí)行線程和事件觸發(fā)線程共同完成的。

        • JS執(zhí)行線程發(fā)起異步請求(瀏覽器會開啟一個HTTP請求線程來執(zhí)行請求,這時JS的任務(wù)完成,繼續(xù)執(zhí)行線程隊列中剩下任務(wù))
        • 然后在未來的某一時刻事件觸發(fā)線程監(jiān)視到之前的發(fā)起的HTTP請求已完成,它就會把完成事件插入到JS執(zhí)行隊列的尾部等待JS處理

        再比如定時器觸發(fā)(settimeout和setinterval) 是由「瀏覽器的定時器線程」執(zhí)行的定時計數(shù),然后在定時時間把定時處理函數(shù)的執(zhí)行請求插入到JS執(zhí)行隊列的尾端(所以用這兩個函數(shù)的時候,實際的執(zhí)行時間是大于或等于指定時間的,不保證能準(zhǔn)確定時的)。

        所以這么說,JS單線程與異步更多是瀏覽器行為,之間不沖突。


        4. CSS加載會造成阻塞嗎

        先給出結(jié)論

        • CSS不會阻塞DOM解析,但會阻塞DOM渲染。
        • CSS會阻塞JS執(zhí)行,并不會阻塞JS文件下載

        先講一講CSSOM作用

        • 第一個是提供給 JavaScript 操作樣式表的能力
        • 第二個是為布局樹的合成提供基礎(chǔ)的樣式信息
        • 這個 CSSOM 體現(xiàn)在 DOM 中就是document.styleSheets。

        由之前講過的瀏覽器渲染流程我們可以看出:

        DOM 和 CSSOM通常是并行構(gòu)建的,所以「CSS 加載不會阻塞 DOM 的解析」

        然而由于Render Tree 是依賴DOM Tree和 CSSOM Tree的,所以它必須等到兩者都加載完畢后,完成相應(yīng)的構(gòu)建,才開始渲染,因此,「CSS加載會阻塞DOM渲染」。

        由于 JavaScript 是可操縱 DOM 和 css 樣式 的,如果在修改這些元素屬性同時渲染界面(即 JavaScript 線程和 UI 線程同時運行),那么渲染線程前后獲得的元素數(shù)據(jù)就可能不一致了。

        因此為了防止渲染出現(xiàn)不可預(yù)期的結(jié)果,瀏覽器設(shè)置 「GUI 渲染線程與 JavaScript 引擎為互斥」的關(guān)系。

        有個需要注意的點就是:

        「有時候JS需要等到CSS的下載,這是為什么呢?」

        仔細(xì)思考一下,其實這樣做是有道理的,如果腳本的內(nèi)容是獲取元素的樣式,寬高等CSS控制的屬性,瀏覽器是需要計算的,也就是依賴于CSS。瀏覽器也無法感知腳本內(nèi)容到底是什么,為避免樣式獲取,因而只好等前面所有的樣式下載完后,再執(zhí)行JS。

        JS文件下載和CSS文件下載是并行的,有時候CSS文件很大,所以JS需要等待。

        因此,樣式表會在后面的 js 執(zhí)行前先加載執(zhí)行完畢,所以「css 會阻塞后面 js 的執(zhí)行」。


        5. 為什么JS會阻塞頁面加載

        先給出結(jié)論?

        • 「JS阻塞DOM解析」,也就會阻塞頁面

        這也是為什么說JS文件放在最下面的原因,那為什么會阻塞DOM解析呢

        你可以這樣子理解:

        由于 JavaScript 是可操縱 DOM 的,如果在修改這些元素屬性同時渲染界面(即 JavaScript 線程和 UI 線程同時運行),那么渲染線程前后獲得的元素數(shù)據(jù)就可能不一致了。

        因此為了防止渲染出現(xiàn)不可預(yù)期的結(jié)果,瀏覽器設(shè)置 「GUI 渲染線程與 JavaScript 引擎為互斥」的關(guān)系。

        當(dāng) JavaScript 引擎執(zhí)行時 GUI 線程會被掛起,GUI 更新會被保存在一個隊列中等到引擎線程空閑時立即被執(zhí)行。

        當(dāng)瀏覽器在執(zhí)行 JavaScript 程序的時候,GUI 渲染線程會被保存在一個隊列中,直到 JS 程序執(zhí)行完成,才會接著執(zhí)行。

        因此如果 JS 執(zhí)行的時間過長,這樣就會造成頁面的渲染不連貫,導(dǎo)致頁面渲染加載阻塞的感覺。

        另外,如果 JavaScript 文件中沒有操作 DOM 相關(guān)代碼,就可以將該 JavaScript 腳本設(shè)置為異步加載,通過 async 或 defer 來標(biāo)記代碼


        6. defer 和 async 的區(qū)別 ?

        • 兩者都是異步去加載外部JS文件,不會阻塞DOM解析
        • Async是在外部JS加載完成后,瀏覽器空閑時,Load事件觸發(fā)前執(zhí)行,標(biāo)記為async的腳本并不保證按照指定他們的先后順序執(zhí)行,該屬性對于內(nèi)聯(lián)腳本無作用 (即沒有「src」屬性的腳本)。
        • defer是在JS加載完成后,整個文檔解析完成后,觸發(fā) DOMContentLoaded 事件前執(zhí)行,如果缺少 src 屬性(即內(nèi)嵌腳本),該屬性不應(yīng)被使用,因為這種情況下它不起作用

        7. DOMContentLoaded 與 load 的區(qū)別 ?

        • DOMContentLoaded事件觸發(fā)時:僅當(dāng)DOM解析完成后,不包括樣式表,圖片等資源。
        • onload 事件觸發(fā)時,頁面上所有的 DOM,樣式表,腳本,圖片等資源已經(jīng)加載完畢。

        那么也就是先DOMContentLoaded -> load,那么在Jquery中,使用(document).load(callback)監(jiān)聽的就是load事件。

        那我們可以聊一聊它們與async和defer區(qū)別

        帶async的腳本一定會在load事件之前執(zhí)行,可能會在DOMContentLoaded之前或之后執(zhí)行。

        • 情況1:HTML 還沒有被解析完的時候,async腳本已經(jīng)加載完了,那么 HTML 停止解析,去執(zhí)行腳本,腳本執(zhí)行完畢后觸發(fā)DOMContentLoaded事件
        • 情況2:HTML 解析完了之后,async腳本才加載完,然后再執(zhí)行腳本,那么在HTML解析完畢、async腳本還沒加載完的時候就觸發(fā)DOMContentLoaded事件

        如果 script 標(biāo)簽中包含 defer,那么這一塊腳本將不會影響 HTML 文檔的解析,而是等到HTML 解析完成后才會執(zhí)行。而 DOMContentLoaded 只有在 defer 腳本執(zhí)行結(jié)束后才會被觸發(fā)。

        • 情況1:HTML還沒解析完成時,defer腳本已經(jīng)加載完畢,那么defer腳本將等待HTML解析完成后再執(zhí)行。defer腳本執(zhí)行完畢后觸發(fā)DOMContentLoaded事件
        • 情況2:HTML解析完成時,defer腳本還沒加載完畢,那么defer腳本繼續(xù)加載,加載完成后直接執(zhí)行,執(zhí)行完畢后觸發(fā)DOMContentLoaded事件

        8. 為什么CSS動畫比JavaScript高效

        我覺得這個題目說法上可能就是行不通,不能這么說,如果了解的話,都知道will-change只是一個優(yōu)化的手段,使用JS改變transform也可以享受這個屬性帶來的變化,所以這個說法上有點不妥。

        所以圍繞這個問題展開話,更應(yīng)該說建議推薦使用CSS動畫,至于為什么呢,涉及的知識點大概就是重排重繪,合成,這方面的點,我在瀏覽器渲染流程中也提及了。

        盡可能的避免重排和重繪,具體是哪些操作呢,如果非要去操作JS實現(xiàn)動畫的話,有哪些優(yōu)化的手段呢?

        比如?

        • 使用createDocumentFragment進(jìn)行批量的 DOM 操作
        • 對于 resize、scroll 等進(jìn)行防抖/節(jié)流處理。
        • rAF優(yōu)化等等

        剩下的東西就留給你們思考吧,希望我這是拋磚引玉吧(●'?'●)


        9. 能不能實現(xiàn)事件防抖和節(jié)流

        函數(shù)節(jié)流(throttle)

        節(jié)流的意思是讓函數(shù)有節(jié)制地執(zhí)行,而不是毫無節(jié)制的觸發(fā)一次就執(zhí)行一次。什么叫有節(jié)制呢?就是在一段時間內(nèi),只執(zhí)行一次。

        規(guī)定在一個單位時間內(nèi),只能觸發(fā)一次函數(shù)。如果這個單位時間內(nèi)觸發(fā)多次函數(shù),只有一次生效。

        抓取一個關(guān)鍵的點:就是執(zhí)行的時機(jī)。要做到控制執(zhí)行的時機(jī),我們可以通過「一個開關(guān)」,與定時器setTimeout結(jié)合完成。

        		function throttle(fn, delay) {            let flag = true,                timer = null;            return function (...args) {                let context = this;                if (!flag) return;                flag = false;                clearTimeout(timer)                timer = setTimeout(() => {                    fn.apply(context, args);                    flag = true;                }, delay);            };        };

        函數(shù)防抖(debounce)

        在事件被觸發(fā)n秒后再執(zhí)行回調(diào),如果在這n秒內(nèi)又被觸發(fā),則重新計時。

        核心思想:每次事件觸發(fā)都會刪除原有定時器,建立新的定時器。通俗意思就是反復(fù)觸發(fā)函數(shù),只認(rèn)最后一次,從最后一次開始計時。

        代碼:

        		function debounce(fn, delay) {            let timer = null            return function (...args) {                let context = this                if(timer)   clearTimeout(timer)                timer = setTimeout(function() {                    fn.apply(context, args)                },delay)            }        }

        如何使用 debounce 和 throttle 以及常見的坑

        自己造一個 debounce / throttle 的輪子看起來多么誘人,或者隨便找個博文復(fù)制過來。「我是建議直接使用 underscore 或 Lodash」 。如果僅需要 _.debounce_.throttle 方法,可以使用 Lodash 的自定義構(gòu)建工具,生成一個 2KB 的壓縮庫。使用以下的簡單命令即可:

        npm i -g lodash-clinpm i -g lodash-clilodash-cli include=debounce,throttle

        常見的坑是,不止一次地調(diào)用 _.debounce 方法:

        // 錯誤$(window).on('scroll', function() {   _.debounce(doSomething, 300); });// 正確$(window).on('scroll', _.debounce(doSomething, 200));

        debounce 方法保存到一個變量以后,就可以用它的私有方法 debounced_version.cancel(),lodash 和 underscore.js 都有效。

        let debounced_version = _.debounce(doSomething, 200);
        $(window).on('scroll', debounced_version);

        // 如果需要的話debounced_version.cancel();

        適合應(yīng)用場景

        防抖

        • search搜索,用戶不斷輸入值時,用防抖來節(jié)約Ajax請求,也就是輸入框事件。
        • window觸發(fā)resize時,不斷的調(diào)整瀏覽器窗口大小會不斷的觸發(fā)這個事件,用防抖來讓其只觸發(fā)一次

        節(jié)流

        • 鼠標(biāo)的點擊事件,比如mousedown只觸發(fā)一次
        • 監(jiān)聽滾動事件,比如是否滑到底部自動加載更多,用throttle判斷
        • 比如游戲中發(fā)射子彈的頻率(1秒發(fā)射一顆)

        10. 談一談你對requestAnimationFrame(rAF)理解

        正好跟節(jié)流有點關(guān)系,有點相似處,就準(zhǔn)備梳理一下這個知識點。

        「高性能動畫是什么,那它衡量的標(biāo)準(zhǔn)是什么呢?」

        動畫幀率可以作為衡量標(biāo)準(zhǔn),一般來說畫面在 60fps 的幀率下效果比較好。

        換算一下就是,每一幀要在 16.7ms (16.7 = 1000/60) 內(nèi)完成渲染。

        我們來看看MDN對它的解釋吧?

        window.requestAnimationFrame() 方法告訴瀏覽器您希望執(zhí)行動畫并請求瀏覽器在下一次重繪之前調(diào)用指定的函數(shù)來更新動畫。該方法使用一個回調(diào)函數(shù)作為參數(shù),這個回調(diào)函數(shù)會在瀏覽器重繪之前調(diào)用。-- MDN

        當(dāng)我們調(diào)用這個函數(shù)的時候,我們告訴它需要做兩件事:

        1. 我們需要新的一幀;
        2. 當(dāng)你渲染新的一幀時需要執(zhí)行我傳給你的回調(diào)函數(shù)

        rAF與 setTimeout 相比

        rAF(requestAnimationFrame) 最大的優(yōu)勢是「由系統(tǒng)來決定回調(diào)函數(shù)的執(zhí)行時機(jī)」

        具體一點講就是,系統(tǒng)每次繪制之前會主動調(diào)用 rAF 中的回調(diào)函數(shù),如果系統(tǒng)繪制率是 60Hz,那么回調(diào)函數(shù)就每16.7ms 被執(zhí)行一次,如果繪制頻率是75Hz,那么這個間隔時間就變成了 1000/75=13.3ms。

        換句話說就是,rAF 的執(zhí)行步伐跟著系統(tǒng)的繪制頻率走。它能保證回調(diào)函數(shù)在屏幕每一次的繪制間隔中只被執(zhí)行一次(上一個知識點剛剛梳理完「函數(shù)節(jié)流」),這樣就不會引起丟幀現(xiàn)象,也不會導(dǎo)致動畫出現(xiàn)卡頓的問題。

        另外它可以自動調(diào)節(jié)頻率。如果callback工作太多無法在一幀內(nèi)完成會自動降低為30fps。雖然降低了,但總比掉幀好。

        與setTimeout動畫對比的話,有以下幾點優(yōu)勢

        • 當(dāng)頁面隱藏或者最小化時,setTimeout仍然在后臺執(zhí)行動畫,此時頁面不可見或者是不可用狀態(tài),動畫刷新沒有意義,而言浪費CPU。
        • rAF不一樣,當(dāng)頁面處理未激活的狀態(tài)時,該頁面的屏幕繪制任務(wù)也會被系統(tǒng)暫停,因此跟著系統(tǒng)步伐走的rAF也會停止渲染,當(dāng)頁面被激活時,動畫就從上次停留的地方繼續(xù)執(zhí)行,有效節(jié)省了 CPU 開銷。

        什么時候調(diào)用呢

        規(guī)范中似乎是這么去定義的:

        • 在重新渲染前調(diào)用。
        • 很可能在宏任務(wù)之后不去調(diào)用

        這樣子分析的話,似乎很合理嘛,為什么要在重新渲染前去調(diào)用呢?因為rAF作為官方推薦的一種做流暢動畫所應(yīng)該使用的API,做動畫不可避免的去操作DOM,而如果是在渲染后去修改DOM的話,那就只能等到下一輪渲染機(jī)會的時候才能去繪制出來了,這樣子似乎不合理。

        rAF在瀏覽器決定渲染之前給你最后一個機(jī)會去改變 DOM 屬性,然后很快在接下來的繪制中幫你呈現(xiàn)出來,所以這是做流暢動畫的不二選擇。

        至于宏任務(wù),微任務(wù),這可以說起來就要展開篇幅了,暫時不在這里梳理了。

        rAF與節(jié)流相比

        _.throttle(dosomething, 16) 等價。它是高保真的,如果追求更好的精確度的話,可以用瀏覽器原生的 API 。

        可以使用 rAF API 替換 throttle 方法,考慮一下優(yōu)缺點:

        優(yōu)點

        • 動畫保持 60fps(每一幀 16 ms),瀏覽器內(nèi)部決定渲染的最佳時機(jī)
        • 簡潔標(biāo)準(zhǔn)的 API,后期維護(hù)成本低

        缺點

        • 動畫的開始/取消需要開發(fā)者自己控制,不像 ‘.debounce’ 或 ‘.throttle’由函數(shù)內(nèi)部處理。
        • 瀏覽器標(biāo)簽未激活時,一切都不會執(zhí)行。
        • 盡管所有的現(xiàn)代瀏覽器都支持 rAF ,IE9,Opera Mini 和 老的 Android 還是需要打補丁。
        • Node.js 不支持,無法在服務(wù)器端用于文件系統(tǒng)事件。

        根據(jù)經(jīng)驗,如果 JavaScript 方法需要繪制或者直接改變屬性,我會選擇 requestAnimationFrame,只要涉及到重新計算元素位置,就可以使用它。

        涉及到 AJAX 請求,添加/移除 class (可以觸發(fā) CSS 動畫),我會選擇 _.debounce 或者 _.throttle ,可以設(shè)置更低的執(zhí)行頻率(例子中的200ms 換成16ms)。


        11. 能不能實現(xiàn)圖片的懶加載

        頁可見區(qū)域?qū)挘篸ocument.body.clientWidth;網(wǎng)頁可見區(qū)域高:document.body.clientHeight;網(wǎng)頁可見區(qū)域?qū)挘篸ocument.body.offsetWidth (包括邊線的寬);網(wǎng)頁可見區(qū)域高:document.body.offsetHeight (包括邊線的寬);網(wǎng)頁正文全文寬:document.body.scrollWidth;網(wǎng)頁正文全文高:document.body.scrollHeight;網(wǎng)頁被卷去的高:document.body.scrollTop;網(wǎng)頁被卷去的左:document.body.scrollLeft;網(wǎng)頁正文部分上:window.screenTop;網(wǎng)頁正文部分左:window.screenLeft;屏幕分辨率的高:window.screen.height;屏幕分辨率的寬:window.screen.width;屏幕可用工作區(qū)高度:window.screen.availHeight;

        關(guān)于scrollTop,offsetTop,scrollLeft,offsetLeft用法介紹,點這里

        「原理思路」

        1. 拿到所以的圖片img dom
        2. 重點是第二步,判斷當(dāng)前圖片是否到了可視區(qū)范圍內(nèi)
        3. 到了可視區(qū)的高度以后,就將img的src屬性設(shè)置給src
        4. 綁定window的scroll事件

        當(dāng)然了,為了用戶的體驗更加,默認(rèn)的情況下,設(shè)置一個「占位圖」

        本次測試代碼

        CSS代碼?

        HTML?


        第一種方式

        「clientHeight-scrollTop-offsetTop」

        直接上我運行的代碼?

        let Img = document.getElementsByTagName("img"),            len = Img.length,            count = 0;         function lazyLoad () {            let viewH = document.body.clientHeight, //可見區(qū)域高度                scrollTop = document.body.scrollTop; //滾動條距離頂部高度            for(let i = count; i < len; i++) {                if(Img[i].offsetTop < scrollTop + viewH ){                    if(Img[i].getAttribute('src') === ''){                        Img[i].src = Img[i].getAttribute('src')                        count++;                    }                }            }        }        function throttle(fn, delay) {            let flag = true,                timer = null;            return function (...args) {                let context = this;                if (!flag) return;                flag = false;                clearTimeout(timer)                timer = setTimeout(() => {                    fn.apply(context, args);                    flag = true;                }, delay);            };        };        window.addEventListener('scroll', throttle(lazyLoad,1000))                lazyLoad();  // 首次加載

        第二種方式

        使用 element.getBoundingClientRect() API 直接得到 top 值。

        代碼?

        let Img = document.getElementsByTagName("img"),            len = Img.length,            count = 0;         function lazyLoad () {            let viewH = document.body.clientHeight, //可見區(qū)域高度                scrollTop = document.body.scrollTop; //滾動條距離頂部高度            for(let i = count; i < len; i++) {                if(Img[i].getBoundingClientRect().top < scrollTop + viewH ){                    if(Img[i].getAttribute('src') === ''){                        Img[i].src = Img[i].getAttribute('src')                        count++;                    }                }            }        }        function throttle(fn, delay) {            let flag = true,                timer = null;            return function (...args) {                let context = this;                if (!flag) return;                flag = false;                clearTimeout(timer)                timer = setTimeout(() => {                    fn.apply(context, args);                    flag = true;                }, delay);            };        };        window.addEventListener('scroll', throttle(lazyLoad,1000))
        lazyLoad(); // 首次加載

        好像也差不多,不知道是不是我寫的方式有問題(●'?'●),感覺差不多

        來看看效果吧,我給這個事件加了一個節(jié)流,這樣子操作看起來就更好了。

        圖片懶加載

        12. 說一說你對Cookie localStorage sessionStorage

        Cookie

        得扯一下HTTP是一個無狀態(tài)的協(xié)議,這里主要指的是HTTP1.x版本,簡單的可以理解為即使同一個客戶端連續(xù)兩次發(fā)送請求給服務(wù)器,服務(wù)器也無法識別這個同一個客戶端發(fā)的請求,導(dǎo)致的問題,比如現(xiàn)實生活中你加入一個商品到購物車,但是因為無法識別同一個客戶端,你刷新頁面的話就?

        為了解決 HTTP 無狀態(tài)導(dǎo)致的問題(HTTP1.x),后來出現(xiàn)了 Cookie。

        Cookie 的存在也不是為了解決通訊協(xié)議無狀態(tài)的問題,只是為了解決客戶端與服務(wù)端會話狀態(tài)的問題,這個狀態(tài)是指后端服務(wù)的狀態(tài)而非通訊協(xié)議的狀態(tài)。

        Cookie存放在本地的好處就在于即使你關(guān)閉了瀏覽器,Cookie 依然可以生效。

        Cookie設(shè)置

        怎么去設(shè)置呢?簡單來說就是?

        1. 客戶端發(fā)送 HTTP 請求到服務(wù)器
        2. 當(dāng)服務(wù)器收到 HTTP 請求時,在響應(yīng)頭里面添加一個 Set-Cookie 字段
        3. 瀏覽器收到響應(yīng)后保存下 Cookie
        4. 之后對該服務(wù)器每一次請求中都通過 Cookie 字段將 Cookie 信息發(fā)送給服務(wù)器。

        Cookie指令

        在下面這張圖里我們可以看到 Cookies 相關(guān)的一些屬性?

        這里主要說一些大家可能沒有注意的點:

        「Name/Value」

        用 JavaScript 操作 Cookie 的時候注意對 Value 進(jìn)行編碼處理。

        Expires/Max-Age

        Expires 用于設(shè)置 Cookie 的過期時間。比如:

        Set-Cookie: id=aad3fWa; Expires=Wed, 21 May 2020 07:28:00 GMT;
        • 當(dāng) Expires 屬性缺省時,表示是會話性 Cookie。
        • 像上圖 Expires 的值為 Session,表示的就是會話性 Cookie。
        • 會話性 Cookie 的時候,值保存在客戶端內(nèi)存中,并在用戶關(guān)閉瀏覽器時失效。
        • 需要注意的是,有些瀏覽器提供了會話恢復(fù)功能,關(guān)閉瀏覽器,會話期Cookie會保留下來。
        • 與會話性 Cookie 相對的是持久性 Cookie,持久性 Cookies 會保存在用戶的硬盤中,直至過期或者清除 Cookie。

        Max-Age 用于設(shè)置在 Cookie 失效之前需要經(jīng)過的秒數(shù)。比如:

        Set-Cookie: id=a3fWa; Max-Age=604800;

        假如 Expires 和 Max-Age 都存在,Max-Age 優(yōu)先級更高。

        Domain

        Domain 指定了 Cookie 可以送達(dá)的主機(jī)名。假如沒有指定,那么默認(rèn)值為當(dāng)前文檔訪問地址中的主機(jī)部分(但是不包含子域名)。

        在這里注意的是,不能跨域設(shè)置 Cookie

        Path

        Path 指定了一個 URL 路徑,這個路徑必須出現(xiàn)在要請求的資源的路徑中才可以發(fā)送 Cookie 首部。比如設(shè)置 Path=/docs/docs/Web/ 下的資源會帶 Cookie 首部,/test 則不會攜帶 Cookie 首部。

        「Domain 和 Path 標(biāo)識共同定義了 Cookie 的作用域:即 Cookie 應(yīng)該發(fā)送給哪些 URL。」

        Secure屬性

        標(biāo)記為 Secure 的 Cookie 只應(yīng)通過被HTTPS協(xié)議加密過的請求發(fā)送給服務(wù)端。使用 HTTPS 安全協(xié)議,可以保護(hù) Cookie 在瀏覽器和 Web 服務(wù)器間的傳輸過程中不被竊取和篡改。

        HTTPOnly

        設(shè)置 HTTPOnly 屬性可以防止客戶端腳本通過 document.cookie 等方式訪問 Cookie,有助于避免 XSS 攻擊。

        SameSite

        SameSite 屬性可以讓 Cookie 在跨站請求時不會被發(fā)送,從而可以阻止跨站請求偽造攻擊(CSRF)。

        后面講CSRF攻擊會將講到,這里過。

        這個屬性值修改有什么影響呢?

        從上圖可以看出,對大部分 web 應(yīng)用而言,Post 表單,iframe,AJAX,Image 這四種情況從以前的跨站會發(fā)送三方 Cookie,變成了不發(fā)送。

        Cookie 的作用

        Cookie 主要用于以下三個方面:

        1. 會話狀態(tài)管理(如用戶登錄狀態(tài)、購物車、游戲分?jǐn)?shù)或其它需要記錄的信息)
        2. 個性化設(shè)置(如用戶自定義設(shè)置、主題等)
        3. 瀏覽器行為跟蹤(如跟蹤分析用戶行為等)

        Cookie 的缺點

        從大小,安全,增加請求大小。

        • 容量缺陷。Cookie 的體積上限只有4KB,只能用來存儲少量的信息。
        • 降低性能,Cookie緊跟著域名,不管域名下的某個地址是否需要這個Cookie,請求都會帶上完整的Cookie,請求數(shù)量增加,會造成巨大的浪費。
        • 安全缺陷,Cookie是以純文本的形式在瀏覽器和服務(wù)器中傳遞,很容易被非法用戶獲取,當(dāng)HTTPOnly為false時,Cookie信息還可以直接通過JS腳本讀取。

        localStorage 和 ?sessionStorage

        在 web 本地存儲場景上,cookie 的使用受到種種限制,最關(guān)鍵的就是存儲容量太小和數(shù)據(jù)無法持久化存儲。

        在 HTML 5 的標(biāo)準(zhǔn)下,出現(xiàn)了 localStorage 和 sessionStorage 供我們使用。

        異同點

        分類生命周期存儲容量存儲位置
        cookie默認(rèn)保存在內(nèi)存中,隨瀏覽器關(guān)閉失效(如果設(shè)置過期時間,在到過期時間后失效)4KB保存在客戶端,每次請求時都會帶上
        localStorage理論上永久有效的,除非主動清除。4.98MB(不同瀏覽器情況不同,safari 2.49M)保存在客戶端,不與服務(wù)端交互。節(jié)省網(wǎng)絡(luò)流量
        sessionStorage僅在當(dāng)前網(wǎng)頁會話下有效,關(guān)閉頁面或瀏覽器后會被清除。4.98MB(部分瀏覽器沒有限制)同上

        操作方式

        接下來我們來具體看看如何來操作localStoragesessionStorage

        let obj = { name: "TianTianUp", age: 18 };localStorage.setItem("name", "TianTianUp"); localStorage.setItem("info", JSON.stringify(obj));

        接著進(jìn)入相同的域名時就能拿到相應(yīng)的值?

        let name = localStorage.getItem("name");let info = JSON.parse(localStorage.getItem("info"));

        從這里可以看出,localStorage其實存儲的都是字符串,如果是存儲對象需要調(diào)用JSONstringify方法,并且用JSON.parse來解析成對象。

        應(yīng)用場景

        • localStorage 適合持久化緩存數(shù)據(jù),比如頁面的默認(rèn)偏好配置,如官網(wǎng)的logo,存儲Base64格式的圖片資源等;
        • sessionStorage 適合一次性臨時數(shù)據(jù)保存,存儲本次瀏覽信息記錄,這樣子頁面關(guān)閉的話,就不需要這些記錄了,還有對表單信息進(jìn)行維護(hù),這樣子頁面刷新的話,也不會讓表單信息丟失。

        13. 聊一聊瀏覽器緩存

        瀏覽器緩存是性能優(yōu)化的一個重要手段,對于理解緩存機(jī)制而言也是很重要的,我們來梳理一下吧?

        強緩存

        強緩存兩個相關(guān)字段,「Expires」「Cache-Control」。

        「強緩存分為兩種情況,一種是發(fā)送HTTP請求,一種不需要發(fā)送。」

        首先檢查強緩存,這個階段**不需要發(fā)送HTTP請求。**通過查找不同的字段來進(jìn)行,不同的HTTP版本所以不同。

        • HTTP1.0版本,使用的是Expires,HTTP1.1使用的是Cache-Control

        Expires

        Expires即過期時間,時間是相對于服務(wù)器的時間而言的,存在于服務(wù)端返回的響應(yīng)頭中,在這個過期時間之前可以直接從緩存里面獲取數(shù)據(jù),無需再次請求。比如下面這樣:

        Expires:Mon, 29 Jun 2020 11:10:23 GMT

        表示該資源在2020年7月29日11:10:23過期,過期時就會重新向服務(wù)器發(fā)起請求。

        這個方式有一個問題:「服務(wù)器的時間和瀏覽器的時間可能并不一致」,所以HTTP1.1提出新的字段代替它。

        Cache-Control

        HTTP1.1版本中,使用的就是該字段,這個字段采用的時間是過期時長,對應(yīng)的是max-age。

        Cache-Control:max-age=6000

        上面代表該資源返回后6000秒,可以直接使用緩存。

        當(dāng)然了,它還有其他很多關(guān)鍵的指令,梳理了幾個重要的?

        注意點:

        • 當(dāng)Expires和Cache-Control同時存在時,優(yōu)先考慮Cache-Control。
        • 當(dāng)然了,當(dāng)緩存資源失效了,也就是沒有命中強緩存,接下來就進(jìn)入?yún)f(xié)商緩存?

        協(xié)商緩存

        強緩存失效后,瀏覽器在請求頭中攜帶響應(yīng)的緩存Tag來向服務(wù)器發(fā)送請求,服務(wù)器根據(jù)對應(yīng)的tag,來決定是否使用緩存。

        緩存分為兩種,「Last-Modified」「ETag」。兩者各有優(yōu)勢,并不存在誰對誰有絕對的優(yōu)勢,與上面所講的強緩存兩個Tag所不同。

        Last-Modified

        這個字段表示的是「最后修改時間」。在瀏覽器第一次給服務(wù)器發(fā)送請求后,服務(wù)器會在響應(yīng)頭中加上這個字段。

        瀏覽器接收到后,「如果再次請求」,會在請求頭中攜帶If-Modified-Since字段,這個字段的值也就是服務(wù)器傳來的最后修改時間。

        服務(wù)器拿到請求頭中的If-Modified-Since的字段后,其實會和這個服務(wù)器中該資源的最后修改時間對比:

        • 如果請求頭中的這個值小于最后修改時間,說明是時候更新了。返回新的資源,跟常規(guī)的HTTP請求響應(yīng)的流程一樣。
        • 否則返回304,告訴瀏覽器直接使用緩存。

        ETag

        ETag是服務(wù)器根據(jù)當(dāng)前文件的內(nèi)容,對文件生成唯一的標(biāo)識,比如MD5算法,只要里面的內(nèi)容有改動,這個值就會修改,服務(wù)器通過把響應(yīng)頭把該字段給瀏覽器。

        瀏覽器接受到ETag值,會在下次請求的時候,將這個值作為「If-None-Match」這個字段的內(nèi)容,發(fā)給服務(wù)器。

        服務(wù)器接收到「If-None-Match」后,會跟服務(wù)器上該資源的「ETag」進(jìn)行比對?

        • 如果兩者一樣的話,直接返回304,告訴瀏覽器直接使用緩存
        • 如果不一樣的話,說明內(nèi)容更新了,返回新的資源,跟常規(guī)的HTTP請求響應(yīng)的流程一樣

        兩者對比

        • 性能上,Last-Modified優(yōu)于ETagLast-Modified記錄的是時間點,而Etag需要根據(jù)文件的MD5算法生成對應(yīng)的hash值。
        • 精度上,ETag優(yōu)于Last-ModifiedETag按照內(nèi)容給資源帶上標(biāo)識,能準(zhǔn)確感知資源變化,Last-Modified在某些場景并不能準(zhǔn)確感知變化,比如?
          • 編輯了資源文件,但是文件內(nèi)容并沒有更改,這樣也會造成緩存失效。
          • Last-Modified 能夠感知的單位時間是秒,如果文件在 1 秒內(nèi)改變了多次,那么這時候的 Last-Modified 并沒有體現(xiàn)出修改了。

        最后,「如果兩種方式都支持的話,服務(wù)器會優(yōu)先考慮ETag

        緩存位置

        接下來我們考慮使用緩存的話,緩存的位置在哪里呢?

        瀏覽器緩存的位置的話,可以分為四種,優(yōu)先級從高到低排列分別?

        • Service Worker
        • Memory Cache
        • Disk Cache
        • Push Cache

        Service Worker

        這個應(yīng)用場景比如PWA,它借鑒了Web Worker思路,由于它脫離了瀏覽器的窗體,因此無法直接訪問DOM。它能完成的功能比如:離線緩存消息推送網(wǎng)絡(luò)代理,其中離線緩存就是「Service Worker Cache」。

        Memory Cache

        指的是內(nèi)存緩存,從效率上講它是最快的,從存活時間來講又是最短的,當(dāng)渲染進(jìn)程結(jié)束后,內(nèi)存緩存也就不存在了。

        Disk Cache

        存儲在磁盤中的緩存,從存取效率上講是比內(nèi)存緩存慢的,優(yōu)勢在于存儲容量和存儲時長。

        Disk Cache VS Memory Cache

        兩者對比,主要的策略?

        內(nèi)容使用率高的話,文件優(yōu)先進(jìn)入磁盤

        比較大的JS,CSS文件會直接放入磁盤,反之放入內(nèi)存。

        Push Cache

        推送緩存,這算是瀏覽器中最后一道防線吧,它是HTTP/2的內(nèi)容。具體我也不是很清楚,有興趣的可以去了解。

        總結(jié)

        • 首先檢查Cache-Control, 嘗鮮,看強緩存是否可用
        • 如果可用的話,直接使用
        • 否則進(jìn)入?yún)f(xié)商緩存,發(fā)送HTTP請求,服務(wù)器通過請求頭中的If-Modified-Since或者If-None-Match字段檢查資源是否更新
        • 資源更新,返回資源和200狀態(tài)碼。
        • 否則,返回304,直接告訴瀏覽器直接從緩存中去資源。

        14. 說一說從輸入URL到頁面呈現(xiàn)發(fā)生了什么?

        一旦問這個問題的話,我覺得肯定是一個非常深的問題了,無論從深度還是廣度上,要真的答好這個題目,或者梳理清楚的話,挺難的,畢竟一個非常綜合性的問題,我作為一個剛剛?cè)腴T的小白,只能梳理部分知識,更深的知識可以去看看參考鏈接。

        那么我們就開始吧,假設(shè)你輸入的內(nèi)容是?

        https://juejin.im/

        ???

        網(wǎng)絡(luò)請求

        1. 構(gòu)建請求

        首先,瀏覽器構(gòu)建「請求行」信息(如下所示),構(gòu)建好后,瀏覽器準(zhǔn)備發(fā)起網(wǎng)絡(luò)請求?

        GET / HTTP1.1GET是請求方法,路徑就是根路徑,HTTP協(xié)議版本1.1

        2. 查找緩存

        在真正發(fā)起網(wǎng)絡(luò)請求之前,瀏覽器會先在瀏覽器緩存中查詢是否有要請求的文件。

        先檢查強緩存,如果命中的話直接使用,否則進(jìn)入下一步,強緩存的知識點,上面?梳理過了。

        3. DNS解析

        輸入的域名的話,我們需要根據(jù)域名去獲取對應(yīng)的ip地址。這個過程需要依賴一個服務(wù)系統(tǒng),叫做是DNS域名解析, 從查找到獲取到具體IP的過程叫做是DNS解析

        關(guān)于DNS篇,可以看看阮一峰的網(wǎng)絡(luò)日志

        首先,瀏覽器提供了DNS數(shù)據(jù)緩存功能,如果一個域名已經(jīng)解析過了,那么就會把解析的結(jié)果緩存下來,下次查找的話,直接去緩存中找,不需要結(jié)果DNS解析。

        「解析過程總結(jié)如下」?

        1. 「首先查看是否有對應(yīng)的域名緩存,有的話直接用緩存的ip訪問」

            ipconfig /displaydns// 輸入這個命令就可以查看對應(yīng)的電腦中是否有緩存
        2. 「如果緩存中沒有,則去查找hosts文件」 一般在 c:\windows\system32\drivers\etc\hosts

        3. 如果hosts文件里沒找到想解析的域名,則將「域名發(fā)往自己配置的dns服務(wù)器」,也叫「本地dns服務(wù)器」。

            ipconfig/all通過這個命令可以查看自己的本地dns服務(wù)器
        4. 如果「本地dns服務(wù)器有相應(yīng)域名的記錄」,則返回記錄。

          1. 電腦的dns服務(wù)器一般是各大運營商如電信聯(lián)通提供的,或者像180.76.76.76,223.5.5.5,4個114等知名dns服務(wù)商提供的,本身緩存了大量的常見域名的ip,所以常見的網(wǎng)站,都是有記錄的。不需要找根服務(wù)器。

        5. 如果電腦自己的服務(wù)器沒有記錄,會去找根服務(wù)器。根服務(wù)器全球只要13臺,回去找其中之一,找了根服務(wù)器后,「根服務(wù)器會根據(jù)請求的域名,返回對應(yīng)的“頂級域名服務(wù)器”」,如:

          1. 如果請求的域名是http://xxx.com,則返回負(fù)責(zé)com域的服務(wù)器
          2. 如果是http://xxx.cn,則發(fā)給負(fù)責(zé)cn域的服務(wù)器
          3. 如果是http://xxx.ca,則發(fā)給負(fù)責(zé)ca域的服務(wù)器
        6. 「頂級域服務(wù)器收到請求,會返回二級域服務(wù)器的地址」

          1. 比如一個網(wǎng)址是www.xxx.edu.cn,則頂級域名服務(wù)器再轉(zhuǎn)發(fā)給負(fù)責(zé).edu.cn域的二級服務(wù)器
        7. 「以此類推,最終會發(fā)到負(fù)責(zé)鎖查詢域名的,最精確的那臺dns,可以得到查詢結(jié)果。」

        8. 最后一步,「本地dns服務(wù)器,把最終的解析結(jié)果,返回給客戶端,對客戶端來講,只是一去一回的事,客戶端并不知道本地dns服務(wù)器經(jīng)過了千山萬水?!?/strong>

        以上就是大概的過程了,有興趣的話,可以仔細(xì)去看看。

        建立TCP鏈接

        我們所了解的就是?Chrome 在同一個域名下要求同時最多只能有 6 個 TCP 連接,超過 6 個的話剩下的請求就得等待。

        那么我們假設(shè)不需要等待,我們進(jìn)入了TCP連接的建立階段。

        建立TCP連接經(jīng)歷下面三個階段:

        • 通過「三次握手」建立客戶端和服務(wù)器之間的連接。
        • 進(jìn)行數(shù)據(jù)傳輸。
        • 斷開連接的階段。數(shù)據(jù)傳輸完成,現(xiàn)在要斷開連接了,通過「四次揮手」來斷開連接。

        從上面看得出來,TCP 連接通過什么手段來保證數(shù)據(jù)傳輸?shù)目煽啃?,一?code style="overflow-wrap: break-word;padding: 2px 4px;border-radius: 4px;margin-right: 2px;margin-left: 2px;font-family: "Operator Mono", Consolas, Monaco, Menlo, monospace;word-break: break-all;color: rgb(145, 109, 213);font-weight: bolder;background-image: none;background-position: initial;background-size: initial;background-repeat: initial;background-attachment: initial;background-origin: initial;background-clip: initial;">三次握手確認(rèn)連接,二是數(shù)據(jù)包校驗保證數(shù)據(jù)到達(dá)接收方,三是通過四次揮手斷開連接。

        深入理解的話,可以看看對應(yīng)的文章,掘金上面很多文章都有深入了解,這里就不梳理了。

        發(fā)送HTTP請求

        TCP連接完成后,接下來就可以與服務(wù)器通信了,也就是我們經(jīng)常說的發(fā)送HTTP請求。

        發(fā)送HTTP請求的話,需要攜帶三樣?xùn)|西:「請求行」,「請求頭」,「請求體」

        我們看看大概是是什么樣子的吧?

        Accept: text/html,application/xhtml+xml,application/xml;q=0.9,image/webp,image/apng,*/*;q=0.8,application/signed-exchange;v=b3Accept-Encoding: gzip, deflate, brAccept-Language: zh-CN,zh;q=0.9Cache-Control: no-cacheConnection: keep-aliveCookie: /* 省略cookie信息 */Host: juejin.imPragma: no-cacheUpgrade-Insecure-Requests: 1User-Agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/84.0.4147.89 Safari/537.36

        最后就是請求體,請求體的話只有在POST請求場景下存在,常見的就是表單提交

        網(wǎng)絡(luò)響應(yīng)

        HTTP 請求到達(dá)服務(wù)器,服務(wù)器進(jìn)行對應(yīng)的處理。最后要把數(shù)據(jù)傳給瀏覽器,也就是通常我們說的返回網(wǎng)絡(luò)響應(yīng)。

        跟請求部分類似,網(wǎng)絡(luò)響應(yīng)具有三個部分:「響應(yīng)行」、「響應(yīng)頭」「響應(yīng)體」。

        響應(yīng)行類似下面這樣?

        HTTP/1.1 200 OK

        對應(yīng)的響應(yīng)頭數(shù)據(jù)是怎么樣的呢?我們來舉個例子看看?

        Access-Control-Max-Age: 86400Cache-control: privateConnection: closeContent-Encoding: gzipContent-Type: text/html;charset=utf-8Date: Wed, 22 Jul 2020 13:24:49 GMTVary: Accept-EncodingSet-Cookie: ab={}; path=/; expires=Thu, 22 Jul 2021 13:24:49 GMT; secure; httponlyTransfer-Encoding: chunked

        接下來,我們數(shù)據(jù)拿到了,你認(rèn)為就會斷開TCP連接嗎?

        這個的看響應(yīng)頭中的Connection字段。上面的字段值為close,那么就會斷開,一般情況下,HTTP1.1版本的話,通常請求頭會包含「Connection: Keep-Alive」表示建立了持久連接,這樣TCP連接會一直保持,之后請求統(tǒng)一站點的資源會復(fù)用這個連接。

        上面的情況就會斷開TCP連接,請求-響應(yīng)流程結(jié)束。

        到這里的話,網(wǎng)絡(luò)請求就告一段落了,接下來的內(nèi)容就是渲染流程了?

        渲染階段

        較為專業(yè)的術(shù)語總結(jié)為以下階段:

        1. 構(gòu)建DOM樹
        2. 樣式計算
        3. 布局階段
        4. 分層
        5. 繪制
        6. 分塊
        7. 光柵化
        8. 合成

        關(guān)于渲染流程的話,可以看我之前總結(jié)的一篇???

        [1.1W字]寫給女友的秘籍-瀏覽器工作原理(渲染流程)篇


        15. 談一談你對重排和重繪理解

        關(guān)于重排和重繪,可以上面的知識點去梳理,也就是渲染階段,里面也梳理了部分的點,(●'?'●)

        偷個懶,看下面的文章噢?

        [1.1W字]寫給女友的秘籍-瀏覽器工作原理(渲染流程)篇


        16. 談一談跨域,同源策略,以及跨域解決方案

        什么是跨域

        跨域,是指瀏覽器不能執(zhí)行其他網(wǎng)站的腳本。它是由瀏覽器的同源策略造成的,是瀏覽器對JavaScript實施的安全限制。

        同源策略

        同源策略是一個安全策略。所謂的同源,指的是協(xié)議,域名,端口相同。

        同源策略

        瀏覽器處于安全方面的考慮,只允許本域名下的接口交互,不同源的客戶端腳本,在沒有明確授權(quán)的情況下,不能讀寫對方的資源。

        限制了一下行為:

        • Cookie、LocalStorage 和 IndexDB 無法讀取
        • DOM 和 JS 對象無法獲取
        • Ajax請求發(fā)送不出去

        解決方案

        當(dāng)然了,我梳理了幾個我覺得工作中常用的,其他的自行去了解。

        jsonp跨域

        利用script標(biāo)簽沒有跨域限制的漏洞,網(wǎng)頁可以拿到從其他來源產(chǎn)生動態(tài)JSON數(shù)據(jù),當(dāng)然了JSONP請求一定要對方的服務(wù)器做支持才可以。

        「與AJAX對比」

        JSONP和AJAX相同,都是客戶端向服務(wù)器發(fā)送請求,從服務(wù)器獲取數(shù)據(jù)的方式。但是AJAX屬于同源策略,JSONP屬于非同源策略(跨域請求)

        「JSONP優(yōu)點」

        兼容性比較好,可用于解決主流瀏覽器的跨域數(shù)據(jù)訪問的問題。缺點就是僅支持get請求,具有局限性,不安全,可能會受到XSS攻擊。

        「思路?」

        • 創(chuàng)建script標(biāo)簽
        • 設(shè)置script標(biāo)簽的src屬性,以問號傳遞參數(shù),設(shè)置好回調(diào)函數(shù)callback名稱
        • 插入html文本中
        • 調(diào)用回調(diào)函數(shù),res參數(shù)就是獲取的數(shù)據(jù)
        let script = document.createElement('script');
        script.src = 'http://www.baidu.cn/login?username=TianTianUp&callback=callback';
        document.body.appendChild(script);
        function callback(res) { console.log(res); }

        當(dāng)然,jquery也支持jsonp的實現(xiàn)方式

        		$.ajax({            url: 'http://www.baidu.cn/login',            type: 'GET',            dataType: 'jsonp', //請求方式為jsonp            jsonpCallback: 'callback',            data: {                "username": "Nealyang"            }        })

        「JSONP優(yōu)點」

        • 它不像XMLHttpRequest對象實現(xiàn)的Ajax請求那樣受到同源策略的限制
        • 它的兼容性更好,在更加古老的瀏覽器中都可以運行,不需要XMLHttpRequest或ActiveX的支持
        • 并且在請求完畢后可以通過調(diào)用callback的方式回傳結(jié)果。

        「JSONP缺點」

        • 它只支持GET請求而不支持POST等其它類型的HTTP請求
        • 它只支持跨域HTTP請求這種情況,不能解決不同域的兩個頁面之間如何進(jìn)行JavaScript調(diào)用的問題

        跨域資源共享 CORS

        CORS(Cross-Origin Resource Sharing)跨域資源共享,定義了必須在訪問跨域資源時,瀏覽器與服務(wù)器應(yīng)該如何溝通。CORS背后的基本思想就是使用自定義的HTTP頭部讓瀏覽器與服務(wù)器進(jìn)行溝通,從而決定請求或響應(yīng)是應(yīng)該成功還是失敗。目前,所有瀏覽器都支持該功能,IE瀏覽器不能低于IE10。整個CORS通信過程,都是瀏覽器自動完成,不需要用戶參與。對于開發(fā)者來說,CORS通信與同源的AJAX通信沒有差別,代碼完全一樣。瀏覽器一旦發(fā)現(xiàn)AJAX請求跨源,就會自動添加一些附加的頭信息,有時還會多出一次附加的請求,但用戶不會有感覺。

        上面是引用,你要記住的關(guān)鍵點?

        「CORS 需要瀏覽器和后端同時支持。IE 8 和 9 需要通過 XDomainRequest 來實現(xiàn)」

        • 「瀏覽器會自動進(jìn)行 CORS 通信,實現(xiàn) CORS 通信的關(guān)鍵是后端。只要后端實現(xiàn)了 CORS,就實現(xiàn)了跨域。」
        • 服務(wù)端設(shè)置 Access-Control-Allow-Origin 就可以開啟 CORS。該屬性表示哪些域名可以訪問資源,如果設(shè)置通配符則表示所有網(wǎng)站都可以訪問資源。

        請求分為「簡單請求」「非簡單請求」,所以我們的了解這兩種情況。

        「簡單請求」

        滿足下面兩個條件,就屬于簡單請求?

        條件1:使用下列方法之一:

        • GET
        • HEAD
        • POST

        條件2:Content-Type 的值僅限于下列三者之一?

        • text/plain
        • multipart/form-data
        • application/x-www-form-urlencoded

        請求中的任意 XMLHttpRequestUpload 對象均沒有注冊任何事件監(jiān)聽器;

        XMLHttpRequestUpload 對象可以使用 XMLHttpRequest.upload 屬性訪問。

        「復(fù)雜請求」

        不符合以上條件的請求就肯定是復(fù)雜請求了。復(fù)雜請求的CORS請求,會在正式通信之前,增加一次HTTP查詢請求,稱為"預(yù)檢"請求,該請求是 option 方法的,通過該請求來知道服務(wù)端是否允許跨域請求。

        直接上一個例子吧? 看看一個完整的復(fù)雜請求吧,并且介紹一下CORS請求的字段。

        //server2.jslet express = require('express')let app = express()let whitList = ['http://localhost:3000'] //設(shè)置白名單app.use(function(req, res, next) {  let origin = req.headers.origin  if (whitList.includes(origin)) {    // 設(shè)置哪個源可以訪問我    res.setHeader('Access-Control-Allow-Origin', origin)    // 允許攜帶哪個頭訪問我    res.setHeader('Access-Control-Allow-Headers', 'name')    // 允許哪個方法訪問我    res.setHeader('Access-Control-Allow-Methods', 'PUT')    // 允許攜帶cookie    res.setHeader('Access-Control-Allow-Credentials', true)    // 預(yù)檢的存活時間    res.setHeader('Access-Control-Max-Age', 6)    // 允許返回的頭    res.setHeader('Access-Control-Expose-Headers', 'name')    if (req.method === 'OPTIONS') {      res.end() // OPTIONS請求不做任何處理    }  }  next()})app.put('/getData', function(req, res) {  console.log(req.headers)  res.setHeader('name', 'jw') //返回一個響應(yīng)頭,后臺需設(shè)置  res.end('我不愛你')})app.get('/getData', function(req, res) {  console.log(req.headers)  res.end('我不愛你')})app.use(express.static(__dirname))app.listen(4000)

        上述代碼由http://localhost:3000/index.htmlhttp://localhost:4000/跨域請求,正如我們上面所說的,后端是實現(xiàn) CORS 通信的關(guān)鍵。

        上述的例子,一定對你會有所幫助的,這塊代碼,是跟著浪里行舟代碼來的,參考處注明了出處。

        「與JSONP對比」

        • JSONP只能實現(xiàn)GET請求,而CORS支持所有類型的HTTP請求。
        • 使用CORS,開發(fā)者可以使用普通的XMLHttpRequest發(fā)起請求和獲得數(shù)據(jù),比起JSONP有更好的錯誤處理。
        • JSONP主要被老的瀏覽器支持,它們往往不支持CORS,而絕大多數(shù)現(xiàn)代瀏覽器都已經(jīng)支持了CORS)

        WebSocket協(xié)議跨域

        Websocket是HTML5的一個持久化的協(xié)議,它實現(xiàn)了瀏覽器與服務(wù)器的全雙工通信,同時也是跨域的一種解決方案。

        WebSocket和HTTP都是應(yīng)用層協(xié)議,都基于 TCP 協(xié)議。但是 「WebSocket 是一種雙向通信協(xié)議,在建立連接之后,WebSocket 的 server 與 client 都能主動向?qū)Ψ桨l(fā)送或接收數(shù)據(jù)」。同時,WebSocket 在建立連接時需要借助 HTTP 協(xié)議,連接建立好了之后 client 與 server 之間的雙向通信就與 HTTP 無關(guān)了。

        我們先來看個例子?

        本地文件socket.html向localhost:3000發(fā)生數(shù)據(jù)和接受數(shù)據(jù)?

        // socket.html

        后端部分?

        // server.jslet WebSocket = require('ws'); //記得安裝wslet wss = new WebSocket.Server({port:3000});wss.on('connection',function(ws) {  ws.on('message', function (data) {    console.log(data);    ws.send('我不愛你')  });})

        如果 你想去嘗試的話,建議可以去玩一玩Socket.io,

        • 這是因為原生WebSocket API使用起來不太方便,它很好地封裝了webSocket接口
        • 提供了更簡單、靈活的接口,也對不支持webSocket的瀏覽器提供了向下兼容。

        nginx代理跨域


        17. 談一談你對XSS攻擊理解

        什么是 XSS 攻擊

        XSS 全稱是 Cross Site Scripting ,為了與CSS區(qū)分開來,故簡稱 XSS,翻譯過來就是“跨站腳本”。

        XSS是指黑客往 HTML 文件中或者 DOM 中注入惡意腳本,從而在用戶瀏覽頁面時利用注入的惡意腳本對用戶實施攻擊的一種手段。

        最開始的時候,這種攻擊是通過跨域來實現(xiàn)的,所以叫“跨域腳本”。發(fā)展到現(xiàn)在,往HTML文件中中插入惡意代碼方式越來越多,所以是否跨域注入腳本已經(jīng)不是唯一的注入手段了,但是 XSS 這個名字卻一直保留至今。

        注入惡意腳本可以完成這些事情:

        1. 竊取Cookie
        2. 監(jiān)聽用戶行為,比如輸入賬號密碼后之間發(fā)給黑客服務(wù)器
        3. 在網(wǎng)頁中生成浮窗廣告
        4. 修改DOM偽造登入表單

        一般的情況下,XSS攻擊有三種實現(xiàn)方式

        • 存儲型 XSS 攻擊
        • 反射型 XSS 攻擊
        • 基于 DOM 的 XSS 攻擊

        存儲型 XSS 攻擊

        存儲型 XSS 攻擊

        從圖上看,存儲型 XSS 攻擊大致步驟如下:

        1. 首先黑客利用站點漏洞將一段惡意 JavaScript 代碼提交到網(wǎng)站的數(shù)據(jù)庫中;
        2. 然后用戶向網(wǎng)站請求包含了惡意 JavaScript 腳本的頁面;
        3. 當(dāng)用戶瀏覽該頁面的時候,惡意腳本就會將用戶的 Cookie 信息等數(shù)據(jù)上傳到服務(wù)器。

        比如常見的場景:

        在評論區(qū)提交一份腳本代碼,假設(shè)前后端沒有做好轉(zhuǎn)義工作,那內(nèi)容上傳到服務(wù)器,在頁面渲染的時候就會直接執(zhí)行,相當(dāng)于執(zhí)行一段未知的JS代碼。這就是存儲型 XSS 攻擊。

        反射型 XSS 攻擊

        反射型 XSS 攻擊指的就是惡意腳本作為「網(wǎng)絡(luò)請求的一部分」,隨后網(wǎng)站又把惡意的JavaScript腳本返回給用戶,當(dāng)惡意 JavaScript 腳本在用戶頁面中被執(zhí)行時,黑客就可以利用該腳本做一些惡意操作。

        舉個例子:

        http://TianTianUp.com?query=

        如上,服務(wù)器拿到后解析參數(shù)query,最后將內(nèi)容返回給瀏覽器,瀏覽器將這些內(nèi)容作為HTML的一部分解析,發(fā)現(xiàn)是Javascript腳本,直接執(zhí)行,這樣子被XSS攻擊了。

        這也就是反射型名字的由來,將惡意腳本作為參數(shù),通過網(wǎng)絡(luò)請求,最后經(jīng)過服務(wù)器,在反射到HTML文檔中,執(zhí)行解析。

        主要注意的就是,「服務(wù)器不會存儲這些惡意的腳本,這也算是和存儲型XSS攻擊的區(qū)別吧」。

        基于 DOM 的 XSS 攻擊

        基于 DOM 的 XSS 攻擊是不牽涉到頁面 Web 服務(wù)器的。具體來講,黑客通過各種手段將惡意腳本注入用戶的頁面中,在數(shù)據(jù)傳輸?shù)臅r候劫持網(wǎng)絡(luò)數(shù)據(jù)包

        常見的劫持手段有:

        • WIFI路由器劫持
        • 本地惡意軟件

        阻止 XSS 攻擊的策略

        以上講述的XSS攻擊原理,都有一個共同點:讓惡意腳本直接在瀏覽器執(zhí)行。

        針對三種不同形式的XSS攻擊,有以下三種解決辦法

        對輸入腳本進(jìn)行過濾或轉(zhuǎn)碼

        對用戶輸入的信息過濾或者是轉(zhuǎn)碼

        舉個例子?

        轉(zhuǎn)碼后?


        這樣的代碼在 html 解析的過程中是無法執(zhí)行的。

        當(dāng)然了對于

        訪問該頁面后,表單會自動提交,相當(dāng)于模擬用戶完成了一次POST操作。

        同樣也會攜帶相應(yīng)的用戶 cookie 信息,讓服務(wù)器誤以為是一個正常的用戶在操作,讓各種惡意的操作變?yōu)榭赡堋?/p>

        3. 引誘用戶點擊鏈接

        這種需要誘導(dǎo)用戶去點擊鏈接才會觸發(fā),這類的情況比如在論壇中發(fā)布照片,照片中嵌入了惡意鏈接,或者是以廣告的形式去誘導(dǎo),比如:

           重磅消息?。?!  

        點擊后,自動發(fā)送 get 請求,接下來和自動發(fā) GET 請求部分同理。

        以上三種情況,就是CSRF攻擊原理,跟XSS對比的話,CSRF攻擊并不需要將惡意代碼注入HTML中,而是跳轉(zhuǎn)新的頁面,利用「服務(wù)器的驗證漏洞」「用戶之前的登錄狀態(tài)」來模擬用戶進(jìn)行操作

        「防護(hù)策略」

        其實我們可以想到,黑客只能借助受害者的**cookie**騙取服務(wù)器的信任,但是黑客并不能憑借拿到「cookie」,也看不到 「cookie」的內(nèi)容。另外,對于服務(wù)器返回的結(jié)果,由于瀏覽器「同源策略」的限制,黑客也無法進(jìn)行解析。

        這就告訴我們,我們要保護(hù)的對象是那些可以直接產(chǎn)生數(shù)據(jù)改變的服務(wù),而對于讀取數(shù)據(jù)的服務(wù),則不需要進(jìn)行**CSRF**的保護(hù)。而保護(hù)的關(guān)鍵,是 「在請求中放入黑客所不能偽造的信息」

        用戶操作限制——驗證碼機(jī)制

        方法:添加驗證碼來識別是不是用戶主動去發(fā)起這個請求,由于一定強度的驗證碼機(jī)器無法識別,因此危險網(wǎng)站不能偽造一個完整的請求。

        1. 驗證來源站點

        在服務(wù)器端驗證請求來源的站點,由于大量的CSRF攻擊來自第三方站點,因此服務(wù)器跨域禁止來自第三方站點的請求,主要通過HTTP請求頭中的兩個Header

        這兩個Header在瀏覽器發(fā)起請求時,大多數(shù)情況會自動帶上,并且不能由前端自定義內(nèi)容。

        服務(wù)器可以通過解析這兩個Header中的域名,確定請求的來源域。

        其中,「Origin」只包含域名信息,而「Referer」包含了具體的 URL 路徑。

        在某些情況下,這兩者都是可以偽造的,通過AJax中自定義請求頭即可,安全性略差。

        2. 利用Cookie的SameSite屬性

        可以看看MDN對此的解釋

        SameSite可以設(shè)置為三個值,Strict、LaxNone。

        1. Strict模式下,瀏覽器完全禁止第三方請求攜帶Cookie。比如請求sanyuan.com網(wǎng)站只能在sanyuan.com域名當(dāng)中請求才能攜帶 Cookie,在其他網(wǎng)站請求都不能。
        2. Lax模式,就寬松一點了,但是只能在 get 方法提交表單況或者a 標(biāo)簽發(fā)送 get 請求的情況下可以攜帶 Cookie,其他情況均不能。
        3. 在None模式下,Cookie將在所有上下文中發(fā)送,即允許跨域發(fā)送。

        3. 「CSRF Token」

        前面講到CSRF的另一個特征是,攻擊者無法直接竊取到用戶的信息(Cookie,Header,網(wǎng)站內(nèi)容等),僅僅是冒用Cookie中的信息。

        那么我們可以使用Token,在不涉及XSS的前提下,一般黑客很難拿到Token。

        可以看看這篇文章,將了Token是怎么操作的?徹底理解cookie,session,token

        Token(令牌)做為Web領(lǐng)域驗證身份是一個不錯的選擇,當(dāng)然了,JWT有興趣的也可以去了解一下。

        Token步驟如下:

        「第一步:將CSRF Token輸出到頁面中」

        首先,用戶打開頁面的時候,服務(wù)器需要給這個用戶生成一個Token,該Token通過加密算法對數(shù)據(jù)進(jìn)行加密,一般Token都包括隨機(jī)字符串和時間戳的組合,顯然在提交時Token不能再放在Cookie中了(XSS可能會獲取Cookie),否則又會被攻擊者冒用。因此,為了安全起見Token最好還是存在服務(wù)器的Session中,之后在每次頁面加載時,使用JS遍歷整個DOM樹,對于DOM中所有的a和form標(biāo)簽后加入Token。這樣可以解決大部分的請求,但是對于在頁面加載之后動態(tài)生成的HTML代碼,這種方法就沒有作用,還需要程序員在編碼時手動添加Token。

        「第二步:頁面提交的請求攜帶這個Token」

        對于GET請求,Token將附在請求地址之后,這樣URL 就變成 http://url?csrftoken=tokenvalue。而對于 POST 請求來說,要在 form 的最后加上:這樣,就把Token以參數(shù)的形式加入請求了。

        「第三步:服務(wù)器驗證Token是否正確」

        當(dāng)用戶從客戶端得到了Token,再次提交給服務(wù)器的時候,服務(wù)器需要判斷Token的有效性,驗證過程是先解密Token,對比加密字符串以及時間戳,如果加密字符串一致且時間未過期,那么這個Token就是有效的。

        非常感興趣的,可以仔細(xì)去閱讀一下相關(guān)的文章,Token是如何加密的,又是如何保證不被攻擊者獲取道。

        總結(jié)

        CSRF(Cross-site request forgery), 即跨站請求偽造,本質(zhì)是沖著瀏覽器分不清發(fā)起請求是不是真正的用戶本人,所以防范的關(guān)鍵在于在請求中放入黑客所不能偽造的信息。從而防止黑客偽造一個完整的請求欺騙服務(wù)器。

        「防范措施」:驗證碼機(jī)制,驗證來源站點,利用Cookie的SameSite屬性,CSRF Token

        參考


        最后



        如果你覺得這篇內(nèi)容對你挺有啟發(fā),我想邀請你幫我三個小忙:

        1. 點個「在看」,讓更多的人也能看到這篇內(nèi)容(喜歡不點在看,都是耍流氓 -_-)

        2. 歡迎加我微信「qianyu443033099」拉你進(jìn)技術(shù)群,長期交流學(xué)習(xí)...

        3. 關(guān)注公眾號「前端下午茶」,持續(xù)為你推送精選好文,也可以加我為好友,隨時聊騷。


        點個在看支持我吧,轉(zhuǎn)發(fā)就更好了



        瀏覽 75
        點贊
        評論
        收藏
        分享

        手機(jī)掃一掃分享

        分享
        舉報
        評論
        圖片
        表情
        推薦
        點贊
        評論
        收藏
        分享

        手機(jī)掃一掃分享

        分享
        舉報

        感谢您访问我们的网站,您可能还对以下资源感兴趣:

        国产秋霞理论久久久电影-婷婷色九月综合激情丁香-欧美在线观看乱妇视频-精品国avA久久久久久久-国产乱码精品一区二区三区亚洲人-欧美熟妇一区二区三区蜜桃视频 日日操夜夜爽| 久久无码黄片| 无码国精品一区二区免费蜜桃| 在线操逼视频| 欧美黄页| 亚洲中文字幕码mv| 日逼视频| 好看的中文字幕av| 欧美日韩亚洲成人| 在线观看成年人视频| 久热国产视频| 黑人Av在线| 久久精品女同亚洲女同13| 中文字幕+乱码+中文字幕电视剧| 成人毛片18女人毛片| 91无码一区二区三区在线| 一级内射片在线网站观看| 天堂网在线观看| 制服丝袜乱伦| 91精品人妻人人爽| 精品成人在线视频| 午夜无码精品一区二区三区99午| 2025最新国产精品每日更新 | 91黄色片| 性爱xxxxx| 黄色一级视频网站| 五月婷婷五月天| 五月婷婷性爱| 一本一本久久a久久精品牛牛影视| 国产一二三| 午夜福利100| 懂色av懂色av粉嫩av| 深爱五月激情网| 日韩人妻丰满无码区A片| 久久人妻无码中文字幕系列| 一区二区三区日本| 91九色在线观看| 俺来也俺去也www色| 色国产在线视频| 人人妻人人色| 啪啪人妻| 无码人妻A片一区二区青苹果| 欧美一级黄色电影| 亚洲无码一区二区三区蜜桃| 国产一区二区三区在线观看免费视频免费视频免费视频 | 亚洲色啪| 粉嫩小泬BBBBBB免费| 天堂在线视频| 免费看无码| 国产三级电影在线观看| 婷婷丁香人妻天天爽| 激情无码一区二区三区| 久草免费在线| 亚洲福利社| 91欧美精品| 国产成人AV免费观看| 国产AⅤ无码一区二区| 中文在线观看视频| 青青草婷婷| 91色色色色| 日韩熟妇无码中文字幕| 久久六月天| 91国产在线播放| 亚洲综合区| 国产三级精品三级在线观看| 臭小子啊轻点灬太粗太长了的视频 | 午夜免费视频1000| 中日韩中文字幕一区二区区别| 超小超嫩国产合集六部| 兔子先生和優奈玩游戲脫衣服,運氣報表優奈輸到脫精光 | 免费黄色在线视频| 日本无码视频在线| 中文在线字幕电视剧免费平台| 熟女视频一区二区| 久草黄色| 操逼视频免费观看| 亚洲.无码.制服.日韩.中文字幕| 欧美精产国品一二三区| 久久精品苍井空免费一区| 成人视频免费网站| 在线成人自拍| 亚洲一卡二卡| 国语精品自拍| 首页-91n| 美女特黄视频| 六月色| 中文无码一区二区三区四区| 久草福利在线观看| 岛国电影av| 中文字幕无码在线播放| 熟女资源网| 黄色成人网站在线免费观看| 日韩在线二区| 91你懂的| 操女人大逼| 蜜臀久久99精品久久久巴士| 黄色工厂这里只有精品| 无码东京热国产| 黑人巨粗进入疼哭A片| 欧美午夜乱伦电影| 天天色色色| 亚洲精品无码永久| 日韩成人精品视频| 亚洲中文AV在线| 嘿嘿午夜| 五月网婷婷| 欧美一区二区三区四区视频| 在线观看的AV| 91无码成人视频| 丰满熟妇人妻中文字幕| 3DAV一区二区三区动漫| V天堂在线| 亚洲美女网站免费观看网址| 蜜桃av秘无码一区三区四| 三级网址在线| 最新精品视频| 激情五月天丁香| 伊人啪啪| 午夜av无码| 在线观看亚洲一区| 老妇性BBWBBWBBWBBW| 欧美激情另类| 亚洲欧美成人片| 日韩精品一区二区三区使用方法| 3D动漫精品啪啪一区二区竹笋| 操你啦日韩| 在线中文字幕AV| 蜜桃在线无码| 日韩高清无码一区| 99热精品国产| 亚洲AV无码精品岛国| 亚洲h| 亚洲视频福利| 另类老妇性bbwbbwbbw| igao视频| 成年人视频网| 美女三片| 97人妻精品一区二区三区视频| 999在线视频| 99国产精品免费视频观看8| 日韩一区二区在线看在线看| 亚洲永久| 亚洲无码18禁| 动漫av网站| 91青青草| 国产色色色色| 亚洲白浆| 做爱网站在线观看| 婷婷中文字幕| 撸一撸AV| 国产91在线拍揄自揄拍无码九色 | 91夫妻交友视频| 国产夫妻精品| 国产乱论视频| 毛片黄片| 五月天成人社区| 一级Aa视频免费看| 九九成人精品| 兔子先生和優奈玩游戲脫衣服,運氣報表優奈輸到脫精光 | 国产精品一区二区AV日韩在线 | 无码伦理电影| 无码不卡在线观看| 成人性爱免费网站| 五月天久久久久久| 涩涩99| 天天日夜夜草| 青青久视频| 在线看片AV| 三级乱伦| 亚洲一区在线免费观看| 黄色福利视频在线观看| 国产精品视频你懂的| 无码视频观看| 东方av在线播放| 日韩免费在线观看| 日韩无码专区| 一区二区三区高清无码| 激情深爱五月| aaa在线观看| 国产理论视频在线观看| 中文字幕巨乱亚洲高清A片28| 夜夜嗨AV一区二区三区| 大鷄巴成人A片视频| 中文字幕丰满的翔田千里| 国产熟女一区二区三区五月婷| 人妻久久久| 99er在线| 国产18禁网站| 69亚洲视频| 黑人操逼| 嫩BBB搡BBB搡BBB搡| 成人做爰A片免费看网站| 日韩免费不卡| 五月激情久久| 欧美第一网站| 人妻精品一卡二卡| 麻豆免费成人传媒| 天天日天天射天天操| 五月婷婷色色色| 蜜桃91精品| 久久99久久99久久| 久久黄色视频网站| 日韩欧美爱爱| 午夜天堂精品久久| 亚洲无码视频看看| 日韩一级性爱视频| 久久久久亚洲AV无码专区成人| 亚洲区无码| 黄网站在线观看| 亚洲无码动漫| 五月激情六月丁香| 人人色视频| 五月天成人小说| 操逼视频网站免费| 欧美狠狠插| 午夜久操| gogogo高清在线观看免费直播中国 | 日本免费A片| 男人的天堂视频| 少妇人妻无码| 亚洲乱伦中文字幕| 黄色国产免费| 一本色道久久综合无码| 一级日逼视频| 成人精品免费无码毛片| 一本一道久久综合| 青娱乐一级无码| 黄色视频免费在线看| 在线观看免费黄片| 中文字幕综合| 三级网址在线观看| 熟女人妻人蜜桃视频| 国产AⅤ爽aV久久久久成人| av一区二区三区四区| 国产在线无码观看| 北条麻妃被躁57分钟视频在线| 亚洲中文字幕免费在线观看| 精品无码一区二区Av蜜桃 | 小小拗女BBw搡BBBB搡| 一级黄色影院| 在线看黄网| 波多野结衣高潮| 国产精品在线观看| 青青草免费在线视频| 亚洲三级网站| 波多野结衣无码高清| 免费亚洲视频| 精品无码产区一区二| 91亚洲视频| 三级网站在线| 久久丁香五月婷婷五月天激情视频| 男女日皮视频| 日韩欧美在线观看| 国产精品免费av在线| 色青娱乐| 就爱操逼网| 五月婷婷免费视频| 欧美性爱a视频| 91白浆肆意四溢456| 蜜桃影视| 一道本视频在线免费观看| 人人人人干| AV中文字幕电影| 香蕉污视频| 亚洲日韩在线看| 狠狠操婷婷| 欧美91视频| 国产女人18毛片水真多成人如厕 | 天天干天天操天天拍| 四虎激情影院| 成人黄网站免费观看| 久久免费成人电影| 91人妻一区二区三区| 无码高清18| 免费污视频在线观看| 日本黄色高清视频| 五月婷婷色色色| 婷婷天堂网| 丝袜乱伦| 男人天堂V| 国产激情av| 网络自拍亚洲激情| free性欧美| 蜜芽成人在线视频| 日本一级黄| 成人aV无码精品国产一区二区| 国产传媒_色哟哟| 中国黄色大片| 日韩无码黄片| 成人毛片18女人毛片| 中文字幕成人在线| 人妻少妇91精品一区黑人| 亚洲1234区| 色欲AV秘无码一区二区三区| 婷婷五月天性爱| 成人午夜黄片| 亚洲无码网| 丰满人妻一区二区三区| 人人爽亚洲AV人人爽AV人人片 | 一级片免费在线观看| 国产性爱一级片| 日韩wuma| 可以免费观看的毛片| 欧美成人性爱图片| 青草国产| 少妇人妻无码| 强开小嫩苞一区二区三区视频| 三级片在线看| 国产AV无遮挡| 免费爱爱视频网站| 99久久夜色精品国产亚洲| 国产不卡一| 亚洲高清无码在线视频| 无码一区在线观看| 久久91av| 免费三级怡红院| 成人网站三级片| 91乱子伦国产乱子伦海的味道 | 亚洲加勒比在线| GOGO人体做爰大胆视频| 国产香蕉视频免费| 天堂8在线视频| 中文字幕第一| 韩国成人啪啪无码高潮| 成人三级黄色| 亚洲日本三级| 久热福利视频| 青娱乐亚洲领先| 91黄色在线视频| 欧美成人网站免费在线观看| 久久久精品免费| 免费a在线观看| 蝌蚪窝在线免费观看视频| 亚洲色啪| 日本综合色| 国产精品你懂的| 69午夜| 欧美成人三级在线播放| 呦小BBBB小小BBBB| 99热这里有精品| 久久精品99国产国产精| 亚洲爆乳无码一区二区三区| 人人操操| 中日韩中文字幕一区二区区别| 草逼动态图| 91做爱| 亚洲色在线视频| 在线观看精品视频| 丁香五月大香蕉| 国产精品123区| 99re视频在线播放| 亚洲成人第一页| 久热伊人| 思思热免费视频| 乱伦一级| 亚洲美眉综合网| 在线观看AV资源| 波多野结衣国产| 人人摸人人摸| 国产精品成人在线视频| 丁香花小说完整视频免费观看| 无码人妻日本| 国内精品久久久久久久久98| 亚洲人妻少妇| 国产乱伦影片| 国产99久久九九精品无码免费| 国产黄网| 成人网站大香蕉| 成人毛片在线播放| 51XX嘿嘿午夜| 日韩图色| 天天射视频| 中文字幕在线播放第一页| 尤物精品| 免费观看久久久| 丁香激情五月少妇| 蜜臀久久99精品久久久晴天影视| 欧美一级片免费观看| 大伊人久久| 五月天久久久久久| 日本少妇做爱| 色眯眯久久爱| 青娱乐| 操碰视频| 婷婷久热| 99黄色视频| 最新激情网站| 三级片一区二区| 高清无码免费在线视频| 六月婷婷中文字幕| 91亚洲精品久久久久蜜桃| 亚洲欧洲日韩综合| 日本无码一区二区| 91热爆TS人妖系列| 天天撸视频| 国产又爽又黄免费观看| 日韩中文字幕AV| 91精品午夜少妇| 国产中文字幕在线视频| 色国产视频| 特级毛片片A片AAAAAA| 成人性生交大片免费看小芳| 婷婷久久亚洲| 中国人妻HDbute熟睡| 欧美三级片在线视频| 中国女人如毛片| 日本国产在线观看| 久久久久久亚洲精品| 亚洲调教| 天天干天天日蜜臀色欲av| Chinese搡老女人| 青青草婷婷| 337p西西人体大胆瓣开下部| 国产女同在线观看| 日本親子亂子倫XXXX50路| 欧美爱爱免费看| 欧美一区二区三曲的| 无码高清18| 亚洲在线视频播放| 91ncom| 亚洲特黄| 国产黄色小视频在线观看| 日本精品一区| 一级a免一级a做免费线看内裤的注意事项 | 91大神免费在线观看| 精品一区无码| 国产精品系列视频| 人人草在线视频| 欧美AAA| 人人草人人操| 伊人视频网| 五月综合激情| 国产精品精品精品| 亚洲AV无码一区二区三竹菊| 韩国精品在线观看| 四川妇搡BBBB搡BBBB| 在线观看视频91| 91嫖妓站街埯店老熟女| 丁香婷婷一区二区三区| 天天日日日干| 欧美操| 免费观看AV| 最新国产毛片| AV三级片在线观看| 无码人妻久久一区二区三区蜜桃 | 二区三区在线| 大香蕉偷拍视频| 天天操B| 伊人久久视频| 九九re| www.骚逼| 欧洲成人午夜精品无码区久久| av天天av无码av天天爽| 91精品视频在线播放| 人妻啪啪视频| 亚洲操逼无码| 国产高清一区二区三区| 国产AV无码专区| 亚洲精品一区二区三区蜜桃| 久操视频在线免费观看| 女生自慰网站免费| 尹人香蕉久久| 久久亚洲AV| 色福利视频| 18sav| 先锋影音av资源网| 日韩成人高清| 夜夜bb| 日韩精品成人| 国产—a毛—a毛A免费看图| 色哟哟网站| 中文字幕无码视频| 伊人视频在线观看| 欧美mv日韩mv国产| 日韩色小说| 天天综合91| 特级婬片A片AAA毛片AA做头| 毛片A片免费看| 91成人视频| 欧美精品无码| 毛茸茸BBBBBB毛茸茸| 日韩AV电影网| 欧美三级片在线| 开心深爱激情网| 欧美日韩日逼视频| 婷婷五月天在线电影| 天天综合91| av在线资源观看| 九九亚洲精品| 亚洲图片在线播放| 一区二区高清| 黄色成人网站在线播放| AV四虎| 北条麻妃无码av| 狼友在线视频| 国产又色又爽又黄又免费| 日韩久久中文字幕| 色欲一区二区三区| 国产Av婬乱麻豆| 久久久1| 国产精品操| 日本一级黄色A片| 亚洲无码一级电影| 女人久久| 日韩AV综合| 97久久一区二区| av亚洲波多野结衣白嫩水多波| 996热久久| 日本黄色A片| 亚洲精品乱码久久久久久蜜桃91| 日韩av免费在线| 国产伊人自拍| 久久久一区二区三区四曲免费听| 久久99深爱久久99精品| av在线直播| 中文字幕在线中文| 久久激情av| 中国免费毛片| 99精品一区| www.骚逼| 97久久久| 日韩成人精品视频| 超碰免费人妻| 粉嫩av在线| 日韩中文字幕专区| 黄片网址在线观看| 99热碰碰热| 免费一区二区三区| 波多野结衣无码视频在线观看| 黄色AV免费看| 亚洲AV小说| 草草视频在线观看| 黄色A片免费视频| AV资源免费| 日本在线视频不卡| 成人蜜臀AV| www.天天射视频| 免费爱爱视频| 精品三区| 大香蕉伊在线观看| 18一20女一片毛片| 一道本高清无码| 中文字幕成人视频| 日本高清视频免费观看| 中文字幕人妻在线中文乱码怎么解决 | 色婷婷综合网| 欧美丰满人妻免费视频人| 国产成人精品视频免费| 日本免费在线视频| 亚洲精品一区二区三区无码电影 | 人人妻人人澡人人爽久久| 在线免费观看黄片| 国产精品视频免费观看| 欧美日韩黄片| 99视频网站| 精品人妻一区二区三区浪潮在线| 91av免费观看| 91人妻人人澡人人爽人人| 成人无码视频在线观看| 国产又爽又黄免费网站在线看| jizzjizz欧美| 性欧美69| 视频在线观看一区| 久久久亚洲无码精品| 国产色综合视频| 欧美XXX黑人XYX性爽| 久操福利视频| 91人妻人人澡人人爽人人精| 十八禁无码网站在线观看| 91狠狠综| 欧美A黄| 久久丁香五月| 99热热久久| 丁香五月天网站| 东京热一区二区三区| 欧美日韩三区| 日韩免费高清视频| 成人免费a片| 久久久9999| av在线免费播放| 波多野结衣无码高清| 一区二区三区精品| a亚洲天堂| www99国产| 久久精品片| 小泽玛利亚一区二区免费| 五月综合久久| 亚洲视频综合| 亚洲一级黄| 成人无码在线播放| 中文字幕精品人妻在线| 操比一区| 午夜亚洲AV永久无码精品麻豆| 国产精品一区二区黑人巨大| 亚洲无码中文字幕视频| 久久97人妻AⅤ无码一区| 熟女人妻在线| 国产欧美在线视频| 日韩欧美内射| 在线观看的av| 欧美999| 少妇456| 精品人人操| 夜夜撸天天操| 久久精品在线播放| 骚熟妇| 国产欧美综合视频| 亚洲成人在线免费观看| 你懂的在线观看| 黄片网站免费| 日本一节片在线播放| 国产无码内射视频| 免费的黄色视频在线观看| 亚洲国产免费视频| 中文在线A∨在线| 中日韩特黄A片免费视频| 啪啪视频最新地址发布页| 尤物AV| 蜜臀AV在线| 蜜桃网站视频| 成人做爰100片免费视频| 日本一级婬片免费放| 岛国免费AV| 在线观看污视频| 爱精品视频| 日本色中文字幕| 亚洲综合激情网| 麻豆国产91在线播放| 亚洲无码精品在线| 人妻无码| 99在线观看视频| 成人福利免费视频| 在线亚洲AV| 91久久精品一区二区三| av免费在线播放| 亚洲无码视频在线观看| 大学生18一19GAY169| 麻豆精品国产传媒| 成人免费毛片蓝莓| 精品久久视频| 日韩无码91| 久久艹伊人| 2025国产成人精品一区| 日韩中文字幕av| 中文字幕网在线| 亚洲天堂在线免费| 久久XX| 中文字幕+乱码+中文乱码91| 亚洲不卡免费视频| 亚洲AV成人电影| 波多野结衣国产| 少妇一区二区三区| 1024手机在线视频| 免费久草视频| 中文字幕麻豆| 亚洲AV无码第一区二区三区蜜桃| 黄色片视频日韩| 亚洲视频一区二区三区四区娇小视频在线观看视频 | 国产精品久久久久永久免费看| 久久婷婷久久| 国产丰满乱子伦无码| 亚洲精品一二三区| 久久永久免费精品人妻专区| 高清无码小视频| 国产AV资源网| 亚洲日韩精品中文字幕| 国产A毛片| 91超碰久久在线| 99久久综合| 午夜老湿机| 香蕉一区二区| 一级A片在线观看| 综合天堂| 国产3区| 内射在线播放| 精品成人在线观看| 豆花网| 国产欧美精品成人在线观看 | 精品国产污污免费网站入口| 久草网站| 成人精品一区二区无码| av资源站| 国产精品外围| 少妇喷水视频| 欧美性爱综合网| 日韩干网| 亚洲高清无码一区二区三区| 亚洲视频在线免费观看| 婷婷五月综合中文字幕| 亚洲精品乱码久久久久久蜜桃91| 激情五月天小说网| 国产成人免费在线视频| 欧美三P囗交做爰| 影音先锋久久久久AV综合网成人| 一区视频在线| 亚洲中文字幕在线观看视频网站| 日韩一区二区三区无码| 久久精品三级片| 成人操b视频| 91在线网址| 欧美激情内射| 国产69精品久久久久久| 色婷婷丁香| 99久久婷婷国产综合精品| 性爱国产| 少妇人妻一级A毛片| 黄色视频网站在线观看免费| 五月婷婷性爱| 欧美精品一级片| 久久av综合| 在线国产91| 亚洲天堂在线视频| 成人免费网站在线| 国产免费一区二区在线A片视频| 成人18视频| 日韩精品一区二区三区黄冈站长| 亚洲午夜福利电影| 97天天操| 影音先锋日韩| 日本一级片中文字幕| 国产aⅴ激情无码久久久无码| 欧美不卡在线视频| 中文字幕国产精品| 欧美性久久久久| 欧美激情在线观看| 一区二区三区国产视频| 北条麻妃在线视频聊天| 无码av亚洲一区二区毛片公司| 国产成人午夜精品无码区久久麻豆 | 中字一区人妻水多多| 欧美深夜福利| 精品无码久久久久久久久app| 婷婷色在线观看| 色老板在线视频| 成人在线不卡| 日本在线不卡一区| 亚洲熟妇无码| 国产综合色网| 超碰99在线观看| 51伦理| 亚洲精品午夜福利| 老熟女伦一区二区三区| 操东北女人| 欧美日韩色图| 亚洲va综合va国产va中文| 蜜芽成人网| 911国产在线| A级成人网站| 99爱在线| 黄片av| 成人久久久久一级大黄毛片中国| 91人妻无码一区二区久久| 欧美又大又粗| 黄网站欧美内射| 国产suv精品一区二区| 天天综合天天| 欧洲黑种人日P视频| 国产麻豆精品成人毛片| 开心色播五月天| 一级片在线免费观看| 久久视频免费在线观看| 操逼视频免费看| 日韩欧美v| 伊人免费视频在线观看| 日本一区二区三区免费视频| 69人妻人人澡人人爽久久| 亚洲vs天堂vs成人vs无码| 日韩中文字幕人妻| 国产精品久久一区二区三区影音先锋 | 国产无码AV在线| 伊人精品大香蕉| 操逼视频在线播放| 伊人久久香蕉网| 亚洲国产女人| 国产在线观看av| 乱伦乱码| 一级国产黄色视频| 国产AV黄色| 成人一区视频| 欧美亚洲黄色| 天堂中文字幕在线| 亚洲成人精品一区二区| 亚洲avwww| 色99999| 污污污污污www网站免费观看| av无码导航| 婷婷久久五月| 美日韩视频| 成人做爰100片免费视频| 国产一级美女操逼视频免费播放| 噜噜噜在线| 成人无码精品亚洲| 日韩视频免费看| 国产毛片视频| 欧美成人精品在线| 人人操人人看人人| 国产中文字幕亚洲综合欧美| 天天操夜夜撸| 日韩色情在线| 2021狠狠操| 91人人澡| 国产成人综合网| A片免费网址| 国产伦精品一区二区三区色大师| 成人在线视频免费观看| 亚洲国产婷婷香蕉A片| 国产AV无码精品| 亚洲午夜福利一区二区三区| 日本黄色A片| 精品A区| 免费观看在线无码视频| 国精品无码一区二区三区在线| 久青草视频| 九九九在线| 日韩综合在线视频| 日韩无码A| 国产成人a亚洲精品www| 亚洲午夜福利在线观看| 免费看操片| 黃色一級片黃色一級片尖叫声-百度-百 | 亚洲精品成人7777777| 手机av在线观看| 久久久麻豆| 欧美人妻中文字幕| 婷婷五月在线观看| 蜜桃秘一二三区最新| 午夜福利免费在线观看| www污| 亚洲精品成a人在线观看| 亚洲系列| 97免费在线视频| 黄片视频在线| 亚洲va国产天堂va久久en| 五夜福利成人视频| 欧美激情久久久| 久久99久久99| 精品人妻一区二区| 久久精品色| 少妇中文字幕| 天天综合网久久综合网| 欧美色图在线观看| 亚洲无码十八禁| 亚洲精品国偷拍自产在线观看蜜桃| 国产h视频在线观看| 啪啪成人网| 九九九av| 亚洲熟妇无码| 麻豆一区| 欧美日韩人妻高清中文| 在线看91| 亚洲一级av| 亚洲内射网| 五月婷婷色播| 久久五月天婷婷| 国产成人a亚洲精品www| 色情片免费看| 蜜桃视频网站18| adn日韩av| 成人精品一区二区无码| 操东北女人逼| 中文无码在线观看| 色综合99久久久无码国产精品| 欧美黄片一区| 四虎精品一区二区三区| 国产性受XXXXXYX性爽| 翔田千里无码播放| 亚洲AV成人无码精在线| 欧美特黄AAA| 日韩视频中文| 江苏妇搡BBBB搡BBBB小说| 大香蕉伊人网在线| 一本无码中文字幕| 99精品视频在线播放免费| 亚洲综合五月天| 日韩中文字幕精品| 亚洲黄色精品| 中文无码高清在线| 五月天久久| 日日骚亚洲| 伊人网在线免费视频| 日韩在线第—页| 中文字幕不卡无码| 亚洲无码电影在线观看| 无码AV电影在线观看| 乱伦无码视频| 操逼视频免费看| 色婷婷色婷婷| 不卡视频在线观看| 中文字幕一区二区蜜桃| 日本黄色视频网| 高清视频一区二区| 大香蕉视频国产| A天堂视频| 久久精品福利视频| 看免费黄色录像| 操逼网国产| 五月婷婷六月丁香| 爱爱免费不卡视频| 色色9999| 精品视频久|