Chrome拓展程序hello world

Posted hans774882968

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了Chrome拓展程序hello world相关的知识,希望对你有一定的参考价值。

Chrome拓展程序的好处还用我多说吗

实现的是“阅读清单”。参考:https://blog.csdn.net/qq_24734285/article/details/118409229。参考链接有些小错误,我将在下文指出。

效果图

icon是一个小笑脸,截图没截到。点击icon就能展示了,展示的内容叫做“视图”。

目录结构

icon.png

Chrome拓展程序对项目的目录结构只有一个要求:有manifest.json

manifest.json

{
  "manifest_version": 3,
  "name": "阅读清单",
  "version": "1.0.0",
  "description": "chrome拓展程序hello world",
  "action":
  {
    "default_icon": "images/icon.png",
    "default_title": "chrome拓展程序hello world",
    "default_popup": "popup.html"
  },
  "permissions":
  [
    "tabs",
    "storage",
    "unlimitedStorage"
  ],
  "options_page": "options.html",
  "homepage_url": "https://github.com/Hans774882968/"
}

manifest_version2和3有一些区别。3的action对应2的browser_action,因此在此指出,参考链接的json的键名是错的!这玩意写错了,页面右上角就不能显示这个icon,也不能展示popup.html指定的视图了。

提示:如果想知道这里写错与否,可以点击“更多工具”=>“拓展程序”,在展示的拓展程序列表里找到我们关注的那个,并点击“错误”按钮(如果没有错误,这个按钮不会出现)。

options.html类似游戏里面的settings,这个hello world项目没用到。在manifest.json里删掉这条键值对也行。

popup是”视图“,直接按照老一套经验开发就行。

popup.html

<!DOCTYPE html>
<html lang="en">
<head>
  <meta charset="UTF-8">
  <link rel="stylesheet" type="text/css" href = "./popup.css" />
</head>
<body>
  <!--参考:https://blog.csdn.net/qq_24734285/article/details/118409229-->
  <p id="read-list-num"></p>
  <div id="read-list"></div>
  <div class="btns">
    <button id="add-btn">当前tab加入阅读清单</button>
    <button id="add-all-btn">加入所有当前打开的tab</button>
    <button id="remove-all-btn">清空阅读清单</button>
  </div>
  <script src="popup.js"></script>
</body>
</html>

popup.css

#read-list{
  margin: 16px 0;
  /*如果想实现高度过渡效果,需要用max-height+js实时获取高度*/
}

.read-item{
  margin: 8px 0;
}

button{
  background-color: #6c8cd4;
  color: white;
  border-width: 0;
  cursor: pointer;
}

button:hover{
  background-color: #4769c2;
}

.del-btn{
  border-radius: 4px;
}

.btns {
  min-width: 400px;
  display: grid;
  grid-template-columns: 1fr 1fr;
  column-gap: 8px;
  row-gap: 8px;
}

.btns button{
  padding: 8px 16px;
  border-radius: 8px;
}

popup.js

'use strict';

let utils = (() => {
  let isNullObj = (o) => {
    return !Object.keys(o).length
  }
  return {
    isNullObj
  }
})();

// chrome.storage.sync.remove('tabs')//移除
// chrome.storage.sync.get("tabs", ({tabs}) => console.log(tabs))//获取

