🐋

Chrome Extension Tutorial

Announcement

原创作者为FaiChou

代码放在了这里

extension名字叫Rettiwt, 是Twitter的反过来写,刚在商店搜到了重名的extension,功能也是一样的(当然比我的丰富多了),先在此做下声明,在我完成这个extension之前,我完全不知道有一个重名的extension。

本文仅仅是一个教程,demo并没有上传到商店,而且并没有上传商店的教程,如果想上传商店,请参考官方文档。

Before We Start

一直好奇chrome extension是如何开发的,就像Adblock这样的extension好像做起来挺偏僻的,因为没有多少人会靠开发extension赚钱,所以开发extension很多都是希望能够解决上网冲浪遇到的问题,比如屏蔽广告,比如修改网页展示等。再一个造成开发人员少的原因是学习成本,如果我们会python,那么直接可以拿来写一个爬虫,但是你如果会js,想要直接写extension,还是会遇到很多困难,因为你要按照chrome文档上标准来写,还需要学习一套新的chrome.*API,万幸的是API写的挺js规范的,so,我们开始吧!

Target

清除Twitter两侧dashboard栏,达成Ins效果

First

通过开发者工具,可以查到Twitter首页两侧的dashboard栏是通过class="dashboard"定位的,所以思路犹如泉涌,只要写个css干掉dashboard就OK了。

  1. 通过设置display: none
  2. 通过设置visibility: hidden

实验发现,当设置display: none时候,中间的content就会跑到左边,这显然非常糟糕,所以采用第二种,只是隐藏掉它,而dashboard仍然存在于原来的位置,只是显示不出来。

Second

建立文件们如下(jquery-3.2.1.min.js不需要)

这里面最重要的是manifest.json,它是整个extension的配置文件,整合了所有资源、权限、版本、名称、描述等。我的manifest文件内容是这样的:

{
  "name": "Rettiwt",
  "description": "Clear twitter dashboard",
  "version": "1.0",
  "background": {
    "persistent": false,
    "scripts": ["background.js"]
  },
  "permissions": ["storage", "activeTab"],
  "icons": { "128": "rettiwt.png" },
  "browser_action": {
    "default_icon": "rettiwt.png",
    "default_title": "Rettiwt"
  },
  "content_scripts": [
    {
      "matches": [
        "https://twitter.com/*",
        "http://twitter.com/*"
      ],
      "js": ["jquery-3.2.1.min.js", "content.js"]
    }
  ],
  "manifest_version": 2
}

这里的manifest_version必须填2,因为1号版本在很久之前chrome就弃用了。

里面的ICON、title、name、description、version照着填就可以,没什么讲究,不满意随便改。

permissions是你的extension需要访问哪些权限

这里的因为要切换隐藏、展示功能,需要存储它的状态,所以保存在localStorage中。

这里要多说一点,每个web都可以有自己的localStorage

咱的extension虽然限定在Twitter上使用并存储localStorage,但是它存的地方并不是和Twitter一个,在chrome可视化界面中,我没找到它存在什么地方,但是路径中是有的,一个单独的localStorage。

background是我们很重要的组成部分,默默在后台监视着我们的一举一动,所以我们可以起一个监听,当我们点击我们的extension图标,就会callback:

chrome.browserAction.onClicked.addListener(function(tab) {
  // Send a message to the active tab
});

content_scripts是主要的工作者,可以匹配我们的网站,当我们限定它工作网站是推特时候这样写:

"matches": ["https://twitter.com/*","http://twitter.com/*"]

background和contentscripts谷歌给了不同的权限,所以就分开工作了,我们在background中监听用户点击,当用户点击我们发一个消息给contentscripts,让它执行一部分代码。

当然也可以在background中直接执行一些代码,比如下面的改变背景颜色:

chrome.browserAction.onClicked.addListener(function(tab) {
  // change background color
  chrome.tabs.executeScript({
    code: 'document.body.style.backgroundColor="#F9FAFC"'
  });
  // send message to content_scripts
  chrome.tabs.query({active: true, currentWindow: true}, function(tabs) {
    console.log(tabs);
    var activeTab = tabs[0];
    chrome.tabs.sendMessage(activeTab.id, {"message": "clicked_browser_action"});
  });
});

Third

当Twitter网页加载完成之后,content_scripts里的js就会执行一遍,所以可以在这里面设置一些初始化,监听一些事件:

initialWork();

chrome.runtime.onMessage.addListener(
  function(request, sender, sendResponse) {
    if( request.message === "clicked_browser_action" ) {
      // do something
    }
  }
);

chrome.browserAction.onClickedchrome.runtime.onMessage这些都是chrome.*API,可以在文档中查阅,没必要都先学习一遍,拿来即用。

整体的思路就是,页面加载完成肯定是有dashboard的,所以初始化时候设置一个为true的变量dashExist保存在localStorage中,当点击extension,获取dashExist值,true的话,执行switchDashboard方法,来隐藏掉dashboard,再将dashExist设置为false,反之亦然。

初始化工作:

var storage = chrome.storage.local;
const dashExist = true;
pageDidLoad();
function pageDidLoad() {
  storage.set({dashExist}, function() {
    console.log('Settings saved');
  });
}

切换工作:

function switchDashboard(dashExist) {
  if (dashExist) {
    for (let el of document.querySelectorAll(".dashboard"))
      el.style.visibility = "hidden";
    storage.set({'dashExist': false}, function() {
      console.log('close success!');
    });
  } else {
    for (let el of document.querySelectorAll(".dashboard"))
      el.style.visibility = "visible";
    storage.set({'dashExist': true}, function() {
      console.log('open success!');
    });
  }
}

document.querySelectorAll(".dashboard")获取dashboard,返回一个数组,遍历设置每个style.visibility。

chrome.storage.local的set和get方法来设置和获取localStorage值,这是一个异步的操作,因为extension并不希望阻塞网页。

监听里的方法:

chrome.runtime.onMessage.addListener(
  function(request, sender, sendResponse) {
    if( request.message === "clicked_browser_action" ) {
      storage.get('dashExist', function(items) {
        console.log(items.dashExist);
        switchDashboard(items.dashExist);
      });
    }
  }
);

Last

最后,所有done了,就可以把它搞到我们的chrome中了!!

首先要在chrome://extensions中打开开发者模式,再将我们的文件夹拖到extensions这个页面,也可以通过加载已解压的扩展程序按钮来导入。

现在在Twitter刷新下试下,加载完成点按我们的extension,是不是很爽呢?

Others

就像Adblock是如何实现插入代码到html呢?这并不困难

function injectScript() {
  var script = document.createElement('script');
  script.textContent = `console.log('shittt!!');`;
  (document.head || document.documentElement).appendChild(script);
  // script.remove();
}

Reference


在我们一生中,命运赐予我们每个人三个导师,三个朋友,三名敌人,三个挚爱。但这十二人总是不以真面目示人,总要等到我们爱上他们、离开他们、或与他们对抗时,才能知道他们是其中哪种角色。