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

          是誰動(dòng)了我的 DOM?

          共 8258字,需瀏覽 17分鐘

           ·

          2020-10-18 10:09

          在某些場(chǎng)景下,我們希望能監(jiān)視 DOM 樹的變動(dòng),然后做一些相關(guān)的操作。比如監(jiān)聽元素被插入 DOM 或從 DOM 樹中移除,然后添加相應(yīng)的動(dòng)畫效果。或者在富文本編輯器中輸入特殊的符號(hào),如 #@ 符號(hào)時(shí)自動(dòng)高亮后面的內(nèi)容等。

          要實(shí)現(xiàn)這些功能,我們就可以考慮使用 MutationObserver API,接下來阿寶哥將帶大家一起來探索 MutationObserver API 所提供的強(qiáng)大能力。

          閱讀完本文,你將了解以下內(nèi)容:

          • MutationObserver 是什么;
          • MutationObserver API 的基本使用及 MutationRecord 對(duì)象;
          • MutationObserver API 常見的使用場(chǎng)景;
          • 什么是觀察者設(shè)計(jì)模式及如何使用 TS 實(shí)現(xiàn)觀察者設(shè)計(jì)模式。

          一、MutationObserver 是什么

          MutationObserver 接口提供了監(jiān)視對(duì) DOM 樹所做更改的能力。它被設(shè)計(jì)為舊的 Mutation Events 功能的替代品,該功能是 DOM3 Events 規(guī)范的一部分。

          利用 MutationObserver API 我們可以監(jiān)視 DOM 的變化。DOM 的任何變化,比如節(jié)點(diǎn)的增加、減少、屬性的變動(dòng)、文本內(nèi)容的變動(dòng),通過這個(gè) API 我們都可以得到通知。

          MutationObserver 有以下特點(diǎn):

          • 它等待所有腳本任務(wù)執(zhí)行完成后,才會(huì)運(yùn)行,它是異步觸發(fā)的。即會(huì)等待當(dāng)前所有 DOM 操作都結(jié)束才觸發(fā),這樣設(shè)計(jì)是為了應(yīng)對(duì) DOM 頻繁變動(dòng)的問題。
          • 它把 DOM 變動(dòng)記錄封裝成一個(gè)數(shù)組進(jìn)行統(tǒng)一處理,而不是一條一條進(jìn)行處理。
          • 它既可以觀察 DOM 的所有類型變動(dòng),也可以指定只觀察某一類變動(dòng)。

          二、MutationObserver API 簡(jiǎn)介

          在介紹 MutationObserver API 之前,我們先來了解一下它的兼容性:

          (圖片來源:https://caniuse.com/#search=MutationObserver)

          從上圖可知,目前主流的 Web 瀏覽器基本都支持 MutationObserver API,而對(duì)于 IE 瀏覽器只有 IE 11 才支持。在項(xiàng)目中,如需要使用 ?MutationObserver API,首先我們需要?jiǎng)?chuàng)建 MutationObserver 對(duì)象,因此接下來我們來介紹 MutationObserver 構(gòu)造函數(shù)。

          DOM 規(guī)范中的 MutationObserver 構(gòu)造函數(shù),用于創(chuàng)建并返回一個(gè)新的觀察器,它會(huì)在觸發(fā)指定 DOM 事件時(shí),調(diào)用指定的回調(diào)函數(shù)。MutationObserver 對(duì) DOM 的觀察不會(huì)立即啟動(dòng),而必須先調(diào)用 observe() 方法來指定所要觀察的 DOM 節(jié)點(diǎn)以及要響應(yīng)哪些更改。

          2.1 構(gòu)造函數(shù)

          MutationObserver 構(gòu)造函數(shù)的語法為:

          const?observer?=?new?MutationObserver(callback);

          相關(guān)的參數(shù)說明如下:

          • callback:一個(gè)回調(diào)函數(shù),每當(dāng)被指定的節(jié)點(diǎn)或子樹有發(fā)生 DOM 變動(dòng)時(shí)會(huì)被調(diào)用。該回調(diào)函數(shù)包含兩個(gè)參數(shù):一個(gè)是描述所有被觸發(fā)改動(dòng)的 MutationRecord 對(duì)象數(shù)組,另一個(gè)是調(diào)用該函數(shù)的 MutationObserver 對(duì)象。

          使用示例

          const?observer?=?new?MutationObserver(function?(mutations,?observer)?{
          ??mutations.forEach(function(mutation)?{
          ????console.log(mutation);
          ??});
          });

          2.2 方法

          • disconnect():阻止 MutationObserver 實(shí)例繼續(xù)接收通知,除非再次調(diào)用其 observe() 方法,否則該觀察者對(duì)象包含的回調(diào)函數(shù)都不會(huì)再被調(diào)用。

          • observe(target[, options]):該方法用來啟動(dòng)監(jiān)聽,它接受兩個(gè)參數(shù)。第一個(gè)參數(shù),用于指定所要觀察的 DOM 節(jié)點(diǎn)。第二個(gè)參數(shù),是一個(gè)配置對(duì)象,用于指定所要觀察的特定變動(dòng)。

            const?editor?=?document.querySelector('#editor');

            const?options?=?{
            ??childList:?true,?//?監(jiān)視node直接子節(jié)點(diǎn)的變動(dòng)
            ??subtree:?true,?//?監(jiān)視node所有后代的變動(dòng)
            ??attributes:?true,?//?監(jiān)視node屬性的變動(dòng)
            ??characterData:?true,?//?監(jiān)視指定目標(biāo)節(jié)點(diǎn)或子節(jié)點(diǎn)樹中節(jié)點(diǎn)所包含的字符數(shù)據(jù)的變化。
            ??attributeOldValue:?true?//?記錄任何有改動(dòng)的屬性的舊值
            };

            observer.observe(article,?options);
          • takeRecords():返回已檢測(cè)到但尚未由觀察者的回調(diào)函數(shù)處理的所有匹配 DOM 更改的列表,使變更隊(duì)列保持為空。此方法最常見的使用場(chǎng)景是:在斷開觀察者之前立即獲取所有未處理的更改記錄,以便在停止觀察者時(shí)可以處理任何未處理的更改

          2.3 MutationRecord 對(duì)象

          DOM 每次發(fā)生變化,就會(huì)生成一條變動(dòng)記錄,即 MutationRecord 實(shí)例。該實(shí)例包含了與變動(dòng)相關(guān)的所有信息。Mutation Observer 對(duì)象處理的就是一個(gè)個(gè) MutationRecord 實(shí)例所組成的數(shù)組。

          MutationRecord 實(shí)例包含了變動(dòng)相關(guān)的信息,含有以下屬性:

          • type:變動(dòng)的類型,值可以是 attributes、characterData 或 childList;
          • target:發(fā)生變動(dòng)的 DOM 節(jié)點(diǎn);
          • addedNodes:返回新增的 DOM 節(jié)點(diǎn),如果沒有節(jié)點(diǎn)被添加,則返回一個(gè)空的 NodeList;
          • removedNodes:返回移除的 DOM 節(jié)點(diǎn),如果沒有節(jié)點(diǎn)被移除,則返回一個(gè)空的 NodeList;
          • previousSibling:返回被添加或移除的節(jié)點(diǎn)之前的兄弟節(jié)點(diǎn),如果沒有則返回 null
          • nextSibling:返回被添加或移除的節(jié)點(diǎn)之后的兄弟節(jié)點(diǎn),如果沒有則返回 null
          • attributeName:返回被修改的屬性的屬性名,如果設(shè)置了 attributeFilter,則只返回預(yù)先指定的屬性;
          • attributeNamespace:返回被修改屬性的命名空間;
          • oldValue:變動(dòng)前的值。這個(gè)屬性只對(duì) attributecharacterData 變動(dòng)有效,如果發(fā)生 childList 變動(dòng),則返回 null

          2.4 MutationObserver 使用示例


          <html?lang="zh-CN">
          ??<head>
          ????<meta?charset="UTF-8"?/>
          ????<meta?name="viewport"?content="width=device-width,?initial-scale=1.0"?/>
          ????<title>DOM?變動(dòng)觀察器示例title>
          ????<style>
          ??????.editor?{border:?1px?dashed?grey;?width:?400px;?height:?300px;}
          ????
          style>
          ??head>
          ??<body>
          ????<h3>阿寶哥:DOM 變動(dòng)觀察器(Mutation observer)h3>
          ????<div?contenteditable?id="container"?class="editor">大家好,我是阿寶哥!div>

          ????<script>
          ??????const?containerEle?=?document.querySelector("#container");

          ??????let?observer?=?new?MutationObserver((mutationRecords)?=>?{
          ????????console.log(mutationRecords);?//?輸出變動(dòng)記錄
          ??????});

          ??????observer.observe(containerEle,?{
          ????????subtree:?true,?//?監(jiān)視node所有后代的變動(dòng)
          ????????characterDataOldValue:?true,?//?記錄任何有變動(dòng)的屬性的舊值
          ??????});
          ????
          script>
          ??body>
          html>

          以上代碼成功運(yùn)行之后,阿寶哥對(duì) id 為 container 的 div 容器中原始內(nèi)容進(jìn)行修改,即把 大家好,我是阿寶哥! 修改為 大家好,我。對(duì)于上述的修改,控制臺(tái)將會(huì)輸出 5 條變動(dòng)記錄,這里我們來看一下最后一條變動(dòng)記錄:

          MutationObserver 對(duì)象的 observe(target [, options]) 方法支持很多配置項(xiàng),這里阿寶哥就不詳細(xì)展開介紹了。

          但是為了讓剛接觸 MutationObserver API 的小伙伴能更直觀的感受每個(gè)配置項(xiàng)的作用,阿寶哥把 mutationobserver-api-guide 這篇文章中使用的在線示例統(tǒng)一提取出來,做了一下匯總與分類:

          1、MutationObserver Example - childList:https://codepen.io/impressivewebs/pen/aXVVjg

          2、MutationObserver Example - childList with subtree:https://codepen.io/impressivewebs/pen/PVgyLa

          3、MutationObserver Example - Attributes:https://codepen.io/impressivewebs/pen/XOzaWv

          4、MutationObserver Example - Attribute Filter:https://codepen.io/impressivewebs/pen/pGGdVr

          5、MutationObserver Example - attributeFilter with subtree:https://codepen.io/impressivewebs/pen/ywYaYv

          6、MutationObserver Example - characterData:https://codepen.io/impressivewebs/pen/pGdpvq

          7、MutationObserver Example - characterData with subtree:https://codepen.io/impressivewebs/pen/bZVpMZ

          8、MutationObserver Example - Recording an Old Attribute Value:https://codepen.io/impressivewebs/pen/wNNjrP

          9、MutationObserver Example - Recording old characterData:https://codepen.io/impressivewebs/pen/aXrzex

          10、MutationObserver Example - Multiple Changes for a Single Observer:https://codepen.io/impressivewebs/pen/OqJMeG

          11、MutationObserver Example - Moving a Node Tree:https://codepen.io/impressivewebs/pen/GeRWPX

          三、MutationObserver 使用場(chǎng)景

          3.1 語法高亮

          相信大家對(duì)語法高亮都不會(huì)陌生,平時(shí)在閱讀各類技術(shù)文章時(shí),都會(huì)遇到它。接下來,阿寶哥將跟大家介紹如何使用 MutationObserver API 和 Prism.js 這個(gè)庫實(shí)現(xiàn) JavaScript 和 CSS 語法高亮。

          在看具體的實(shí)現(xiàn)代碼前,我們先來看一下以下 HTML 代碼段未語法高亮和語法高亮的區(qū)別:

          let?htmlSnippet?=?`下面是一個(gè)JavaScript代碼段:
          ????
          ????????let?greeting?=?"大家好,我是阿寶哥";?
          ????

          ????
          另一個(gè)CSS代碼段:

          ???????

          ?????????
          ????????????#code-container?{?border:?1px?dashed?grey;?padding:?5px;?}?
          ?????????
          ????

          `

          通過觀察上圖,我們可以很直觀地發(fā)現(xiàn),有進(jìn)行語法高亮的代碼塊閱讀起來更加清晰易懂。下面我們來看一下實(shí)現(xiàn)語法高亮的功能代碼:


          <html?lang="zh-CN">
          ??<head>
          ????<meta?charset="UTF-8"?/>
          ????<meta?name="viewport"?content="width=device-width,?initial-scale=1.0"?/>
          ????<title>MutationObserver?實(shí)戰(zhàn)之語法高亮title>
          ????<style>
          ??????#code-container?{
          ????????border:?1px?dashed?grey;
          ????????padding:?5px;
          ????????width:?550px;
          ????????height:?200px;
          ??????}
          ????
          style>
          ????<link?href="https://cdn.bootcdn.net/ajax/libs/prism/9000.0.1/themes/prism.min.css"?rel="stylesheet">
          ????<script?src="https://cdn.bootcdn.net/ajax/libs/prism/9000.0.1/prism.min.js"?data-manual>script>
          ????<script?src="https://cdn.bootcdn.net/ajax/libs/prism/9000.0.1/components/prism-javascript.min.js">script>
          ????<script?src="https://cdn.bootcdn.net/ajax/libs/prism/9000.0.1/components/prism-css.min.js">script>
          ??head>
          ??<body>
          ????<h3>阿寶哥:MutationObserver 實(shí)戰(zhàn)之語法高亮h3>
          ????<div?id="code-container">div>
          ????<script>
          ??????let?observer?=?new?MutationObserver((mutations)?=>?{
          ????????for?(let?mutation?of?mutations)?{
          ??????????//?獲取新增的DOM節(jié)點(diǎn)
          ??????????for?(let?node?of?mutation.addedNodes)?{
          ????????????//?只處理HTML元素,跳過其他節(jié)點(diǎn),比如文本節(jié)點(diǎn)
          ????????????if?(!(node?instanceof?HTMLElement))?continue;

          ????????????//?檢查插入的節(jié)點(diǎn)是否為代碼段
          ????????????if?(node.matches('pre[class*="language-"]'))?{
          ??????????????Prism.highlightElement(node);
          ????????????}

          ????????????//?檢查插入節(jié)點(diǎn)的子節(jié)點(diǎn)是否為代碼段
          ????????????for?(let?elem?of?node.querySelectorAll('pre[class*="language-"]'))?{
          ??????????????Prism.highlightElement(elem);
          ????????????}
          ??????????}
          ????????}
          ??????});

          ??????let?codeContainer?=?document.querySelector("#code-container");

          ??????observer.observe(codeContainer,?{?childList:?true,?subtree:?true?});
          ??????//?動(dòng)態(tài)插入帶有代碼段的內(nèi)容
          ??????codeContainer.innerHTML?=?`下面是一個(gè)JavaScript代碼段:
          ?????????let?greeting?=?"大家好,我是阿寶哥";?

          ????????
          另一個(gè)CSS代碼段:

          ????????

          ??????????
          ?????????????#code-container?{?border:?1px?dashed?grey;?padding:?5px;?}?
          ??????????
          ????????

          ????????`;
          ????script>
          ??body>
          html>

          在以上代碼中,首先我們?cè)谝?prism.min.js 的 script 標(biāo)簽上設(shè)置 data-manual 屬性,用于告訴 Prism.js 我們將使用手動(dòng)模式來處理語法高亮。

          接著我們?cè)诨卣{(diào)函數(shù)中通過獲取 mutation 對(duì)象的 addedNodes 屬性來進(jìn)一步獲取新增的 DOM 節(jié)點(diǎn)。然后我們遍歷新增的 DOM 節(jié)點(diǎn),判斷新增的 DOM 節(jié)點(diǎn)是否為代碼段,如果滿足條件的話則進(jìn)行高亮操作。

          此外,除了判斷當(dāng)前節(jié)點(diǎn)之外,我們也會(huì)判斷插入節(jié)點(diǎn)的子節(jié)點(diǎn)是否為代碼段,如果滿足條件的話,也會(huì)進(jìn)行高亮操作。

          3.2 監(jiān)聽元素的 load 或 unload 事件

          對(duì) Web 開發(fā)者來說,相信很多人對(duì) load 事件都不會(huì)陌生。當(dāng)整個(gè)頁面及所有依賴資源如樣式表和圖片都已完成加載時(shí),將會(huì)觸發(fā) load 事件。而當(dāng)文檔或一個(gè)子資源正在被卸載時(shí),會(huì)觸發(fā) unload 事件。

          在日常開發(fā)過程中,除了監(jiān)聽頁面的加載和卸載事件之外,我們經(jīng)常還需要監(jiān)聽 DOM 節(jié)點(diǎn)的插入和移除事件。比如當(dāng) DOM 節(jié)點(diǎn)插入 DOM 樹中產(chǎn)生插入動(dòng)畫,而當(dāng)節(jié)點(diǎn)從 DOM 樹中被移除時(shí)產(chǎn)生移除動(dòng)畫。針對(duì)這種場(chǎng)景我們就可以利用 MutationObserver API 來監(jiān)聽元素的添加與移除。

          同樣,在看具體的實(shí)現(xiàn)代碼前,我們先來看一下實(shí)際的效果:

          在以上示例中,當(dāng)點(diǎn)擊 跟蹤元素生命周期 按鈕時(shí),一個(gè)新的 DIV 元素會(huì)被插入到 body 中,成功插入后,會(huì)在消息框顯示相關(guān)的信息。在 3S 之后,新增的 DIV 元素會(huì)從 DOM 中移除,成功移除后,會(huì)在消息框中顯示 元素已從DOM中移除了 的信息。

          下面我們來看一下具體實(shí)現(xiàn):

          index.html


          <html?lang="zh-CN">
          ??<head>
          ????<meta?charset="UTF-8"?/>
          ????<meta?name="viewport"?content="width=device-width,?initial-scale=1.0"?/>
          ????<title>MutationObserver?load/unload?事件title>
          ????<link
          ??????rel="stylesheet"
          ??????href="https://cdnjs.cloudflare.com/ajax/libs/animate.css/4.0.0/animate.min.css"
          ????/>

          ??head>
          ??<body>
          ????<h3>阿寶哥:MutationObserver load/unload 事件h3>
          ????<div?class="block">
          ??????<p>
          ????????<button?onclick="trackElementLifecycle()">跟蹤元素生命周期button>
          ??????p>
          ??????<textarea?id="messageContainer"?rows="5"?cols="50">textarea>
          ????div>
          ????<script?src="./on-load.js">script>
          ????<script>
          ??????const?busy?=?false;
          ??????const?messageContainer?=?document.querySelector("#messageContainer");

          ??????function?trackElementLifecycle()?{
          ????????if?(busy)?return;
          ????????const?div?=?document.createElement("div");
          ????????div.innerText?=?"我是新增的DIV元素";
          ????????div.classList.add("animate__animated",?"animate__bounceInDown");
          ????????watchElement(div);
          ????????document.body.appendChild(div);
          ??????}

          ??????function?watchElement(element)?{
          ????????onload(
          ??????????element,
          ??????????function?(el)?{
          ????????????messageContainer.value?=?"元素已被添加到DOM中,?3s后將被移除";
          ????????????setTimeout(()?=>?document.body.removeChild(el),?3000);
          ??????????},
          ??????????function?(el)?{
          ????????????messageContainer.value?=?"元素已從DOM中移除了";
          ??????????}
          ????????);
          ??????}
          ????
          script>
          ??body>
          html>

          on-load.js

          //?只包含部分代碼
          const?watch?=?Object.create(null);
          const?KEY_ID?=?"onloadid"?+?Math.random().toString(36).slice(2);
          const?KEY_ATTR?=?"data-"?+?KEY_ID;
          let?INDEX?=?0;

          if?(window?&&?window.MutationObserver)?{
          ??const?observer?=?new?MutationObserver(function?(mutations)?{
          ????if?(Object.keys(watch).length?1)?return;
          ????for?(let?i?=?0;?i???????if?(mutations[i].attributeName?===?KEY_ATTR)?{
          ????????eachAttr(mutations[i],?turnon,?turnoff);
          ????????continue;
          ??????}
          ??????eachMutation(mutations[i].removedNodes,?function?(index,?el)?{
          ????????if?(!document.documentElement.contains(el))?turnoff(index,?el);
          ??????});
          ??????eachMutation(mutations[i].addedNodes,?function?(index,?el)?{
          ????????if?(document.documentElement.contains(el))?turnon(index,?el);
          ??????});
          ????}
          ??});

          ??observer.observe(document.documentElement,?{
          ????childList:?true,
          ????subtree:?true,
          ????attributes:?true,
          ????attributeOldValue:?true,
          ????attributeFilter:?[KEY_ATTR],
          ??});
          }

          function?onload(el,?on,?off,?caller)?{
          ??on?=?on?||?function?()?{};
          ??off?=?off?||?function?()?{};
          ??el.setAttribute(KEY_ATTR,?"o"?+?INDEX);
          ??watch["o"?+?INDEX]?=?[on,?off,?0,?caller?||?onload.caller];
          ??INDEX?+=?1;
          ??return?el;
          }

          on-load.js 的完整代碼:https://gist.github.com/semlinker/a149763bf033d7f2dff2d32d60c27865

          3.3 富文本編輯器

          除了前面兩個(gè)應(yīng)用場(chǎng)景,在富文本編輯器的場(chǎng)景,MutationObserver API 也有它的用武之地。比如我們希望在富文本編輯器中高亮 # 符號(hào)后的內(nèi)容,這時(shí)候我們就可以通過 MutationObserver API 來監(jiān)聽用戶輸入的內(nèi)容,發(fā)現(xiàn)用戶輸入 # 時(shí)自動(dòng)對(duì)輸入的內(nèi)容進(jìn)行高亮處理。

          這里阿寶哥基于 vue-hashtag-textarea 這個(gè)項(xiàng)目來演示一下上述的效果:

          此外,MutationObserver API 在 Github 上的一個(gè)名為 Editor.js 的項(xiàng)目中也有應(yīng)用。Editor.js 是一個(gè) Block-Styled 編輯器,以 JSON 格式輸出數(shù)據(jù)的富文本和媒體編輯器。它是完全模塊化的,由 “塊” 組成,這意味著每個(gè)結(jié)構(gòu)單元都是它自己的塊(例如段落、標(biāo)題、圖像都是塊),用戶可以輕松地編寫自己的插件來進(jìn)一步擴(kuò)展編輯器。

          在 Editor.js 編輯器內(nèi)部,它通過 MutationObserver API 來監(jiān)聽富文本框的內(nèi)容異動(dòng),然后觸發(fā) change 事件,使得外部可以對(duì)變動(dòng)進(jìn)行響應(yīng)和處理。上述的功能被封裝到內(nèi)部的 modificationsObserver.ts 模塊,感興趣的小伙伴可以閱讀 modificationsObserver.ts 模塊的代碼。

          當(dāng)然利用 MutationObserver API 提供的強(qiáng)大能力,我們還可以有其他的應(yīng)用場(chǎng)景,比如防止頁面的水印元素被刪除,從而避免無法跟蹤到 “泄密” ,當(dāng)然這并不是絕對(duì)的安全,只是多加了一層防護(hù)措施。

          具體如何實(shí)現(xiàn)水印元素被刪除,篇幅有限。這里阿寶哥不繼續(xù)展開介紹了,大家可以參考掘金上 “打開控制臺(tái)也刪不掉的元素,前端都嚇尿了” 這一篇文章。

          至此 MutationObserver 變動(dòng)觀察者相關(guān)內(nèi)容已經(jīng)介紹完了,既然講到觀察者,阿寶哥情不自禁想再介紹一下觀察者設(shè)計(jì)模式。

          四、觀察者設(shè)計(jì)模式

          4.1 簡(jiǎn)介

          觀察者模式,它定義了一種一對(duì)多的關(guān)系,讓多個(gè)觀察者對(duì)象同時(shí)監(jiān)聽某一個(gè)主題對(duì)象,這個(gè)主題對(duì)象的狀態(tài)發(fā)生變化時(shí)就會(huì)通知所有的觀察者對(duì)象,使得它們能夠自動(dòng)更新自己。

          我們可以使用日常生活中,期刊訂閱的例子來形象地解釋一下上面的概念。期刊訂閱包含兩個(gè)主要的角色:期刊出版方和訂閱者,他們之間的關(guān)系如下:

          • 期刊出版方 —— 負(fù)責(zé)期刊的出版和發(fā)行工作。
          • 訂閱者 —— 只需執(zhí)行訂閱操作,新版的期刊發(fā)布后,就會(huì)主動(dòng)收到通知,如果取消訂閱,以后就不會(huì)再收到通知。

          在觀察者模式中也有兩個(gè)主要角色:Subject(主題)和 Observer(觀察者),它們分別對(duì)應(yīng)例子中的期刊出版方和訂閱者。接下來我們來看張圖,進(jìn)一步加深對(duì)以上概念的理解。

          4.2 模式結(jié)構(gòu)

          觀察者模式包含以下角色:

          • Subject:主題類
          • Observer:觀察者

          4.3 觀察者模式實(shí)戰(zhàn)

          4.3.1 定義 Observer 接口
          interface?Observer?{
          ??notify:?Function;
          }
          4.3.2 創(chuàng)建 ConcreteObserver 觀察者實(shí)現(xiàn)類
          class?ConcreteObserver?implements?Observer{
          ????constructor(private?name:?string)?{}

          ????notify()?{
          ??????console.log(`${this.name}?has?been?notified.`);
          ????}
          }
          4.3.3 創(chuàng)建 Subject 類
          class?Subject?{?
          ????private?observers:?Observer[]?=?[];

          ????public?addObserver(observer:?Observer):?void?{
          ??????console.log(observer,?"is?pushed!");
          ??????this.observers.push(observer);
          ????}

          ????public?deleteObserver(observer:?Observer):?void?{
          ??????console.log("remove",?observer);
          ??????const?n:?number?=?this.observers.indexOf(observer);
          ??????n?!=?-1?&&?this.observers.splice(n,?1);
          ????}

          ????public?notifyObservers():?void?{
          ??????console.log("notify?all?the?observers",?this.observers);
          ??????this.observers.forEach(observer?=>?observer.notify());
          ????}
          }
          4.3.4 使用示例
          const?subject:?Subject?=?new?Subject();
          const?semlinker?=?new?ConcreteObserver("semlinker");
          const?kaquqo?=?new?ConcreteObserver("kakuqo");
          subject.addObserver(semlinker);
          subject.addObserver(kaquqo);
          subject.notifyObservers();

          subject.deleteObserver(kaquqo);
          subject.notifyObservers();

          以上代碼成功運(yùn)行后,控制臺(tái)會(huì)輸出以下結(jié)果:

          [LOG]:?{?"name":?"semlinker"?},??is?pushed!?
          [LOG]:?{?"name":?"kakuqo"?},??is?pushed!?
          [LOG]:?notify?all?the?observers,??[?{?"name":?"semlinker"?},?{?"name":?"kakuqo"?}?]?
          [LOG]:?semlinker?has?been?notified.?
          [LOG]:?kakuqo?has?been?notified.?
          [LOG]:?remove,??{?"name":?"kakuqo"?}?
          [LOG]:?notify?all?the?observers,??[?{?"name":?"semlinker"?}?]?
          [LOG]:?semlinker?has?been?notified.?

          通過觀察以上的輸出結(jié)果,當(dāng)觀察者被移除以后,后續(xù)的通知就接收不到了。觀察者模式支持簡(jiǎn)單的廣播通信,能夠自動(dòng)通知所有已經(jīng)訂閱過的對(duì)象。但如果一個(gè)被觀察者對(duì)象有很多的觀察者的話,將所有的觀察者都通知到會(huì)花費(fèi)很多時(shí)間。 所以在實(shí)際項(xiàng)目中使用的話,大家需要注意以上的問題。

          五、參考資源

          • MDN - MutationObserver
          • MDN - MutationRecord
          • JavaScript 標(biāo)準(zhǔn)參考教程 - MutationObserver
          • mutationobserver-api-guide
          • javascript.info-mutation-observer
          瀏覽 59
          點(diǎn)贊
          評(píng)論
          收藏
          分享

          手機(jī)掃一掃分享

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

          手機(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>
                  青娱乐最新偷拍视频 | 亚洲黄片免费观看 | 欧美一区二区黄色 | 一级aa免费视频 | 亚洲在线第一页 |