;(() => {
  let readListNum = document.getElementById('read-list-num')
  let readList = document.getElementById('read-list')
  let addBtn = document.getElementById('add-btn')
  let addAllBtn = document.getElementById('add-all-btn')
  let removeAllBtn = document.getElementById('remove-all-btn')

  let tabList = []
  let getTabItemId = (offset) => {
    offset = (typeof offset === 'number') ? Math.max(offset, 1) : 1
    if (!tabList.length) return offset
    return tabList[tabList.length - 1].id + offset
  }

  let tabItemHTML = (tabItem) => {
    return `
      <div class="read-item" data-id="${tabItem.id}">
        <a href="${tabItem.url}" target="_blank">${tabItem.title}</a>
        <button class="del-btn">删除</button>
      </div>`
  }

  let readListNumHTML = () => {
    readListNum.innerText = `阅读清单标签个数:${tabList.length}`
  }

  let initHTML = () => {
    let itemsHTML = ''
    tabList.forEach((tabItem) =>
      itemsHTML += tabItemHTML(tabItem)
    )
    readList.innerHTML = itemsHTML
    readListNumHTML()
  }

  let addHTML = (items) => {
    let itemsHTML = ''
    if (Array.isArray(items)) {
      items.forEach((tabItem) =>
        itemsHTML += tabItemHTML(tabItem)
      )
    } else if (items instanceof Object) {
      itemsHTML = tabItemHTML(items)
    }
    readList.innerHTML += itemsHTML
    readListNumHTML()
  }

  chrome.storage.sync.get("tabs", ({tabs}) => {
    if ((!!tabs) && !utils.isNullObj(tabs)) {
      tabList = tabs
    }
    // 补丁:如果存储内容没有id属性,就自己初始化一个
    if (Array.isArray(tabList) && tabList.length && !(tabList[0].hasOwnProperty('id'))) {
      tabList.forEach((tabItem, idx) => tabItem.id = idx + 1)
    }
    console.log('1', tabList)//
    initHTML()
  })

  addBtn.addEventListener('click', () => {
    new Promise((resolve) => {
      chrome.tabs.query({
        active: true,
        windowId: chrome.windows.WINDOW_ID_CURRENT
      }, (tabs) => {
        if (!(tabs && tabs[0])) return
        let tab = tabs[0]
        resolve({url: tab.url, title: tab.title, id: getTabItemId()})
      })
      // 以下api对于popup.html不能正确获取tab
      // chrome.tabs.getCurrent((tab) => {
      //   resolve({url: tab.url, title: tab.title})
      // })
    }).then((newTab) => {
      tabList.push(newTab)
      chrome.storage.sync.set({"tabs": tabList})
      console.log('2', tabList)//
      addHTML(newTab)
    })
  })

  addAllBtn.addEventListener('click', () => {
    new Promise((resolve) => {
      chrome.windows.getAll({populate: true}, (windows) => {
        let newTabs = []
        windows.forEach((window) => {
          window.tabs.forEach((tab) => {
            newTabs.push({url: tab.url, title: tab.title, id: getTabItemId(newTabs.length + 1)})
          })
        })
        resolve(newTabs)
      })
    }).then((newTabs) => {
      tabList = tabList.concat(newTabs)
      chrome.storage.sync.set({"tabs": tabList})
      console.log('3', tabList)//
      addHTML(newTabs)
    })
  })

  // 删除单个标签页
  readList.addEventListener('click', (e) => {
    if (e.target.className !== 'del-btn') return
    const delBtn = e.target
    const id = parseInt(delBtn.parentElement.dataset.id)
    for (let i = 0; i < tabList.length; ++i) {
      if (id !== tabList[i].id) continue
      tabList.splice(i, 1)
      break
    }
    chrome.storage.sync.set({"tabs": tabList})
    console.log('5', tabList)//
    delBtn.parentElement.remove()
    readListNumHTML()
  })

  removeAllBtn.addEventListener('click', () => {
    tabList = []
    chrome.storage.sync.remove('tabs')
    console.log('4', tabList)//
    initHTML()
  })
})()

上面这些增删改查的代码,在逻辑上如果还有问题,就在评论区指出吧

为了达到存储阅读清单的效果,我们使用了chrome提供的一些api。

  • chrome.tabs.query:popup.html用chrome.tabs.getCurrent是不能正确获取当前的tab的,应该用query来获取。
  • chrome.windows.getAll:获取所有打开的标签页。以上两个api是异步的,第二个参数是获取完毕后的回调函数,所以要用Promise包裹。
  • chrome.storage.sync.get、chrome.storage.sync.set、chrome.storage.sync.remove:用来存储数据,和localStorage类似,但有一些使用上的区别。我觉得它的返回值有点反人类……

以上是关于Chrome拓展程序hello world的主要内容,如果未能解决你的问题,请参考以下文章

WPF游览器程序为啥启动时候打开一个world文件而不运行程序?

chrome插件编写之新版hello world

6个变态的C语言Hello World程序 之 雷人的程序语言

ES6函数的拓展

“Hello world”Rust 网络服务器从 Chrome 测量而不是从 curl 测量时比 Node 慢

hello.equals(hello)的代码值是多少?