<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>

          這幾個高級前端常用的 API,你用到了嗎?

          共 5302字,需瀏覽 11分鐘

           ·

          2022-05-15 22:25

          推薦關注↓

          本文在github做了收錄 github.com/Michael-lzg/my--article/blob/master/other/分享幾個實用的API.md

          • MutationObserver

          • IntersectionObserver

          • getComputedStyle()

          • getBoundingClientRect

          • requestAnimationFrame

          MutationObserver

          MutationObserver 是一個可以監(jiān)聽 DOM 結構變化的接口。當 DOM 對象樹發(fā)生任何變動時,MutationObserver 會得到通知。

          API

          MutationObserver 是一個構造器,接受一個 callback 參數(shù),用來處理節(jié)點變化的回調(diào)函數(shù),返回兩個參數(shù):

          • mutations:節(jié)點變化記錄列表(sequence
          • observer:構造 MutationObserver 對象。

          MutationObserver 對象有三個方法,分別如下:

          • observe:設置觀察目標,接受兩個參數(shù),target:觀察目標,options:通過對象成員來設置觀察選項
          • disconnect:阻止觀察者觀察任何改變
          • takeRecords:清空記錄隊列并返回里面的內(nèi)容
          //選擇一個需要觀察的節(jié)點
          var?targetNode?=?document.getElementById('root')

          //?設置observer的配置選項
          var?config?=?{?attributes:?true,?childList:?true,?subtree:?true?}

          //?當節(jié)點發(fā)生變化時的需要執(zhí)行的函數(shù)
          var?callback?=?function?(mutationsList,?observer)?{
          ??for?(var?mutation?of?mutationsList)?{
          ????if?(mutation.type?==?'childList')?{
          ??????console.log('A?child?node?has?been?added?or?removed.')
          ????}?else?if?(mutation.type?==?'attributes')?{
          ??????console.log('The?'?+?mutation.attributeName?+?'?attribute?was?modified.')
          ????}
          ??}
          }

          //?創(chuàng)建一個observer示例與回調(diào)函數(shù)相關聯(lián)
          var?observer?=?new?MutationObserver(callback)

          //使用配置文件對目標節(jié)點進行觀測
          observer.observe(targetNode,?config)

          //?停止觀測
          observer.disconnect()

          observe 方法中 options 參數(shù)有已下幾個選項:

          • childList:設置 true,表示觀察目標子節(jié)點的變化,比如添加或者刪除目標子節(jié)點,不包括修改子節(jié)點以及子節(jié)點后代的變化
          • attributes:設置 true,表示觀察目標屬性的改變
          • characterData:設置 true,表示觀察目標數(shù)據(jù)的改變
          • subtree:設置為 true,目標以及目標的后代改變都會觀察
          • attributeOldValue:如果屬性為 true 或者省略,則相當于設置為 true,表示需要記錄改變前的目標屬性值,設置了 attributeOldValue 可以省略 attributes 設置
          • characterDataOldValue:如果 characterData 為 true 或省略,則相當于設置為 true,表示需要記錄改變之前的目標數(shù)據(jù),設置了 characterDataOldValue 可以省略 characterData 設置
          • attributeFilter:如果不是所有的屬性改變都需要被觀察,并且 attributes 設置為 true 或者被忽略,那么設置一個需要觀察的屬性本地名稱(不需要命名空間)的列表

          特點

          MutationObserver 有以下特點:

          • 它等待所有腳本任務完成后才會運行,即采用異步方式
          • 它把 DOM 變動記錄封裝成一個數(shù)組進行處理,而不是一條條地個別處理 DOM 變動。
          • 它即可以觀察發(fā)生在 DOM 節(jié)點的所有變動,也可以觀察某一類變動

          當 DOM 發(fā)生變動會觸發(fā) MutationObserver 事件。但是,它與事件有一個本質不同:事件是同步觸發(fā),也就是說 DOM 發(fā)生變動立刻會觸發(fā)相應的事件;MutationObserver 則是異步觸發(fā),DOM 發(fā)生變動以后,并不會馬上觸發(fā),而是要等到當前所有 DOM 操作都結束后才觸發(fā)。

          舉例來說,如果在文檔中連續(xù)插入 1000 個段落(p 元素),會連續(xù)觸發(fā) 1000 個插入事件,執(zhí)行每個事件的回調(diào)函數(shù),這很可能造成瀏覽器的卡頓;而 MutationObserver 完全不同,只在 1000 個段落都插入結束后才會觸發(fā),而且只觸發(fā)一次,這樣較少了 DOM 的頻繁變動,大大有利于性能。

          IntersectionObserver

          網(wǎng)頁開發(fā)時,常常需要了解某個元素是否進入了"視口"(viewport),即用戶能不能看到它。

          傳統(tǒng)的實現(xiàn)方法是,監(jiān)聽到 scroll 事件后,調(diào)用目標元素的 getBoundingClientRect()方法,得到它對應于視口左上角的坐標,再判斷是否在視口之內(nèi)。這種方法的缺點是,由于 scroll 事件密集發(fā)生,計算量很大,容易造成性能問題。

          目前有一個新的 IntersectionObserver API,可以自動"觀察"元素是否可見,Chrome 51+ 已經(jīng)支持。由于可見(visible)的本質是,目標元素與視口產(chǎn)生一個交叉區(qū),所以這個 API 叫做"交叉觀察器"。

          API

          IntersectionObserver 是瀏覽器原生提供的構造函數(shù),接受兩個參數(shù):callback 是可見性變化時的回調(diào)函數(shù),option 是配置對象(該參數(shù)可選)。

          var?io?=?new?IntersectionObserver(callback,?option)

          //?開始觀察
          io.observe(document.getElementById('example'))

          //?停止觀察
          io.unobserve(element)

          //?關閉觀察器
          io.disconnect()

          如果要觀察多個節(jié)點,就要多次調(diào)用這個方法。

          io.observe(elementA)
          io.observe(elementB)

          目標元素的可見性變化時,就會調(diào)用觀察器的回調(diào)函數(shù) callback。callback 一般會觸發(fā)兩次。一次是目標元素剛剛進入視口(開始可見),另一次是完全離開視口(開始不可見)。

          var?io?=?new?IntersectionObserver((entries)?=>?{
          ??console.log(entries)
          })

          callback 函數(shù)的參數(shù)(entries)是一個數(shù)組,每個成員都是一個 IntersectionObserverEntry 對象。舉例來說,如果同時有兩個被觀察的對象的可見性發(fā)生變化,entries 數(shù)組就會有兩個成員。

          • time:可見性發(fā)生變化的時間,是一個高精度時間戳,單位為毫秒
          • target:被觀察的目標元素,是一個 DOM 節(jié)點對象
          • isIntersecting: 目標是否可見
          • rootBounds:根元素的矩形區(qū)域的信息,getBoundingClientRect()方法的返回值,如果沒有根元素(即直接相對于視口滾動),則返回 null
          • boundingClientRect:目標元素的矩形區(qū)域的信息
          • intersectionRect:目標元素與視口(或根元素)的交叉區(qū)域的信息
          • intersectionRatio:目標元素的可見比例,即 intersectionRect 占 boundingClientRect 的比例,完全可見時為 1,完全不可見時小于等于 0

          舉個例子

          <html?lang="en">
          ??<head>
          ????<meta?charset="UTF-8"?/>
          ????<title>Documenttitle>
          ????<style>
          ??????#div1?{
          ????????position:?sticky;
          ????????top:?0;
          ????????height:?50px;
          ????????line-height:?50px;
          ????????text-align:?center;
          ????????background:?black;
          ????????color:?#ffffff;
          ????????font-size:?18px;
          ??????}
          ????
          style>
          ??head>

          ??<body>
          ????<div?id="div1">首頁div>
          ????<div?style="height:?1000px;">div>
          ????<div?id="div2"?style="height:?100px;?background:?red;">div>
          ????<script>
          ??????var?div2?=?document.getElementById('div2')
          ??????let?observer?=?new?IntersectionObserver(
          ????????function?(entries)?{
          ??????????entries.forEach(function?(element,?index)?{
          ????????????console.log(element)
          ????????????if?(element.isIntersecting)?{
          ??????????????div1.innerText?=?'我出來了'
          ????????????}?else?{
          ??????????????div1.innerText?=?'首頁'
          ????????????}
          ??????????})
          ????????},
          ????????{
          ??????????root:?null,
          ??????????threshold:?[0,?1]
          ????????}
          ??????)

          ??????observer.observe(div2)
          ????
          script>
          ??body>
          html>

          相比于 getBoundingClientRect,它的優(yōu)點是不會引起重繪回流。兼容性如下

          ce1aacb7ef401638fdedfd742041bfde.webp

          圖片懶加載

          圖片懶加載的原理主要是判斷當前圖片是否到了可視區(qū)域這一核心邏輯實現(xiàn)的。這樣可以節(jié)省帶寬,提高網(wǎng)頁性能。

          傳統(tǒng)的突破懶加載是通過監(jiān)聽 scroll 事件實現(xiàn)的,但是 scroll 事件會在很短的時間內(nèi)觸發(fā)很多次,嚴重影響頁面性能。為提高頁面性能,我們可以使用 IntersectionObserver 來實現(xiàn)圖片懶加載。

          const?imgs?=?document.querySelectorAll('img[src]')
          const?config?=?{
          ??rootMargin:?'0px',
          ??threshold:?0
          }
          let?observer?=?new?IntersectionObserver((entries,?self)?=>?{
          ??entries.forEach((entry)?=>?{
          ????if?(entry.isIntersecting)?{
          ??????let?img?=?entry.target
          ??????let?src?=?img.dataset.src
          ??????if?(src)?{
          ????????img.src?=?src
          ????????img.removeAttribute('src')
          ??????}
          ??????//?解除觀察
          ??????self.unobserve(entry.target)
          ????}
          ??})
          },?config)

          imgs.forEach((image)?=>?{
          ??observer.observe(image)
          })

          無限滾動

          無限滾動(infinite scroll)的實現(xiàn)也很簡單。

          var?intersectionObserver?=?new?IntersectionObserver(function?(entries)?{
          ??//?如果不可見,就返回
          ??if?(entries[0].intersectionRatio?<=?0)?return
          ??loadItems(10)
          ??console.log('Loaded?new?items')
          })

          //?開始觀察
          intersectionObserver.observe(document.querySelector('.scrollerFooter'))

          getComputedStyle()

          DOM2 Style 在?document.defaultView?上增加了 getComputedStyle()方法,該方法返回一個?CSSStyleDeclaration?對象(與 style 屬性的類型一樣),包含元素的計算樣式。

          API

          document.defaultView.getComputedStyle(element[,pseudo-element])
          //?or
          window.getComputedStyle(element[,pseudo-element])

          這個方法接收兩個參數(shù):要取得計算樣式的元素和偽元素字符串(如":after")。如果不需要查詢偽元素,則第二個參數(shù)可以傳 null。

          html>
          <html>?
          ??<head>??
          ????<style?type="text/css">????
          ??????#myDiv?{?????
          ????????background-color:?blue;????
          ????????width:?100px;????
          ????????height:?200px;???
          ??????}??
          ????
          style>
          ??head>?
          ??<body>??
          ????<div?id="myDiv"?style="background-color:?red;?border:?1px?solid?black">div>?
          ??body>?
          ??<script>??
          ????function?getStyleByAttr(obj,?name)?{???
          ??????return?window.getComputedStyle???window.getComputedStyle(obj,?null)[name]?:?obj.currentStyle[name]??
          ????}???
          ????let?node?=?document.getElementById('myDiv')??
          ????console.log(getStyleByAttr(node,?'backgroundColor'))??
          ????console.log(getStyleByAttr(node,?'width'))??
          ????console.log(getStyleByAttr(node,?'height'))??
          ????console.log(getStyleByAttr(node,?'border'))?
          ??
          script>
          html>

          和 style 的異同

          getComputedStyle 和 element.style 的相同點就是二者返回的都是 CSSStyleDeclaration 對象。而不同點就是:

          • element.style 讀取的只是元素的內(nèi)聯(lián)樣式,即寫在元素的 style 屬性上的樣式;而 getComputedStyle 讀取的樣式是最終樣式,包括了內(nèi)聯(lián)樣式、嵌入樣式和外部樣式。
          • element.style 既支持讀也支持寫,我們通過 element.style 即可改寫元素的樣式。而 getComputedStyle 僅支持讀并不支持寫入。我們可以通過使用 getComputedStyle 讀取樣式,通過 element.style 修改樣式

          getBoundingClientRect

          getBoundingClientRect() 方法返回元素的大小及其相對于視口的位置。

          API

          let?DOMRect?=?object.getBoundingClientRect()

          它的返回值是一個 DOMRect 對象,這個對象是由該元素的 getClientRects() 方法返回的一組矩形的集合,就是該元素的 CSS 邊框大小。

          返回的結果是包含完整元素的最小矩形,并且擁有 left, top, right, bottom, x, y, width, 和 height 這幾個以像素為單位的只讀屬性用于描述整個邊框。除了 width 和 height 以外的屬性是相對于視圖窗口的左上角來計算的。

          b9fa6ec21a7041d254a0daabfb3618ec.webp

          應用場景

          1、獲取 dom 元素相對于網(wǎng)頁左上角定位的距離

          以前的寫法是通過 offsetParent 找到元素到定位父級元素,直至遞歸到頂級元素 body 或 html。

          //?獲取dom元素相對于網(wǎng)頁左上角定位的距離
          function?offset(el)?{?
          ??var?top?=?0?
          ??var?left?=?0?
          ??do?{??
          ????top?+=?el.offsetTop??
          ????left?+=?el.offsetLeft?
          ??}?while?((el?=?el.offsetParent))?//?存在兼容性問題,需要兼容?
          ??return?{?
          ????top:?top,??
          ????left:?left?
          ??}
          }

          var?odiv?=?document.getElementsByClassName('markdown-body')
          offset(a[0])?//?{top:?271,?left:?136}

          現(xiàn)在根據(jù) getBoundingClientRect 這個 api,可以寫成這樣:

          var?positionX?=?this.getBoundingClientRect().left?+?document.documentElement.scrollLeft
          var?positionY?=?this.getBoundingClientRect().top?+?document.documentElement.scrollTop

          2、判斷元素是否在可視區(qū)域內(nèi)

          function?isElView(el)?{?
          ??var?top?=?el.getBoundingClientRect().top?//?元素頂端到可見區(qū)域頂端的距離?
          ??var?bottom?=?el.getBoundingClientRect().bottom?//?元素底部端到可見區(qū)域頂端的距離?
          ??var?se?=?document.documentElement.clientHeight?//?瀏覽器可見區(qū)域高度。?
          ??if?(top??0)?{???
          ????return?true?
          ??}?else?if?(top?>=?se?||?bottom?<=?0)?{??
          ????//?不可見?
          ??}?
          ??return?false
          }

          requestAnimationFrame

          window.requestAnimationFrame() 告訴瀏覽器——你希望執(zhí)行一個動畫,并且要求瀏覽器在下次重繪之前調(diào)用指定的回調(diào)函數(shù)更新動畫。

          API

          該方法需要傳入一個回調(diào)函數(shù)作為參數(shù),該回調(diào)函數(shù)會在瀏覽器下一次重繪之前執(zhí)行。

          window.requestAnimationFrame(callback)

          兼容性處理

          window._requestAnimationFrame?=?(function?()?{?
          ??return?(?
          ????window.requestAnimationFrame?||???
          ????window.webkitRequestAnimationFrame?||??
          ????window.mozRequestAnimationFrame?||??
          ????function?(callback)?{????
          ??????window.setTimeout(callback,?1000?/?60)??
          ????}?
          ??)
          })()

          結束動畫

          var?globalID
          function?animate()?{?
          ??//?done();?一直運行?
          ??globalID?=?requestAnimationFrame(animate)?//?Do?something?animate
          }
          globalID?=?requestAnimationFrame(animate)?//開始
          cancelAnimationFrame(globalID)?//結束

          與 setTimeout 相比,requestAnimationFrame 最大的優(yōu)勢是由系統(tǒng)來決定回調(diào)函數(shù)的執(zhí)行時機。具體一點講,如果屏幕刷新率是 60Hz,那么回調(diào)函數(shù)就每 16.7ms 被執(zhí)行一次,如果刷新率是 75Hz,那么這個時間間隔就變成了 1000/75=13.3ms,換句話說就是,requestAnimationFrame 的步伐跟著系統(tǒng)的刷新步伐走。

          它能保證回調(diào)函數(shù)在屏幕每一次的刷新間隔中只被執(zhí)行一次,這樣就不會引起丟幀現(xiàn)象,也不會導致動畫出現(xiàn)卡頓的問題。這個 API 的調(diào)用很簡單,如下所示:

          var?progress?=?0
          //回調(diào)函數(shù)
          function?render()?{
          ??progress?+=?1?//修改圖像的位置?
          ??if?(progress?100)?{??
          ????//在動畫沒有結束前,遞歸渲染
          ????window.requestAnimationFrame(render)?
          ??}
          }
          //第一幀渲染
          window.requestAnimationFrame(render)

          優(yōu)點:

          • CPU 節(jié)能:使用 setTimeout 實現(xiàn)的動畫,當頁面被隱藏或最小化時,setTimeout 仍然在后臺執(zhí)行動畫任務,由于此時頁面處于不可見或不可用狀態(tài),刷新動畫是沒有意義的,完全是浪費 CPU 資源。
            而 requestAnimationFrame 則完全不同,當頁面處理未激活的狀態(tài)下,該頁面的屏幕刷新任務也會被系統(tǒng)暫停,因此跟著系統(tǒng)步伐走的 requestAnimationFrame 也會停止渲染,當頁面被激活時,動畫就從上次停留的地方繼續(xù)執(zhí)行,有效節(jié)省了 CPU 開銷。

          • 函數(shù)節(jié)流:在高頻率事件(resize,scroll 等)中,為了防止在一個刷新間隔內(nèi)發(fā)生多次函數(shù)執(zhí)行,使用 requestAnimationFrame 可保證每個刷新間隔內(nèi),函數(shù)只被執(zhí)行一次,這樣既能保證流暢性,也能更好的節(jié)省函數(shù)執(zhí)行的開銷。一個刷新間隔內(nèi)函數(shù)執(zhí)行多次時沒有意義的,因為顯示器每 16.7ms 刷新一次,多次繪制并不會在屏幕上體現(xiàn)出來。

          應用場景

          1、監(jiān)聽 scroll 函數(shù)

          頁面滾動事件(scroll)的監(jiān)聽函數(shù),就很適合用這個 api,推遲到下一次重新渲染。

          $(window).on('scroll',?function?()?{
          ??window.requestAnimationFrame(scrollHandler)
          })

          平滑滾動到頁面頂部

          const?scrollToTop?=?()?=>?{?
          ??const?c?=?document.documentElement.scrollTop?||?document.body.scrollTop?
          ??if?(c?>?0)?{??
          ????window.requestAnimationFrame(scrollToTop)?
          ????window.scrollTo(0,?c?-?c?/?8)?
          ??}
          }

          scrollToTop()

          2、大量數(shù)據(jù)渲染

          比如對十萬條數(shù)據(jù)進行渲染,主要由以下幾種方法:

          (1)使用定時器

          //需要插入的容器
          let?ul?=?document.getElementById('container')
          //?插入十萬條數(shù)據(jù)
          let?total?=?100000
          //?一次插入?20?條
          let?once?=?20
          //總頁數(shù)
          let?page?=?total?/?once
          //每條記錄的索引
          let?index?=?0
          //循環(huán)加載數(shù)據(jù)
          function?loop(curTotal,?curIndex)?{?
          ??if?(curTotal?<=?0)?{??
          ????return?false?
          ??}??
          ??//每頁多少條
          ??let?pageCount?=?Math.min(curTotal,?once)?
          ??setTimeout(()?=>?{??
          ????for?(let?i?=?0;?i???????let?li?=?document.createElement('li')????
          ??????li.innerText?=?curIndex?+?i?+?'?:?'?+?~~(Math.random()?*?total)????
          ??????ul.appendChild(li)??
          ????}??
          ????loop(curTotal?-?pageCount,?curIndex?+?pageCount)?
          ??},?0)
          }
          loop(total,?index)

          (2)使用 requestAnimationFrame

          //需要插入的容器
          let?ul?=?document.getElementById('container')
          //?插入十萬條數(shù)據(jù)
          let?total?=?100000
          //?一次插入?20?條
          let?once?=?20
          //總頁數(shù)
          let?page?=?total?/?once
          //每條記錄的索引
          let?index?=?0
          //循環(huán)加載數(shù)據(jù)
          function?loop(curTotal,?curIndex)?{
          ??if?(curTotal?<=?0)?{
          ????return?false
          ??}
          ??//每頁多少條
          ??let?pageCount?=?Math.min(curTotal,?once)
          ??window.requestAnimationFrame(function?()?{
          ????for?(let?i?=?0;?i???????let?li?=?document.createElement('li')
          ??????li.innerText?=?curIndex?+?i?+?'?:?'?+?~~(Math.random()?*?total)
          ??????ul.appendChild(li)
          ????}
          ????loop(curTotal?-?pageCount,?curIndex?+?pageCount)
          ??})
          }
          loop(total,?index)

          監(jiān)控卡頓方法

          每秒中計算一次網(wǎng)頁的 FPS,獲得一列數(shù)據(jù),然后分析。通俗地解釋就是,通過 requestAnimationFrame API 來定時執(zhí)行一些 JS 代碼,如果瀏覽器卡頓,無法很好地保證渲染的頻率,1s 中 frame 無法達到 60 幀,即可間接地反映瀏覽器的渲染幀率。

          var?lastTime?=?performance.now()
          var?frame?=?0
          var?lastFameTime?=?performance.now()
          var?loop?=?function?(time)?{
          ??var?now?=?performance.now()
          ??var?fs?=?now?-?lastFameTime
          ??lastFameTime?=?now
          ??var?fps?=?Math.round(1000?/?fs)
          ??frame++
          ??if?(now?>?1000?+?lastTime)?{
          ????var?fps?=?Math.round((frame?*?1000)?/?(now?-?lastTime))
          ????frame?=?0
          ????lastTime?=?now
          ??}
          ??window.requestAnimationFrame(loop)
          }

          我們可以定義一些邊界值,比如連續(xù)出現(xiàn) 3 個低于 20 的 FPS 即可認為網(wǎng)頁存在卡頓。

          作者:lzg9527?

          https://juejin.cn/post/7028744289890861063

          - EOF -


          60fece30fb34dbeb7e1c78efbd68bc71.webp

          加主頁君微信,不僅前端技能+1

          b54176be071e4703c8dbac4ee0b6be5f.webp00e222a1a85033ab3ff568ea1bc8ca5b.webp

          主頁君日常還會在個人微信分享前端開發(fā)學習資源技術文章精選,不定期分享一些有意思的活動崗位內(nèi)推以及如何用技術做業(yè)余項目

          加個微信,打開一扇窗



          推薦閱讀??點擊標題可跳轉

          1、太卷了!瀏覽器也支持原生的深拷貝 API 了?

          2、vue 中 Axios 的封裝和 API 接口的管理

          3、Vue3.0 最新動態(tài):script-setup 定稿,部分實驗性 API 將棄用


          覺得本文對你有幫助?請分享給更多人

          推薦關注「前端大全」,提升前端技能

          點贊和在看就是最大的支持??

          瀏覽 44
          點贊
          評論
          收藏
          分享

          手機掃一掃分享

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

          手機掃一掃分享

          分享
          舉報
          <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>
                  国产日韩欧美在线影院 | 台湾大香蕉视频网 | 爆操嫩逼| A级免费电影 | 学生妹做爱在线播放 |