<kbd id="afajh"><form id="afajh"></form></kbd>
<strong id="afajh"><dl id="afajh"></dl></strong>
    <del id="afajh"><form id="afajh"></form></del>
        1. <th id="afajh"><progress id="afajh"></progress></th>
          <b id="afajh"><abbr id="afajh"></abbr></b>
          <th id="afajh"><progress id="afajh"></progress></th>

          現(xiàn)代瀏覽器內(nèi)部機(jī)制 Part 4 | 事件

          共 5007字,需瀏覽 11分鐘

           ·

          2021-03-19 17:21

          原文:Inside Look at Modern Web Browser(part 4)[1]

          作者:Mariko Kosaka[2]

          譯者:kyrieliu

          終于到最后一篇了!作為這個(gè)系列的最后一篇文章。在之前的文章中,我們了解了現(xiàn)在瀏覽器的多進(jìn)程架構(gòu)、導(dǎo)航以及渲染進(jìn)程和合成器。在這篇文章中,我們將了解到合成器是如何在用戶輸入時(shí)流暢的處理交互的。

          從瀏覽器的角度定義輸入事件

          當(dāng)提到“輸入事件”時(shí),你可能會想到在文本域中打字或是鼠標(biāo)的點(diǎn)擊事件,但在瀏覽器看來,用戶的任何動作都意味著“輸入”。鼠標(biāo)滾輪的滾動是一種輸入事件,觸摸或者鼠標(biāo)滑過也是一種輸入事件。

          當(dāng)用戶的交互行為發(fā)生時(shí)(比如觸摸點(diǎn)擊屏幕),瀏覽器進(jìn)程會第一個(gè)感知到這個(gè)用戶行為,但也僅僅是感知而已,因?yàn)闉g覽器 tab 下的內(nèi)容都是由渲染進(jìn)程全盤掌控著。于是瀏覽器進(jìn)程在第一時(shí)間將用戶事件的類型和坐標(biāo)發(fā)送給渲染進(jìn)程。渲染進(jìn)程通過查找并調(diào)用對應(yīng)的事件處理函數(shù)來處理這個(gè)用戶輸入事件。

          合成器接收到輸入事件

          在上一篇文章中,我們研究了合成器如何通過光柵化圖層來平滑的處理滾動。如果頁面上沒有事件監(jiān)聽器,合成器線程會創(chuàng)建一個(gè)完全獨(dú)立于主線程的新的合成幀。如果頁面上掛在了一些事件監(jiān)聽器又會發(fā)生什么呢?合成器線程又是怎樣找出需要被觸發(fā)的事件呢?

          非快速滾動區(qū)域

          因?yàn)檫\(yùn)行 JavaScript 是主線程的任務(wù),當(dāng)一個(gè)頁面被合成,合成器線程將頁面上掛在了事件處理器的區(qū)域標(biāo)記為“非快速滾動區(qū)域”。有了這個(gè)標(biāo)記之后,合成器就能保證在對應(yīng)的區(qū)域觸發(fā)輸入事件時(shí)可以向主線程傳遞這一事件。如果輸入事件來自于這個(gè)區(qū)域之外,合成器則會持續(xù)合成新的幀,并不會等待主線程。

          寫事件處理器時(shí)要注意

          在 Web 開發(fā)中一個(gè)比較常見的事件處理模型就是事件委托(代理)。因?yàn)槭录拿芭輽C(jī)制,開發(fā)者可以在最頂層的元素掛載一個(gè)事件處理函數(shù),并且基于 event target 分發(fā)不同的處理邏輯。下面的代碼,你可能已經(jīng)司空見慣了:

          document.body.addEventListener('touchstart', event => {
           if (event.target === area) {
              event.preventDefault();
            }
          });

          只用寫一個(gè)事件處理器就可以搞定所有的輸入事件,這在工程學(xué)上是一件極具魅力的事情。當(dāng)你從瀏覽器的視角審視這段代碼的時(shí)候,你會發(fā)現(xiàn)整個(gè)頁面都被標(biāo)記成了“非快速滾動區(qū)域”。這就意味著即使你的 web app 不關(guān)心來自頁面上某個(gè)位置的輸入事件,但合成器線程仍然會基于這次觸發(fā)的事件和主線程進(jìn)行“交流”。在這種模式之下,合成器本身“平滑處理頁面滾動”的能力就不復(fù)存在了。

          為了減輕這種情況的發(fā)生,開發(fā)者可以給自己的事件處理器傳遞 passive: true 這樣一個(gè)參。這等同于告訴瀏覽器開發(fā)者仍然希望在主線程中監(jiān)聽頁面上每一次觸發(fā)的輸入事件,但也希望合成器該干啥干啥,持續(xù)合成新的幀。

          document.body.addEventListener('touchstart', event => {
            if (event.target === area) {
              event.preventDefault();
            }
          }, { passivetrue });

          事件是否可以取消?

          假設(shè)此時(shí)頁面上有個(gè)容器,你只想讓它進(jìn)行水平滾動。

          在你的鼠標(biāo)事件監(jiān)聽函數(shù)中使用 passive:true 意味著頁面的滾動可以按照往常縱享絲滑般地去處理,你會為了限制滾動的方向調(diào)用 preventDefault ,但在這之前豎直的滾動就可能已經(jīng)發(fā)生了。你可以通過 event.cancelable 針對此種情況進(jìn)行相應(yīng)的優(yōu)化。

          document.body.addEventListener('pointermove', event => {
            if (event.cancelable) {
              event.preventDefault();
            }
          }, { passivetrue });

          同時(shí),你也可以用如下 css 來幫忙消除事件處理器:

          #area {
            touch-action: pan-x;
          }

          查找 event target

          當(dāng)合成器線程向主線程發(fā)送了一個(gè)輸入事件后,第一件事情就是通過 hit test(點(diǎn)擊測試) 找到對應(yīng)的 event target(事件目標(biāo),還是不翻譯這個(gè)詞比較正宗)。Hit test 利用渲染進(jìn)程產(chǎn)生的繪制記錄來找出在觸發(fā)本次輸入事件的坐標(biāo)底下的真實(shí)元素。

          減少主線程的事件處理負(fù)擔(dān)

          在上一篇文章中,我們討論了主流的顯示器通過每秒 60 次的頻率刷新以及我們需要跟上這個(gè)節(jié)奏以實(shí)現(xiàn)流暢的動畫效果。對于輸入事件來說,主流的觸摸屏?xí)悦棵?60 到 120 次的頻率向主線程傳遞觸摸事件,大多數(shù)的鼠標(biāo)事件都被以每秒 100 次的頻率傳遞給主線程。輸入事件的保真度是普遍高于主流屏幕的刷新能力的。

          如果一個(gè)持續(xù)不斷的事件(比如 touchmove)在一秒內(nèi)被傳遞給了主線程 120 次,這就會觸發(fā)大量的 hit test 和 JavaScript 的執(zhí)行,這么一對比,每秒 60 次的屏幕刷新速率就顯得太慢了。

          為了減少主線程的負(fù)擔(dān),Chrome 將常見的連續(xù)事件進(jìn)行了合并(比如 wheel、mousewheel、mousemove、pointermove、touchmove 等),并且在 requestAnimationFrame 中延緩了事件的觸發(fā)時(shí)機(jī)。

          其他“分散觸發(fā)”的事件(keydown、keyup、mouseup、touchstart、touchend 等)仍保持立即觸發(fā)的策略。

          通過 getCoalescedEvents 獲取幀內(nèi)事件

          對于大多數(shù)的 web app 來說,合成事件是為了更好的用戶體驗(yàn)。假如你在開發(fā)一款繪畫的應(yīng)用程序,如果你根據(jù) touchmove 的坐標(biāo)來放置路徑,大概率是會丟失掉中間的坐標(biāo)的,你也就無法畫一條平滑的線了。這種情況下,你就可以用 getCoalescedEvents 這個(gè)方法來獲取更多關(guān)于合成事件的信息。

          window.addEventListener('pointermove', event => {
            const evnets = event.getCoalescedEvents();
            for (let event of events) {
              const x = event.pageX;
              const y = event.pageY;
              // 用這些坐標(biāo)畫線,穩(wěn)
            }
          });

          接下來...

          在這個(gè)系列中,我們詳細(xì)的探討了現(xiàn)代瀏覽器的內(nèi)部工作機(jī)制。如果你之前從來沒有想過為什么官方推薦在你的事件處理函數(shù)中添加 passive 參數(shù),或者不知道為什么在 script 標(biāo)簽上添加 async 屬性,我希望這個(gè)系列能為你闡明為什么瀏覽器需要這些東西來提供更快、更流暢的用戶體驗(yàn)。

          Lighthouse 用起來

          如果你想讓自己的代碼變得更加“瀏覽器友好”卻不知道從哪里開始,不妨試試 Lighthouse[3] 吧。Lighthouse 是一個(gè)可以對網(wǎng)站進(jìn)行審核檢查的工具,會為開發(fā)者提供一份包含網(wǎng)站得分以及優(yōu)化方案的詳盡報(bào)告。

          學(xué)習(xí)如何度量性能

          不同的網(wǎng)站對于性能的需求可能不同,因此找到合適的度量方法以及優(yōu)化方案是至關(guān)重要的。Chrome 的開發(fā)者工具團(tuán)隊(duì)有話說:通過 Chrome Devtools 優(yōu)化網(wǎng)站性能[4]。

          給網(wǎng)站添加 Feature Policy

          如果你想更進(jìn)一步,F(xiàn)eature Policy 了解一下?Feature Policy 是一個(gè)新的 web 特性,它可以在開發(fā)者構(gòu)建 web app 時(shí)提供“保護(hù)”。啟用 feature policy 可以確保你的 web app 具備某些行為,并在一定程度上避免開發(fā)者犯錯(cuò)。舉個(gè)例子,如果你希望保證你的 app 不會阻塞解析,你可以在同步腳本策略之下運(yùn)行你的 app。當(dāng) sync-script:none 打開時(shí),會阻塞解析的 JavaScript 都會被阻止執(zhí)行。這一策略會防止任何“腳本阻塞解析”的發(fā)生,瀏覽器就再也不用擔(dān)心解析被阻塞這件事情了。

          總結(jié)

          當(dāng)我在構(gòu)建網(wǎng)站時(shí),我通常只關(guān)注怎么寫代碼以及怎樣才能讓自己的效率變得更高。這些事確實(shí)很重要,但我們也需要關(guān)注瀏覽器究竟會怎樣處理我們的代碼。現(xiàn)代瀏覽器在持續(xù)地為用戶提供更好的 Web 體驗(yàn)。通過組織我們的代碼對瀏覽器更加友好,也能改善用戶體驗(yàn),可謂一舉兩得一石二鳥一箭雙雕!

          參考資料

          [1]

          Inside Look at Modern Web Browser(part 4): https://developers.google.com/web/updates/2018/09/inside-browser-part4

          [2]

          Mariko Kosaka: https://developers.google.com/web/resources/contributors/kosamari

          [3]

          Lighthouse: https://developers.google.com/web/tools/lighthouse

          [4]

          通過 Chrome Devtools 優(yōu)化網(wǎng)站性能: https://developers.google.com/web/tools/chrome-devtools/speed/get-started




          本文來自劉凱里,專注分享有趣的前端知識。劉哥還為大家準(zhǔn)備了前端面試官手記,在他的公眾號回復(fù)【前端面試】即可獲取,歡迎大家的關(guān)注~


          往期推薦

          現(xiàn)代瀏覽器內(nèi)部機(jī)制 Part 1 | 多進(jìn)程架構(gòu)

          現(xiàn)代瀏覽器內(nèi)部機(jī)制 Part 2 | 導(dǎo)航這件小事

          現(xiàn)代瀏覽器內(nèi)部機(jī)制 Part 3 | 渲染進(jìn)程的一生

          瀏覽 65
          點(diǎn)贊
          評論
          收藏
          分享

          手機(jī)掃一掃分享

          分享
          舉報(bào)
          評論
          圖片
          表情
          推薦
          點(diǎn)贊
          評論
          收藏
          分享

          手機(jī)掃一掃分享

          分享
          舉報(bào)
          <kbd id="afajh"><form id="afajh"></form></kbd>
          <strong id="afajh"><dl id="afajh"></dl></strong>
            <del id="afajh"><form id="afajh"></form></del>
                1. <th id="afajh"><progress id="afajh"></progress></th>
                  <b id="afajh"><abbr id="afajh"></abbr></b>
                  <th id="afajh"><progress id="afajh"></progress></th>
                  黑人操逼在线观看 | 欧美亚洲动漫 | 熟女天天干| 日韩免费黄色 | 波多野结衣视频在线看 |