【實踐思考】本身開發一個掘金黑名單功能插件

前幾天掘金那篇什麼挑戰前端的文章很火,可是幾十的贊幾百的評論,說明這篇文章不是火在質量而是火在爭議。客觀的來說裏面那道題仍是不錯的,能幫助咱們理解js的一些機制。做者評論裏面屢次提到前端版塊文章質量的問題,我想說的是不止前端版塊其餘版塊也同樣,任何社區的都不可能保證全部的文章都是精品,只是掘金前端版塊文章數量比較多,這個問題比較明顯,可是每一個讀者水平不同,你以爲這篇講的東西沒什麼用,但可能對於別人就頗有幫助。javascript

因此就如同評論下面不少人在說但願掘金出一個拉黑屏蔽功能,能夠拉黑做者,屏蔽文章。這個功能確實能夠知足不少人的篩選文章的需求,可是催這個功能也不是一天兩天了,官方可能有本身的計劃一直沒有提上開發日程。我以前其實沒這個需求,頂多就是不太想看面試類的文章,可是那篇文章出來以後,我發現我仍是有拉黑需求的,起碼那篇文章不該該出如今個人文章推薦列表中。雖然知道官方往後確定會出這個功能,可是遠水解不了近渴,不如當下先本身開發一個插件來實現這個功能。php

需求說明

  1. 將不喜歡的做者加入黑名單,在首頁和搜索文章時再也不顯示黑名單中做者的文章。
  2. 將不喜歡的關鍵字加入標題黑名單,在首頁和搜索文章時再也不顯示標題中包含關鍵字的文章。
  3. 在文章詳情頁面能夠隨時拉黑做者。
  4. 能夠對黑名單列表進行管理(添加和刪除)。

成品展現

特此說明,做者拉黑只是爲了展現功能,並且爲了效果特地選擇了專欄比較多的高等級做者,對兩位上鏡的做者並沒有冒犯之意。css

做者屏蔽html

標題關鍵字屏蔽前端

方案思路

實現思路其實很是簡單,首先要明確的一點是掘金的文章列表是後臺請求回的數據,未來官方實現這個功能的時候能夠後端直接返回過濾後的結果,也能夠前端拿到數據時進行過濾處理再展現。而對於咱們外部插件來講沒有這個能力,只能經過將已經生成好的頁面元素進行隱藏來達到咱們的目的。vue

控制檯輸入代碼版

咱們先來一個最簡單的實現,首先咱們打開chrome的調試面板,看一下掘進首頁文章列表的html結構:java

如圖所示全部的文章以li標籤的形式,放在classentry-listul中,那麼咱們如今要作的是選中entry-list下的全部li標籤並查找裏面的內容,若是存在css關鍵字就隱藏掉這個li標籤。node

//$$是大部分瀏覽器調試控制檯都支持的API,等價於document.querySelectorAll,平時開發不能用。
$$('.entry-list>li').forEach(item => {
    //innerText是一個節點及其後代的「渲染」文本內容,判斷裏面是否包含css,不區分大小寫。
    if (/css/i.test(item.innerText)) {
        //符合要求的元素隱藏
        item.hidden = true;
    }
});
複製代碼

在控制檯輸入這段代碼,你就會發現文章列表裏面的包含css關鍵字的條目(這個不光是標題)都被隱藏掉了。jquery

可是滾動條下滑後加載的文章仍是會顯示的,若是想隱藏只能在控制檯再運行一遍這個代碼。git

油猴腳本版

咱們要作的是一個完整的插件,而不能像上面那樣人工手動在控制檯輸入代碼,雖然你寫一個完整的插件代碼控制檯跑也是能夠,功能效果實際上是同樣的,但終究不是長遠之計。

首先咱們能夠把這個功能作成一個瀏覽器插件,好比根據Chrome的規範和API創建項目開發,最後打包成crx格式的文件,Chrome瀏覽器就能夠本地安裝這個插件,或者花點錢成爲谷歌開發者發佈到谷歌應用商店。

瀏覽器插件的好處是,能夠調用更多瀏覽器級的API,使用瀏覽器爲原生插件提供的相關功能,可是對於咱們只針對一個網站提供的小功能來講,有些殺雞用牛刀了,並且這個插件也只有Chrome能用,若是其餘瀏覽器想用還要再開發,好比FireFox就使用的是xpi格式的擴展。

一種更好的方式就是使用Tampermonkey

Tampermonkey(國內習慣稱其爲油猴)是一個瀏覽器插件,是最爲流行的用戶腳本管理器,並且在 Chrome, Microsoft Edge, Safari, Opera Next, 和 Firefox都有對應的版本,也就是說咱們寫的腳本能夠經過它跑在各個瀏覽器上。而咱們今天就是經過油猴腳本的形式來實現咱們的功能。

對瀏覽器插件開發更感興趣的朋友推薦這篇文章:【乾貨】Chrome插件(擴展)開發全攻略

