Warning: error_log(/data/www/wwwroot/hmttv.cn/caches/error_log.php): failed to open stream: Permission denied in /data/www/wwwroot/hmttv.cn/phpcms/libs/functions/global.func.php on line 537 Warning: error_log(/data/www/wwwroot/hmttv.cn/caches/error_log.php): failed to open stream: Permission denied in /data/www/wwwroot/hmttv.cn/phpcms/libs/functions/global.func.php on line 537 九九久久99综合一区二区,欧美亚洲福利,国产一级毛片视频

          整合營(yíng)銷服務(wù)商

          電腦端+手機(jī)端+微信端=數(shù)據(jù)同步管理

          免費(fèi)咨詢熱線:

          face-api.js:在瀏覽器中進(jìn)行人臉識(shí)別的JavaScript接口

          自IT Next,作者:Vincent Mühler,機(jī)器之心編譯,參與:Geek AI、張倩。

          本文將為大家介紹一個(gè)建立在「tensorflow.js」內(nèi)核上的 javascript API——「face-api.js」,它實(shí)現(xiàn)了三種卷積神經(jīng)網(wǎng)絡(luò)架構(gòu),用于完成人臉檢測(cè)、識(shí)別和特征點(diǎn)檢測(cè)任務(wù),可以在瀏覽器中進(jìn)行人臉識(shí)別。

          號(hào)外!號(hào)外!現(xiàn)在人們終于可以在瀏覽器中進(jìn)行人臉識(shí)別了!本文將為大家介紹「face-api.js」,這是一個(gè)建立在「tensorflow.js」內(nèi)核上的 javascript 模塊,它實(shí)現(xiàn)了三種卷積神經(jīng)網(wǎng)絡(luò)(CNN)架構(gòu),用于完成人臉檢測(cè)、識(shí)別和特征點(diǎn)檢測(cè)任務(wù)。

          • face-api.js:https://github.com/justadudewhohacks/face-api.js
          • TensorFlow.js:https://github.com/tensorflow/tfjs-core

          像往常一樣,我們將查看一個(gè)簡(jiǎn)單的代碼示例,這將使你能立即通過(guò)短短幾行代碼中的程序包開(kāi)始了解這個(gè) API。讓我們開(kāi)始吧!

          我們已經(jīng)有了「face-recognition.js」,現(xiàn)在又來(lái)了另一個(gè)同樣的程序包?

          如果你閱讀過(guò)本文作者另一篇關(guān)于「node.js」環(huán)境下進(jìn)行人臉識(shí)別的文章《Node.js + face-recognition.js : Simple and Robust Face Recognition using Deep Learning》(Node.js + face-recognition.js:通過(guò)深度學(xué)習(xí)實(shí)現(xiàn)簡(jiǎn)單而魯棒的人臉識(shí)別)(https://medium.com/@muehler.v/node-js-face-recognition-js-simple-and-robust-face-recognition-using-deep-learning-ea5ba8e852),你就會(huì)知道他在之前組裝過(guò)一個(gè)類似的程序包,例如「face-recgnition.js」,從而為「node.js」引入了人臉識(shí)別功能。

          起初,作者并沒(méi)有預(yù)見(jiàn)到 JavaScript 社區(qū)對(duì)與人臉識(shí)別程序包的需求程度如此之高。對(duì)許多人而言,「face-recognition.js」似乎是一個(gè)不錯(cuò)的、能夠免費(fèi)試用的開(kāi)源選項(xiàng),它可以替代由微軟或亞馬遜等公司提供的付費(fèi)人臉識(shí)別服務(wù)。但是作者曾多次被問(wèn)道:是否有可能在瀏覽器中運(yùn)行完整的人臉識(shí)別的工作流水線?

          多虧了「tensorflow.js」,這種設(shè)想最終變?yōu)榱爽F(xiàn)實(shí)!作者設(shè)法使用「tf.js

          」內(nèi)核實(shí)現(xiàn)了部分類似的工具,它們能得到和「face-recognition.js」幾乎相同的結(jié)果,但是作者是在瀏覽器中完成的這項(xiàng)工作!而且最棒的是,這套工具不需要建立任何的外部依賴,使用它非常方便。并且這套工具還能通過(guò) GPU 進(jìn)行加速,相關(guān)操作可以使用 WebGL 運(yùn)行。

          這足以讓我相信,JavaScript 社區(qū)需要這樣的一個(gè)為瀏覽器環(huán)境而編寫的程序包!可以設(shè)想一下你能通過(guò)它構(gòu)建何種應(yīng)用程序。

          如何利用深度學(xué)習(xí)解決人臉識(shí)別問(wèn)題

          如果想要盡快開(kāi)始實(shí)戰(zhàn)部分,那么你可以跳過(guò)這一章,直接跳到代碼分析部分去。但是為了更好地理解「face-api.js」中為了實(shí)現(xiàn)人臉識(shí)別所使用的方法,我強(qiáng)烈建議你順著這個(gè)章節(jié)閱讀下去,因?yàn)槲页31蝗藗儐?wèn)到這個(gè)問(wèn)題。

          為簡(jiǎn)單起見(jiàn),我們實(shí)際想要實(shí)現(xiàn)的目標(biāo)是在給定一張人臉的圖像時(shí),識(shí)別出圖像中的人。為了實(shí)現(xiàn)這個(gè)目標(biāo),我們需要為每一個(gè)我們想要識(shí)別的人提供一張(或更多)他們的人臉圖像,并且給這些圖像打上人臉主人姓名的標(biāo)簽作為參考數(shù)據(jù)。現(xiàn)在,我們將輸入圖像和參考數(shù)據(jù)進(jìn)行對(duì)比,找到與輸入圖像最相似的參考圖像。如果有兩張圖像都與輸入足夠相似,那么我們輸出人名,否則輸出「unknown」(未知)。

          聽(tīng)起來(lái)確實(shí)是個(gè)好主意!然而,這個(gè)方案仍然存在兩個(gè)問(wèn)題。首先,如果我們有一張顯示了多人的圖像,并且我們需要識(shí)別出其中所有的人,將會(huì)怎樣呢?其次,我們需要建立一種相似度度量手段,用來(lái)比較兩張人臉圖像。

          人臉檢測(cè)

          我們可以從人臉檢測(cè)技術(shù)中找到第一個(gè)問(wèn)題的答案。簡(jiǎn)單地說(shuō),我們將首先定位輸入圖像中的所有人臉。「face-api.js」針對(duì)人臉檢測(cè)工作實(shí)現(xiàn)了一個(gè) SSD(Single Shot Multibox Detector)算法,它本質(zhì)上是一個(gè)基于 MobileNetV1 的卷積神經(jīng)網(wǎng)絡(luò)(CNN),在網(wǎng)絡(luò)的頂層加入了一些人臉邊框預(yù)測(cè)層。

          該網(wǎng)絡(luò)將返回每張人臉的邊界框,并返回每個(gè)邊框相應(yīng)的分?jǐn)?shù),即每個(gè)邊界框表示一張人臉的概率。這些分?jǐn)?shù)被用于過(guò)濾邊界框,因?yàn)榭赡艽嬖谝粡垐D片并不包含任何一張人臉的情況。請(qǐng)注意,為了對(duì)邊界框進(jìn)行檢索,即使圖像中僅僅只有一個(gè)人,也應(yīng)該執(zhí)行人臉檢測(cè)過(guò)程。

          人臉特征點(diǎn)檢測(cè)及人臉對(duì)齊

          在上文中,我們已經(jīng)解決了第一個(gè)問(wèn)題!然而,我想要指出的是,我們需要對(duì)齊邊界框,從而抽取出每個(gè)邊界框中的人臉居中的圖像,接著將其作為輸入傳給人臉識(shí)別網(wǎng)絡(luò),因?yàn)檫@樣可以使人臉識(shí)別更加準(zhǔn)確!

          為了實(shí)現(xiàn)這個(gè)目標(biāo),「face-api.js」實(shí)現(xiàn)了一個(gè)簡(jiǎn)單的卷積神經(jīng)網(wǎng)絡(luò)(CNN),它將返回給定圖像的 68 個(gè)人臉特征點(diǎn):

          從特征點(diǎn)位置上看,邊界框可以將人臉居中。你可以從下圖中看到人臉檢測(cè)結(jié)果(左圖)與對(duì)齊后的人臉圖像(右圖)的對(duì)比:

          人臉識(shí)別

          現(xiàn)在,我們可以將提取出的對(duì)齊后的人臉圖像輸入到人臉識(shí)別網(wǎng)絡(luò)中,該網(wǎng)絡(luò)基于一個(gè)類似于 ResNet-34 的架構(gòu),基本上與 dlib(https://github.com/davisking/dlib/blob/master/examples/dnn_face_recognition_ex.cpp)中實(shí)現(xiàn)的架構(gòu)一致。該網(wǎng)絡(luò)已經(jīng)被訓(xùn)練去學(xué)習(xí)出人臉特征到人臉描述符的映射(一個(gè)包含 128 個(gè)值的特征向量),這個(gè)過(guò)程通常也被稱為人臉嵌入。

          現(xiàn)在讓我們回到最初對(duì)比兩張人臉圖像的問(wèn)題:我們將使用每張抽取出的人臉圖像的人臉描述符,并且將它們與參考數(shù)據(jù)的人臉描述符進(jìn)行對(duì)比。更確切地說(shuō),我們可以計(jì)算兩個(gè)人臉描述符之間的歐氏距離,并根據(jù)閾值判斷兩張人臉圖像是否相似(對(duì)于 150*150 的圖像來(lái)說(shuō),0.6 是一個(gè)很好的閾值)。使用歐氏距離的效果驚人的好,當(dāng)然,你也可以選用任何一種分類器。下面的 gif 動(dòng)圖可視化了通過(guò)歐氏距離比較兩張人臉圖像的過(guò)程:

          至此,我們已經(jīng)對(duì)人臉識(shí)別的理論有所了解。接下來(lái)讓我們開(kāi)始編寫一個(gè)代碼示例。

          是時(shí)候開(kāi)始編程了!

          在這個(gè)簡(jiǎn)短的示例中,我們將看到如何一步步地運(yùn)行人臉識(shí)別程序,識(shí)別出如下所示的輸入圖像中的多個(gè)人物:

          導(dǎo)入腳本

          首先,從 dist/face-api.js 獲得最新的版本(https://github.com/justadudewhohacks/face-api.js/tree/master/dist),或者從 dist/face-api.min.js 獲得縮減版,并且導(dǎo)入腳本:

          <script src="face-api.js"></script>

          如果你使用 npm 包管理工具,可以輸入如下指令:

          npm i face-api.js

          加載模型數(shù)據(jù)

          你可以根據(jù)應(yīng)用程序的要求加載你需要的特定模型。但是如果要運(yùn)行一個(gè)完整的端到端的示例,我們還需要加載人臉檢測(cè)、人臉特征點(diǎn)檢測(cè)和人臉識(shí)別模型。相關(guān)的模型文件可以在代碼倉(cāng)庫(kù)中找到,鏈接如下:https://github.com/justadudewhohacks/face-api.js/tree/master/weights。

          其中,模型的權(quán)重已經(jīng)被量化,文件大小相對(duì)于初始模型減小了 75%,使你的客戶端僅僅需要加載所需的最少的數(shù)據(jù)。此外,模型的權(quán)重被分到了最大為 4 MB 的數(shù)據(jù)塊中,使瀏覽器能夠緩存這些文件,這樣它們就只需要被加載一次。

          模型文件可以直接作為你的 web 應(yīng)用中的靜態(tài)資源被使用,或者你可以將它們存放在另外的主機(jī)上,通過(guò)指定的路徑或文件的 url 鏈接來(lái)加載。假如你將它們與你在 public/models 文件夾下的資產(chǎn)共同存放在一個(gè) models 目錄中:

          const MODEL_URL = '/models'

          await faceapi.loadModels(MODEL_URL)

          或者,如果你僅僅想要加載特定的模型:

          const MODEL_URL = '/models'

          await faceapi.loadFaceDetectionModel(MODEL_URL)

          await faceapi.loadFaceLandmarkModel(MODEL_URL)

          await faceapi.loadFaceRecognitionModel(MODEL_URL)

          從輸入圖像中得到對(duì)所有人臉的完整描述

          該神經(jīng)網(wǎng)絡(luò)可以接收 HTML 圖像、畫布、視頻元素或張量(tensor)作為輸入。為了檢測(cè)出輸入圖像中分?jǐn)?shù)(score)大于最小閾值(minScore)的人臉邊界框,我們可以使用下面的簡(jiǎn)單操作:

          const minConfidence = 0.8

          const fullFaceDescriptions = await faceapi.allFaces(input, minConfidence)

          一個(gè)完整的人臉描述符包含了檢測(cè)結(jié)果(邊界框+分?jǐn)?shù)),人臉特征點(diǎn)以及計(jì)算出的描述符。正如你所看到的,「faceapi.allFaces」在底層完成了本文前面的章節(jié)所討論的所有工作。然而,你也可以手動(dòng)地獲取人臉定位和特征點(diǎn)。如果這是你的目的,你可以參考 github repo 中的幾個(gè)示例。

          請(qǐng)注意,邊界框和特征點(diǎn)的位置與原始圖像/媒體文件的尺寸有關(guān)。當(dāng)顯示出的圖像尺寸與原始圖像的尺寸不相符時(shí),你可以簡(jiǎn)單地通過(guò)下面的方法重新調(diào)整它們的大小:

          const resized = fullFaceDescriptions.map(fd => fd.forSize(width, height))

          我們可以通過(guò)將邊界框在畫布上繪制出來(lái)對(duì)檢測(cè)結(jié)果進(jìn)行可視化:

          fullFaceDescription.forEach((fd, i) => {

          faceapi.drawDetection(canvas, fd.detection, { withScore: true })

          })

          可以通過(guò)下面的方法將人臉特征點(diǎn)顯示出來(lái):

          fullFaceDescription.forEach((fd, i) => {

          faceapi.drawLandmarks(canvas, fd.landmarks, { drawLines: true })

          })

          通常,我會(huì)在 img 元素的頂層覆蓋一個(gè)具有相同寬度和高度的絕對(duì)定位的畫布(想獲取更多信息,請(qǐng)參閱 github 上的示例)。

          人臉識(shí)別

          當(dāng)我們知道了如何得到給定的圖像中所有人臉的位置和描述符后,我們將得到一些每張圖片顯示一個(gè)人的圖像,并且計(jì)算出它們的人臉描述符。這些描述符將作為我們的參考數(shù)據(jù)。

          假設(shè)我們有一些可以用的示例圖片,我們首先從一個(gè) url 鏈接處獲取圖片,然后使用「faceapi.bufferToImage」從它們的數(shù)據(jù)緩存中創(chuàng)建 HTML 圖像元素:

          // fetch images from url as blobs

          const blobs = await Promise.all(

          ['sheldon.png' 'raj.png', 'leonard.png', 'howard.png'].map(

          uri => (await fetch(uri)).blob()

          )

          )

          // convert blobs (buffers) to HTMLImage elements

          const images = await Promise.all(blobs.map(

          blob => await faceapi.bufferToImage(blob)

          ))

          接下來(lái),在每張圖像中,正如我們之前對(duì)輸入圖像所做的那樣,我們對(duì)人臉進(jìn)行定位、計(jì)算人臉描述符:

          const refDescriptions = await Promsie.all(images.map(

          img => (await faceapi.allFaces(img))[0]

          ))

          const refDescriptors = refDescriptions.map(fd => fd.descriptor)

          現(xiàn)在,我們還需要做的就是遍歷我們輸入圖像的人臉描述符,并且找到參考數(shù)據(jù)中與輸入圖像距離最小的描述符:

          const sortAsc = (a, b) => a - b

          const labels = ['sheldon', 'raj', 'leonard', 'howard']

          const results = fullFaceDescription.map((fd, i) => {

          const bestMatch = refDescriptors.map(

          refDesc => ({

          label: labels[i],

          distance: faceapi.euclideanDistance(fd.descriptor, refDesc)

          })

          ).sort(sortAsc)[0]

          return {

          detection: fd.detection,

          label: bestMatch.label,

          distance: bestMatch.distance

          }

          })

          正如前面提到的,我們?cè)谶@里使用歐氏距離作為一種相似度度量,這樣做的效果非常好。我們?cè)谳斎雸D像中檢測(cè)出的每一張人臉都是匹配程度最高的。

          最后,我們可以將邊界框和它們的標(biāo)簽一起繪制在畫布上,顯示檢測(cè)結(jié)果:

          // 0.6 is a good distance threshold value to judge

          // whether the descriptors match or not

          const maxDistance = 0.6

          results.forEach(result => {

          faceapi.drawDetection(canvas, result.detection, { withScore: false })

          const text = `${result.distance < maxDistance ? result.className : 'unkown'} (${result.distance})`

          const { x, y, height: boxHeight } = detection.getBox()

          faceapi.drawText(

          canvas.getContext('2d'),

          x,

          y + boxHeight,

          text

          )

          })

          至此,我希望你對(duì)如何使用這個(gè) API 有了一個(gè)初步的認(rèn)識(shí)。同時(shí),我也建議你看看文中給出的代碼倉(cāng)庫(kù)中的其它示例。好好地把這個(gè)程序包玩?zhèn)€痛快吧!

          家好,我是皮皮。

          前言

          對(duì)于前端來(lái)說(shuō),HTML 都是最基礎(chǔ)的內(nèi)容。

          今天,我們來(lái)了解一下 HTML 和網(wǎng)頁(yè)有什么關(guān)系,以及與 DOM 有什么不同。通過(guò)本講內(nèi)容,你將掌握瀏覽器是怎么處理 HTML 內(nèi)容的,以及在這個(gè)過(guò)程中我們可以進(jìn)行怎樣的處理來(lái)提升網(wǎng)頁(yè)的性能,從而提升用戶的體驗(yàn)。


          一、瀏覽器頁(yè)面加載過(guò)程

          不知你是否有過(guò)這樣的體驗(yàn):當(dāng)打開(kāi)某個(gè)瀏覽器的時(shí)候,發(fā)現(xiàn)一直在轉(zhuǎn)圈,或者等了好長(zhǎng)時(shí)間才打開(kāi)頁(yè)面……

          此時(shí)的你,會(huì)選擇關(guān)掉頁(yè)面還是耐心等待呢?

          這一現(xiàn)象,除了網(wǎng)絡(luò)不穩(wěn)定、網(wǎng)速過(guò)慢等原因,大多數(shù)都是由于頁(yè)面設(shè)計(jì)不合理導(dǎo)致加載時(shí)間過(guò)長(zhǎng)導(dǎo)致的。

          我們都知道,頁(yè)面是用 HTML/CSS/JavaScript 來(lái)編寫的。

          • HTML 的職責(zé)在于告知瀏覽器如何組織頁(yè)面,以及搭建頁(yè)面的基本結(jié)構(gòu);
          • CSS 用來(lái)裝飾 HTML,讓我們的頁(yè)面更好看;
          • JavaScript 則可以豐富頁(yè)面功能,使靜態(tài)頁(yè)面動(dòng)起來(lái)。

          HTML由一系列的元素組成,通常稱為HTML元素。HTML 元素通常被用來(lái)定義一個(gè)網(wǎng)頁(yè)結(jié)構(gòu),基本上所有網(wǎng)頁(yè)都是這樣的 HTML 結(jié)構(gòu):

          <html>
              <head></head>
              <body></body>
          </html>

          其中:

          • html元素是頁(yè)面的根元素,它描述完整的網(wǎng)頁(yè);
          • head元素包含了我們想包含在 HTML 頁(yè)面中,但不希望顯示在網(wǎng)頁(yè)里的內(nèi)容;
          • body元素包含了我們?cè)L問(wèn)頁(yè)面時(shí)所有顯示在頁(yè)面上的內(nèi)容,是用戶最終能看到的內(nèi)容;


          HTML 中的元素特別多,其中還包括可用于 Web Components 的自定義元素。

          前面我們提到頁(yè)面 HTML 結(jié)構(gòu)不合理可能會(huì)導(dǎo)致頁(yè)面響應(yīng)慢,這個(gè)過(guò)程很多時(shí)候體現(xiàn)在<script><style>元素的設(shè)計(jì)上,它們會(huì)影響頁(yè)面加載過(guò)程中對(duì) Javascript 和 CSS 代碼的處理。

          因此,如果想要提升頁(yè)面的加載速度,就需要了解瀏覽器頁(yè)面的加載過(guò)程是怎樣的,從根本上來(lái)解決問(wèn)題。

          瀏覽器在加載頁(yè)面的時(shí)候會(huì)用到 GUI 渲染線程和 JavaScript 引擎線程(更詳細(xì)的瀏覽器加載和渲染機(jī)制將在第 7 講中介紹)。其中,GUI 渲染線程負(fù)責(zé)渲染瀏覽器界面 HTML 元素,JavaScript 引擎線程主要負(fù)責(zé)處理 JavaScript 腳本程序。

          由于 JavaScript 在執(zhí)行過(guò)程中還可能會(huì)改動(dòng)界面結(jié)構(gòu)和樣式,因此它們之間被設(shè)計(jì)為互斥的關(guān)系。也就是說(shuō),當(dāng) JavaScript 引擎執(zhí)行時(shí),GUI 線程會(huì)被掛起。

          以網(wǎng)易云課堂官網(wǎng)為例,我們來(lái)看看網(wǎng)頁(yè)加載流程。

          (1)當(dāng)我們打開(kāi)官網(wǎng)的時(shí)候,瀏覽器會(huì)從服務(wù)器中獲取到 HTML 內(nèi)容。

          (2)瀏覽器獲取到 HTML 內(nèi)容后,就開(kāi)始從上到下解析 HTML 的元素。

          (3)<head>元素內(nèi)容會(huì)先被解析,此時(shí)瀏覽器還沒(méi)開(kāi)始渲染頁(yè)面。

          我們看到<head>元素里有用于描述頁(yè)面元數(shù)據(jù)的<meta>元素,還有一些<link>元素涉及外部資源(如圖片、CSS 樣式等),此時(shí)瀏覽器會(huì)去獲取這些外部資源。除此之外,我們還能看到<head>元素中還包含著不少的<script>元素,這些<script>元素通過(guò)src屬性指向外部資源。

          (4)當(dāng)瀏覽器解析到這里時(shí)(步驟 3),會(huì)暫停解析并下載 JavaScript 腳本。

          (5)當(dāng) JavaScript 腳本下載完成后,瀏覽器的控制權(quán)轉(zhuǎn)交給 JavaScript 引擎。當(dāng)腳本執(zhí)行完成后,控制權(quán)會(huì)交回給渲染引擎,渲染引擎繼續(xù)往下解析 HTML 頁(yè)面。

          (6)此時(shí)<body>元素內(nèi)容開(kāi)始被解析,瀏覽器開(kāi)始渲染頁(yè)面。

          在這個(gè)過(guò)程中,我們看到<head>中放置的<script>元素會(huì)阻塞頁(yè)面的渲染過(guò)程:把 JavaScript 放在<head>里,意味著必須把所有 JavaScript 代碼都下載、解析和解釋完成后,才能開(kāi)始渲染頁(yè)面。

          到這里,我們就明白了:如果外部腳本加載時(shí)間很長(zhǎng)(比如一直無(wú)法完成下載),就會(huì)造成網(wǎng)頁(yè)長(zhǎng)時(shí)間失去響應(yīng),瀏覽器就會(huì)呈現(xiàn)“假死”狀態(tài),用戶體驗(yàn)會(huì)變得很糟糕。

          因此,對(duì)于對(duì)性能要求較高、需要快速將內(nèi)容呈現(xiàn)給用戶的網(wǎng)頁(yè),常常會(huì)將 JavaScript 腳本放在<body>的最后面。這樣可以避免資源阻塞,頁(yè)面得以迅速展示。我們還可以使用defer/async/preload等屬性來(lái)標(biāo)記<script>標(biāo)簽,來(lái)控制 JavaScript 的加載順序。

          百度首頁(yè)

          三、DOM 解析

          對(duì)于百度這樣的搜索引擎來(lái)說(shuō),必須要在最短的時(shí)間內(nèi)提供到可用的服務(wù)給用戶,其中就包括搜索框的顯示及可交互,除此之外的內(nèi)容優(yōu)先級(jí)會(huì)相對(duì)較低。

          瀏覽器在渲染頁(yè)面的過(guò)程需要解析 HTML、CSS 以得到 DOM 樹(shù)和 CSS 規(guī)則樹(shù),它們結(jié)合后才生成最終的渲染樹(shù)并渲染。因此,我們還常常將 CSS 放在<head>里,可用來(lái)避免瀏覽器渲染的重復(fù)計(jì)算。


          二、HTML 與 DOM 有什么不同

          我們知道<p>是 HTML 元素,但又常常將<p>這樣一個(gè)元素稱為 DOM 節(jié)點(diǎn),那么 HTML 和 DOM 到底有什么不一樣呢?

          根據(jù) MDN 官方描述:文檔對(duì)象模型(DOM)是 HTML 和 XML 文檔的編程接口。

          也就是說(shuō),DOM 是用來(lái)操作和描述 HTML 文檔的接口。如果說(shuō)瀏覽器用 HTML 來(lái)描述網(wǎng)頁(yè)的結(jié)構(gòu)并渲染,那么使用 DOM 則可以獲取網(wǎng)頁(yè)的結(jié)構(gòu)并進(jìn)行操作。一般來(lái)說(shuō),我們使用 JavaScript 來(lái)操作 DOM 接口,從而實(shí)現(xiàn)頁(yè)面的動(dòng)態(tài)變化,以及用戶的交互操作。

          在開(kāi)發(fā)過(guò)程中,常常用對(duì)象的方式來(lái)描述某一類事物,用特定的結(jié)構(gòu)集合來(lái)描述某些事物的集合。DOM 也一樣,它將 HTML 文檔解析成一個(gè)由 DOM 節(jié)點(diǎn)以及包含屬性和方法的相關(guān)對(duì)象組成的結(jié)構(gòu)集合。


          三、DOM 解析

          我們常見(jiàn)的 HTML 元素,在瀏覽器中會(huì)被解析成節(jié)點(diǎn)。比如下面這樣的 HTML 內(nèi)容:

          <html>
              <head>
                  <title>標(biāo)題</title>
              </head>
              <body>
                  <a href='xx.com'>我的超鏈接</a>
                  <h1>頁(yè)面第一標(biāo)題</h1>
              </body>
          </html>

          打開(kāi)控制臺(tái) Elements 面板,可以看到這樣的 HTML 結(jié)構(gòu),如下圖所示:

          在瀏覽器中,上面的 HTML 會(huì)被解析成這樣的 DOM 樹(shù),如下圖所示:


          我們都知道,對(duì)于樹(shù)狀結(jié)構(gòu)來(lái)說(shuō),常常使用parent/child/sibling等方式來(lái)描述各個(gè)節(jié)點(diǎn)之間的關(guān)系,對(duì)于 DOM 樹(shù)也不例外。

          舉個(gè)例子,我們常常會(huì)對(duì)頁(yè)面功能進(jìn)行抽象,并封裝成組件。但不管怎么進(jìn)行整理,頁(yè)面最終依然是基于 DOM 的樹(shù)狀結(jié)構(gòu),因此組件也是呈樹(shù)狀結(jié)構(gòu),組件間的關(guān)系也同樣可以使用parent/child/sibling這樣的方式來(lái)描述。同時(shí),現(xiàn)在大多數(shù)應(yīng)用程序同樣以root為根節(jié)點(diǎn)展開(kāi),我們進(jìn)行狀態(tài)管理、數(shù)據(jù)管理也常常會(huì)呈現(xiàn)出樹(shù)狀結(jié)構(gòu)。


          四、事件委托

          我們知道,瀏覽器中各個(gè)元素從頁(yè)面中接收事件的順序包括事件捕獲階段、目標(biāo)階段、事件冒泡階段。其中,基于事件冒泡機(jī)制,我們可以實(shí)現(xiàn)將子元素的事件委托給父級(jí)元素來(lái)進(jìn)行處理,這便是事件委托。

          如果我們?cè)诿總€(gè)元素上都進(jìn)行監(jiān)聽(tīng)的話,則需要綁定三個(gè)事件;(假設(shè)頁(yè)面上有a,b,c三個(gè)兄弟節(jié)點(diǎn))

          function clickEventFunction(e) {
            console.log(e.target === this); // logs `true`
            // 這里可以用 this 獲取當(dāng)前元素
          }
          // 元素a,b,c綁定
          element2.addEventListener("click", clickEventFunction, false);
          element5.addEventListener("click", clickEventFunction, false);
          element8.addEventListener("click", clickEventFunction, false);

          使用事件委托,可以通過(guò)將事件添加到它們的父節(jié)點(diǎn),而將事件委托給父節(jié)點(diǎn)來(lái)觸發(fā)處理函數(shù):

          function clickEventFunction(event) {
            console.log(e.target === this); // logs `false`
            // 獲取被點(diǎn)擊的元素
            const eventTarget = event.target;
            // 檢查源元素`event.target`是否符合預(yù)期
            // 此處控制廣告面板的展示內(nèi)容
          }
          // 元素1綁定
          element1.addEventListener("click", clickEventFunction, false);

          這樣能解決什么問(wèn)題呢?

          • 綁定子元素會(huì)綁定很多次的事件,而綁定父元素只需要一次綁定。
          • 將事件委托給父節(jié)點(diǎn),這樣我們對(duì)子元素的增加和刪除、移動(dòng)等,都不需要重新進(jìn)行事件綁定。

          常見(jiàn)的使用方式主要是上述這種列表結(jié)構(gòu),每個(gè)選項(xiàng)都可以進(jìn)行編輯、刪除、添加標(biāo)簽等功能,而把事件委托給父元素,不管我們新增、刪除、更新選項(xiàng),都不需要手動(dòng)去綁定和移除事件。

          如果在列表數(shù)量?jī)?nèi)容較大的時(shí)候,對(duì)成千上萬(wàn)節(jié)點(diǎn)進(jìn)行事件監(jiān)聽(tīng),也是不小的性能消耗。使用事件委托的方式,我們可以大量減少瀏覽器對(duì)元素的監(jiān)聽(tīng),也是在前端性能優(yōu)化中比較簡(jiǎn)單和基礎(chǔ)的一個(gè)做法。

          注意:

          1. 如果我們直接在document.body上進(jìn)行事件委托,可能會(huì)帶來(lái)額外的問(wèn)題;
          2. 由于瀏覽器在進(jìn)行頁(yè)面渲染的時(shí)候會(huì)有合成的步驟,合成的過(guò)程會(huì)先將頁(yè)面分成不同的合成層,而用戶與瀏覽器進(jìn)行交互的時(shí)候需要接收事件。此時(shí),瀏覽器會(huì)將頁(yè)面上具有事件處理程序的區(qū)域進(jìn)行標(biāo)記,被標(biāo)記的區(qū)域會(huì)與主線程進(jìn)行通信。
          3. 如果我們document.body上被綁定了事件,這時(shí)候整個(gè)頁(yè)面都會(huì)被標(biāo)記;
          4. 即使我們的頁(yè)面不關(guān)心某些部分的用戶交互,合成器線程也必須與主線程進(jìn)行通信,并在每次事件發(fā)生時(shí)進(jìn)行等待。這種情況,我們可以使用passive: true選項(xiàng)來(lái)解決


          五、總結(jié)

          我們了解了 HTML 的作用,以及它是如何影響瀏覽器中頁(yè)面的加載過(guò)程的,同時(shí)還介紹了使用 DOM 接口來(lái)控制 HTML 的展示和功能邏輯。我們了解了DOM解析事件委托等相關(guān)概念。

          氣最近越來(lái)越冷啦,在外面上班的人一定要多穿點(diǎn),照顧好自己,常給家里打個(gè)電話報(bào)個(gè)平安,快過(guò)年了,安全第一。

          今天小七和大家分享一下,什么是接口?獲取到接口返回來(lái)的數(shù)據(jù)如何遍歷。

          1:什么是接口?

          (題外話為什么分享這個(gè))我記得我初學(xué)前端時(shí),被這個(gè)問(wèn)題折磨了很久,上班一年多,沒(méi)有調(diào)用過(guò)接口,也不知道接口是什么,當(dāng)時(shí)公司不是前后端分離模式的,突然某天技術(shù)總監(jiān)給啦我一份接口文檔,我天,當(dāng)時(shí)直接懵逼啦,后來(lái)才知道ajax去調(diào)用接口,然后遍歷數(shù)據(jù)。

          接口不要去想那么復(fù)雜,接口就是后臺(tái)給的一個(gè)請(qǐng)求網(wǎng)址,你請(qǐng)求這個(gè)網(wǎng)址,可以獲取到數(shù)據(jù),你想要什么數(shù)據(jù)就給他傳什么樣的值(當(dāng)然值是后端去限制的)。我們現(xiàn)在需要做的就是把接口確定成他就是一個(gè)后臺(tái)寫出來(lái)給我們數(shù)據(jù)的方法。

          2:接口調(diào)用。

          接口調(diào)用,大家都知道ajax是調(diào)用接口的。(圖一代碼圖二結(jié)果)

          代碼

          結(jié)果

          3:如何遍歷賦值(我們就類型全部循環(huán)在li里簡(jiǎn)單做個(gè)循環(huán))

          遍歷

          html的內(nèi)容只有一個(gè)ul calss是book_type;

          這里只是一個(gè)很簡(jiǎn)單的便利,如果有需要我會(huì)講一下購(gòu)物車,或者直播的禮物效果,與socket,因?yàn)槲也惶宄銈冃枰裁矗荒芟葟幕A(chǔ)講起,一步一步來(lái)。

          愿天下永無(wú)bug


          主站蜘蛛池模板: 无码精品一区二区三区在线| 福利片免费一区二区三区| 日韩一区二区三区四区不卡| 武侠古典一区二区三区中文| 91精品福利一区二区三区野战| 白丝爆浆18禁一区二区三区 | 日韩A无码AV一区二区三区 | 国产福利电影一区二区三区久久老子无码午夜伦不 | 在线精品日韩一区二区三区| 91国在线啪精品一区| 相泽南亚洲一区二区在线播放| 亚洲Av高清一区二区三区| 国99精品无码一区二区三区| 国产精品亚洲不卡一区二区三区 | 精品在线一区二区三区| 日本一道高清一区二区三区| 精品国产一区二区三区无码| 精品人妻中文av一区二区三区 | 国产成人无码精品一区不卡| 亚洲午夜精品一区二区麻豆| 亚洲色偷精品一区二区三区| 91精品一区二区综合在线| 久久国产精品最新一区| 亚洲AV色香蕉一区二区| 亚洲国产精品第一区二区| 91视频国产一区| 亚洲av综合av一区二区三区| 欧美av色香蕉一区二区蜜桃小说| 伊人色综合网一区二区三区| 久久中文字幕无码一区二区 | 精品福利一区3d动漫| 亚洲国产一区视频| 中文字幕精品亚洲无线码一区应用| 国产伦一区二区三区高清| 国产在线精品一区二区不卡麻豆| 成人区精品一区二区不卡| 久久综合精品不卡一区二区| 国产对白精品刺激一区二区 | 无码中文人妻在线一区 | 无码精品人妻一区二区三区人妻斩| 无码人妻精品一区二区三18禁|