程序員偷懶指南 -- 用chrome插件實現前端資訊推送

最近接觸了一下 chrome 擴展,也就是咱們常說的插件,發現這確實是一個好東西,使用簡單的 HTML,CSS 和 JavaScript 就可爲瀏覽器新增咱們想要的功能,而且,使用 chrome 擴展開發,不用擔憂跨域等消息傳遞問題,還有討厭的兼容性問題,結合操做用戶頁面 dom,開發的開心度可謂是可觀的。下面,咱們就一塊兒經過一個實例來看看 chrome 擴展開發。html

這個實例不是很複雜,是我以前一直想作的,就是 經過 chrome 擴展來同步每日閱讀的不錯的前端資訊。相信你們也有清晰地感覺到,前端社區在諸多社區中可謂最爲活躍的,因此,堅持篩選學習的內容是比較考驗耐心,也是尤其重要的,相對微信公衆號,twitter 和 newsletter 等方式,我的感受 chrome 擴展也不失爲一種友好的方式。前端

構思

首先,咱們得準備一個倉庫來儲存咱們每日收集的資訊,最好你們也能貢獻,這個好像不用想,沒有比 GitHub 更爲合適了的吧,而且 GitHub 提供的 REST API 應該能夠幫助咱們解決一些額外的問題,查了查果真:git

POST /markdown
複製代碼

能夠將 text 渲染成 Markdown 文檔,測試一下:github

await fetch(
	'https://api.github.com/markdown',
	{
   	method: 'POST',
		body: JSON.stringify({
	  	text: '> # hello fengshangwuqi',
    })
  }
)
.then(res => res.text());
複製代碼

返回:web

"<blockquote>
<h1>
<a id="user-content-hello-fengshangwuqi" class="anchor" href="#hello-fengshangwuqi" aria-hidden="true"><span aria-hidden="true" class="octicon octicon-link"></span></a>hello fengshangwuqi</h1>
</blockquote>
"
複製代碼

很好,這樣,咱們就能夠將獲取到的 text 數據經過該 API 轉化爲 markdown,而後操做 dom,添加進點擊擴展圖標彈出的小窗口的 document 中,再用 CSS 修改下樣式,增長一些切換資訊等功能,初始版本應該算完成了吧。如今想一想感受較簡單,不過,在實際演練的過程當中,應該會發散一些問題。chrome

因而,當即建立了倉庫 Daily-Front-End-Newsjson

準備

書寫配置文件 manifest.json

先從 chrome 開發文檔 複製配置模板:api

{
  "manifest_version": 2,

  "name": "One-click Kittens",
  "description": "This extension demonstrates a browser action with kittens.",
  "version": "1.0",

  "permissions": [
    "https://secure.flickr.com/"
  ],
  "browser_action": {
    "default_icon": "icon.png",
    "default_popup": "popup.html"
  }
}
複製代碼
  • nameversion 是必須的;
  • Chrome 應用的開發者當前必須指定 'manifest_version': 2
  • permissions 聲明須要申請的權限,好比訪問瀏覽器選項卡(tabs)、瀏覽器通知(notifications)等,能夠根據須要添加,目前,咱們的實例主要是從 GitHub 獲取數據並展現數據,暫時不須要申請什麼 permissions;
  • browser_action 指定擴展的圖標放在 Chrome 工具欄中,它定義了擴展圖標文件位置(default_icon)、懸浮提示(default_title)和點擊擴展圖標所顯示的頁面位置(default_popup);
  • background 是一個長時間運行的腳本,在擴展的整個生命週期都存在,用於管理一些任務和狀態;
  • options_page 屬性定義了擴展的設置頁面,配置後在擴展圖標點擊右鍵能夠看到選項 ,點擊即打開指定頁面;
  • content_scripts 則是直接注入頁面的腳本。

咱們要從 GitHub 獲取數據,並在 popup.html 中展現出來,須要申請 webRequest 權限,用於點擊圖標彈出的窗口和背景頁之間的數據傳遞,並添加須要的 background 和 popup 腳本,其中,background 腳本在配置文件中添加,而 popup 腳本經過 <script> 標籤在 popup.html 引入。跨域

消息傳遞

當咱們點擊圖標,打開 popup.html 時,popup 腳本會去從 背景頁 獲取須要的數據,這裏就涉及到內容腳本與擴展其他部分之間的通訊。chrome 擴展的 數據傳遞 方式有多種,好比 簡單的一次性請求長時間的鏈接跨擴展程序消息傳遞 等,目前咱們用到的應該只有一次性請求。瀏覽器

一次性請求即便用簡單的 runtime.sendMessage 方法,向擴展的另外一部分發送消息,而且它提供可選的 callback 處理迴應。以下,一條消息就在 popup 發送出去了:

chrome.runtime.sendMessage(
  {
    action: 'getNew',
  },
  res => {
    console.log(res);
  }
);
複製代碼

在背景頁,咱們使用 runtime.onMessage 事件監聽器來處理消息。以下,咱們能夠迴應來自 popup 的 getNew 請求:

async function handleMessage(message, sender, sendResponse) {
switch (message.action) {
    case 'getNew':
      sendResponse(await getNew());
      break;
    default:
      sendResponse(false);
  }
}

chrome.runtime.onMessage.addListener((message, sender, sendResponse) => {
  handleMessage(message, sender, sendResponse);
  return true;
});
複製代碼

其中,sendResponse() 即背景頁的迴應,而且,某一次事件只有第一次調用 sendResponse() 有效,全部其餘迴應將被忽略,return true 是用於處理咱們的異步請求,若是刪除,異步請求仍能正常發送,但返回給 popup 的數據將再也不是請求獲得的數據。