方案實現

安裝Tampermonkey

Tampermonkey的安裝說簡單也簡單,說不簡單也不簡單,簡單的是Tampermonkey被收錄在各個瀏覽器的官方插件市場,很輕易的就能夠搜索下載,並且不只限於前面提到的那些瀏覽器,一些移動端瀏覽器也是支持的,還有雖然Tampermonkey官網沒寫,可是一些國產瀏覽器插件市場也是有的,好比360極速瀏覽器,搜狗瀏覽器。不簡單的緣由嘛,就是有些瀏覽器的插件市場我們很差訪問。

若是瀏覽器插件市場確實沒有,只要是chromium內核的,一般都支持crx本地安裝。

安裝完成後咱們能夠在瀏覽器上點擊油猴插件圖標,能夠在管理面板中查看,開啓,刪除,管理咱們的腳本,能夠在獲取新腳本中找到幾個腳本市場的網站,直接下載別人寫好的腳本。

建立腳本文件

咱們要本身開發腳本的話,選擇添加新的腳本就能夠打開一個腳本編寫頁面,可是建議仍是在本身編輯器裏寫,有提示和格式化更方便。

建立後的腳本默認有一些代碼以下,就是頭部像是註釋同樣的東西,這裏要用到的簡單說明一下,更多內容能夠參考官方文檔

// ==UserScript==
// @name 掘金文章黑名單 <-插件名,會顯示在管理面板裏
// @namespace https://github.com/hoc2019/blackList <-腳本命名空間
// @version 0.1 <-版本號
// @description 掘金文章黑名單過濾腳本 <-描述
// @author wangzy <-做者
// @match https://juejin.im/* <-只有匹配到的網站纔會使用該腳本
// @require https://cdn.bootcss.com/jquery/3.4.1/jquery.min.js <-引入腳本,這裏用了jQuery
// @grant GM_addStyle <-給網站注入css樣式的方法
// ==/UserScript==

(function() {
 'use strict';

    // Your code here...
})();
複製代碼

文章列表過濾 - MutationObserver觀察dom節點變化

文章列表的過濾隱藏和上面控制檯輸入代碼版原理差很少,重點是觸發隱藏操做的時機。因爲數據異步請求,過早的話文章列表dom節點尚未被建立,過濾操做就會失敗,而過晚的話就會肉眼可見列表的消失。

一個簡單粗暴的方式就是寫一個輪詢,不斷的去檢測文章列表dom節點是否存在,存在則表示文章加載完成,能夠進行過濾操做了。同理滾動到底部加載文章,也可經過輪詢檢測文章列表dom節點數量是否改變來觸發過濾操做(固然也能夠檢測滾輪事件)。可是輪詢自己就是一個在性能和體驗上找平衡的方法,輪詢間隔短了判斷就準確,體驗就好,可是會消耗不少性能,反之則性能好,體驗就差。

因此這裏咱們使用MutationObserver,MutationObserver能夠用來監聽DOM節點的變化,是舊的Mutation Events功能的替代品,詳細內容能夠參考MDN的文檔

大概的使用方式以下:

const container = $('#juejin');
const config = {
    attributes: false,      // 檢測節點屬性變化,這裏用不到,爲減小沒必要要的觸發這裏不用開啓
    childList: true,        // 檢測子節點添加和刪除
    subtree: true           // 檢測包含後代節點
};
const mutationCallback = mutationsList => {
    for (let mutation of mutationsList) {
        const type = mutation.type;
        const addedNodes = mutation.addedNodes;  //增長節點數組
        // 會根據上面的的設置觸發相應事件,這裏能夠判斷觸發的事件類型
        switch (type) {
            case 'childList':
                //由於咱們是要判斷列表加載,只用處理節點增長時的事件便可
                if (addedNodes.length > 0) {
                    list = $('.entry-list');
                    if (list.children().length) {
                        //中止觀察
                        loadObserver.disconnect();
                        //過濾操做
                        filter(list.children());
                        //建立滾動後加載觀察
                        createNodeListener(list[0], config, updateLoad);
                    }
                }
                break;
        }
    }
};
//建立首次加載觀察
const loadObserver = createNodeListener(container[0], config, handleLoad);
//定義一個建立觀察者的工廠函數,方便建立觀察者。
function createNodeListener(node, config, mutationCallback) {
    const observer = new MutationObserver(mutationCallback);
    observer.observe(node, config);
    return observer;
}
複製代碼

因爲是每次文章列表節點發生變化就去處理,因此響應很及時,不太會出現條目先顯示再消失的狀況,並且是被動觸發的,並不會像輪詢同樣,後臺不斷在獲取查詢dom節點信息。

這裏可能會有疑問爲何中間要從新建立一個新的觀察,由於一開始的觀察是基於一個很是頂層的容器(id爲juejin的div節點,一開始只有它存在),文章列表以外的一些其餘DOM節點變化也會觸發事件,因此當咱們拿到文章列表的容器後(class爲entry-list的ul),就中止對頂層容器的觀察改成只觀察文章列表容器,這樣咱們就能夠精確的只響應文章列表節點的變更。

經過MutationObserver構造函數建立完觀察者後不會當即啓動觀察,須要經過觀察者者調用observe方法,須要注意的是observe方法的第一個參數必須是DOM Node (多是一個Element) ,經過jQuery獲取的節點要轉化爲原生dom節點,例如$('#juejin')[0]這樣。

黑名單數據的保存 - localStorage數據存取

黑名單數據存儲在localStorage,一個即便頁面關閉也能夠長期保留數據的方式,具體內容能夠查看MDN的文檔

使用方式很簡單,但要注意的是localStorage存儲的只能是字符串,若是想保存數組和對象須要作一下序列化處理,轉成json字符串。

//存儲
const authorBlackList = ['小四', '小五'];
localStorage.setItem('authorBlackList', JSON.stringify(authorBlackList));

//讀取
const authorBlackList = JSON.parse(localStorage.getItem('authorBlackList'));
複製代碼

存儲後的數據能夠打開瀏覽器調試的Application面板查看,因爲是本地保存瀏覽器清空localStorage就會致使數據清除,跨瀏覽器也是不能共用數據的,服務端存儲的話須要成本,這事仍是交給官方來幹吧。

黑名單管理側邊欄

這個也比較好實現,寫好html,經過jQuery建立節點插入頁面就能夠了,css樣式的話要經過油猴腳本提供的方法GM_addStyle插入,前面有提到過。

大概就是下面這個樣子:

//css樣式
const myScriptStyle ='.black-sidebar{background:#000}'
GM_addStyle(myScriptStyle);

//html結構
const sidebarHtml = '<div class="black-sidebar"><ul><ul></div>';
$('body').append($(sidebarHtml));
複製代碼

可能惟一注意的點就是側邊欄中黑名單列表是後期能夠動態添加的,因此點擊事件不能直接經過jquery的click方法綁定在生成的li標籤上,而是要經過on方法綁定在父級ul上,就和事件委託一個道理。不過這些都是jQuery的用法注意點,不知道新入行的朋友還會不會用到。

不一樣頁面側邊欄數據同步 - visibilitychange事件監聽

黑名單管理側邊欄是一開始就生成的,雖然當前頁的操做會更新到側邊欄,可是一般在瀏覽過程當中咱們是打開多個頁面的,那咱們要如何保持多個頁面側邊欄的數據同步呢?顯然跨瀏覽器標籤頁操做dom是不現實的,可是咱們能夠去監聽一個標籤頁的切換,當該標籤頁爲顯示狀態時,從新獲取一下數據並更新列表。

咱們經過visibilitychange事件來監聽,該事件詳情能夠參考MDN文檔

document.addEventListener('visibilitychange', function() {
    if (document.visibilityState === 'visible') {
        // 更新側邊欄數據
        updateSidebarList();
        if (pathname === 'post') {
            //若是是詳情頁,更新拉黑按鈕狀態
            updateBtnState();
        }
    }
});

複製代碼

效果以下:

上面document.visibilityState是隻讀屬性表示標籤頁狀態,visible表示顯示,還有hidden表示隱藏。因此經過該方法還能夠檢測到標籤頁的隱藏,移動端瀏覽器切換到後臺s,媒體聲音還在繼續播放的問題,能夠經過這個事件手動暫停解決。

文章詳情頁拉黑按鈕

這個也很簡單,不過在作的時候碰到一個有意思的地方,原本想着建立一個和上面分享按鈕同樣的元素,添加一樣的類名就能夠了,可是掘金的樣式裏類選擇器裏還帶了data-v-xxx數據(vue防止css樣式污染使用的scoped特性),因而乾脆直接克隆了一個分享按鈕的節點,再對內容做了修改。

最後

這個插件基本功能已經完成,可是因爲開發的時候目的就是爲了自用,沒有考慮兼容性,也沒有各類狀況下的測試,只是本身跑通能用就行.

其實仍是不少點能夠優化,好比如今添加刪除黑名單後要頁面刷新才能生效,一些dom操做還能夠減小,代碼能夠進一步抽象封裝,面向對象等等。因此這篇文章的目的並非推廣這個插件,而是簡單的分享一下腳本的開發流程,和裏面解決某些問題的思路,能引發某些人關於腳本開發的興趣就足夠了,這也是這個系列文章的目的。

這個腳本的代碼我會放到Github,有需求的能夠用用試試,腳本也已經放到GreasyFork腳本市場上了,能夠直接搜索掘金文章黑名單下載使用,可是後續尚未什麼更新計劃,bug隨緣修復,感興趣的能夠參與共同維護,在官方出這功能以前,我們本身動手,豐衣足食。

腳本倉庫地址 文章備份倉庫地址

相關文章
相關標籤/搜索