使用 REST API 處理數據

在上面,咱們已經發現了一個不錯的 API 能夠幫助咱們渲染 markdown,接下來咱們只需專一地思考如何獲取數據。

固然,獲取數據確定不止一次 POST 請求那麼簡單,由於,實例若不支持查看以前的資訊,即好比切換到昨天的資訊,那這個實例意義也不大。首先,仍是先查看一下 REST API 是怎麼獲取數據的。在 Repositories 的 Contents 目錄下,咱們發現有這樣一個請求:

GET /repos/:owner/:repo/contents/:path
複製代碼

若是這個 contents 是一個文件,它將返回下面這樣的一個對象:

{
  "type": "file",
  "name": "README.md",
  "path": "README.md",
  ...
}
複製代碼

而若是這個 contents 是一個目錄,它將返回:

{
    "type": "dir",
    "name": "fileName",
    "path": "folder/fileName",
    ...
  }
複製代碼

哈哈,這下咱們就能夠放心大膽地在 GitHub 存放數據了。咱們能夠將咱們每日的資訊都記錄在 README 中,而後將以前資訊按照日期放在對應的目錄下,最終目錄結構像這樣:

history
├── 2018
│   ├── 03
│   │   └── 04
│   │   |   ├── README.md
│   │   └── 05
│   │       ├── README.md
複製代碼

而後以下獲取最新資訊的 path:

const year = await GITHUB.getContent('history');
const month = await GITHUB.getContent(
  `history/${year[year.length - 1]}`
);
const day = await GITHUB.getContent(
  `history/${year[year.length - 1]}/${month[month.length - 1]}`
);
const path = `${year.pop()}/${month.pop()}/${day.pop()}`;
複製代碼

path 獲得了,接下來,就可輕鬆經過上面的 API 獲取對應的 content,至此,經過 REST API 處理數據算是搞定了。下面是示例代碼:

class API {
  constructor(url, owner, repo) {
    this.url = url;
    this.owner = owner;
    this.repo = repo;
  }

  /* github * Render an arbitrary Markdown document * */
  async getMarkdown(text) {
    const res = await fetch(`${this.url}/markdown`, {
      method: 'POST',
      body: JSON.stringify({
        text,
      }),
    }).then(res => res.text());

    return res;
  }

  /* github * Get contents * */
  async getContent(path) {
    const res = await fetch(
      `${this.url}/repos/${this.owner}/${this.repo}/contents/${path}`,
      {
        method: 'GET',
      }
    )
      .then(res => res.json())
      .then(json => json.map(path => path.name));

    return res;
  }
}
複製代碼

上面使用 class 對 API 作了一個封裝,並不是徹底出於對代碼的整潔,還有一個重要的緣由是避免重名,雖然擴展支持根據域名加載不一樣 js 文件,但若是有隱藏域名的需求也說不許了。

打通 background 和 popup

接下來的問題主要是處理 popup 給 background 發送消息獲取對應的數據,在上面的準備中,其實咱們已經搞定的差很少了。

思路相對也很清晰,首先,popup 發送消息獲取 paths,即全部能夠查看的資訊的路徑,這個路徑,至關於每條資訊的 title,上面咱們已經知道如何獲取最新資訊的 path 了,咱們只需作一個簡單的操做和判斷,咱們將最新資訊的 path 存儲在 localStorage 中,若是 localStorage 中最新資訊的 path 不全等於實際最新資訊的 path,那麼咱們就執行請求獲取實際最新資訊的 path。

看似不使用 localStorage 感受也沒什麼,實際上,是不得不使用 localStorage。GitHub REST API 有一個 Rate Limit,它限制了每小時你發送請求的次數,顯然,不使用 localStorage 儲存,結果將是比較尷尬的,你將在屢次打開 popup 後,獲得一個尷尬的提示:

{
   "message": "API rate limit exceeded for xxx.xxx.xxx.xxx. (But here's the good news: Authenticated requests get a higher rate limit. Check out the documentation for more details.)",
   "documentation_url": "https://developer.github.com/v3/#rate-limiting"
}
複製代碼

緊接着是發送一個新的消息獲取當前想要查看的資訊,消息迴應的有資訊的 content,還有資訊的 path,資訊的 content 不用說,確定是動態地建立 dom 添加到 popup 中,而 path 主要用於切換,以前咱們獲得了全部可查看資訊的路徑,接下來,咱們就能夠結合 paths 和 path 決定是否能夠向前或向後切換,切換核心代碼以下:

NewsCard.getCurrNew = function (path) {
  chrome.runtime.sendMessage({
      action: 'getCurrNew',
      path,
    },
    res => {
      const card = document.getElementById('new-card');

      card.innerHTML = `<div id="path">${res.path}</div> <div id="left-arrow" class="card-arrow"><<<</div> <div id="right-arrow" class="card-arrow">>>></div> ${res.text}`;

      NewsCard.addListeners();
    }
  );
}
複製代碼

調試

popup 跟普通頁面同樣,右鍵檢查,background 須要進入擴展程序頁面,點擊檢查視圖背景頁。

總結

就這樣,咱們的 chrome 初始版本就沒有什麼阻塞項了,目前截圖以下:

該實例還存在不少 TODO 的內容:

  • 更換 icon;
  • 優化 UI;
  • 限制切換次數,優化儲存;
  • 添加新內容,例如來源,做者等信息;
  • 添加新功能,例如查看所有等;
  • 發佈;
  • ...

目前該擴展處於內測階段,還沒有發佈,若是大夥中有人有興趣,chrome-Daily-Front-End-news 倉庫期待收到你的 PR,Daily-Front-End-News 同上。

相關文章
相關標籤/搜索