# 2021年6月23日Chrome插件开发快速入门


#### 本节内容案例仅供学习，下载后请在24小时内删除

[代码地址](http://gitlab.dui88.com/haiyoucuv/qiuxu_code/tree/%E9%82%B1%E6%97%AD_%E7%99%BE%E5%BA%A6%E5%B9%BF%E5%91%8A%E5%B1%8F%E8%94%BD%E5%99%A8_20210623)

## 本讲内容

- 1.了解Chrome插件开发
- 2.详细介绍Chrome插件manifest清单配置
- 3.注入css实战（让网页变灰）
- 4.注入JS脚本实战（注入并弹出alert提示）
- 5.分析百度广告，编写插件删除广告

## 1.如何开发一款Chrome插件

![img.png](./img/img.png)

Chrome将会把manifest.json文件作为插件的描述文件，文件夹里包含该文件将被认为是插件

## 2.详解manifest文件

```json5
{
  "manifest_version": 2,
  // 清单版本号
  "name": "插件名称",
  "version": "1.8.6",
  // 版本号
  "description": "描述",
  "author": "作者",
  "icons": {
    // 图标
    "16": "xxx.png",
    "48": "xxx.png",
    "128": "xxx.png"
  },
  // browser_action 和 page_action 只能添加一个
  "browser_action": {
    // 浏览器行为，所有页面均生效
    "default_icon": "xxx.png",
    // 图标的图片
    "default_title": "标题",
    "default_popup": "html/popup.html"
    //单击图标后弹窗页面
  },
  "page_action": {
    // 页面行为，只在特定页面下生效
    "default_icon": {
      "24": "xxx.png",
      "38": "xxx.png"
    },
    "default_popup": "xxx.html",
    "default_title": "页面标题"
  },
  "background": {
    // 后台脚本，可简单理解为服务器
    "scripts": [
      "xxx.js",
    ]
  },
  "devtools_page": "xxx.html",
  "content_scripts": [
    // 注入的脚本，css等
    {
      "js": [
        "xxx.js"
      ],
      "css": [
        "xxx.css"
      ],
      "matches": [
        "<all_urls>"
      ]
      // 只在这些站点下生效 <all_urls> 表示全部
    }
  ],
  "permissions": [
    // 所需要的权限
    "cookies",
    "http://*/*",
    "management",
    "tabs",
    "contextMenus"
  ]
}
```

比较重要的几项有以下几项：  
`content_scripts`：描述需要注入的文件，并描述在哪些网页下注入，何时注入；
`browser_action`、`page_action`：可以创建窗口，在窗口创建页面，browser_action是浏览器行为，page_action是页面行为，这两个只能存在一个;  
`devtools_page`：可以在devtools里创建窗口，并且可以使用Chrome提供的devtools API；  
`background`：可将其简单理解为服务器，可以用作通信等，如devtools和注入脚本的通信；

## `content_scripts`

```json5
{
  "content_scripts": [
    {
      "js": [
        "xxx.js"
      ],
      "css": [
        "xxx.css"
      ],
      "run_at": "document_end",
      "matches": [
        "https://*.baidu.com/*"
      ]
    }
  ]
}
```

| 字段名 | 含义 |
| :---: | :---: |
| js | 要注入的js文件 |
| css | 要注入的css文件 |
| run_at | 在什么时候注入<br/>document_end<br/>document_start<br/>document_idle |
| matches | 只在这些规则的URL下生效<br/><all_urls>表示在所有网站下都生效 |

## 创建一个插件

- 新建一个文件夹  
  在文件夹内创建style.css文件和index.js文件  
  在文件夹内创建manifest.json文件，内容如下：

```json
{
  "manifest_version": 2,
  "name": "百度广告屏蔽器",
  "version": "0.0.1",
  "description": "屏蔽百度广告",
  "icons": {
  },
  "author": "你的名字",
  "content_scripts": [
    {
      "js": [
        "index.js"
      ],
      "css": [
        "style.css"
      ],
      "run_at": "document_end",
      "matches": [
        "https://*.baidu.com/*"
      ]
    }
  ]
}
```

- 打开Chrome -> 菜单 -> 更多工具 -> 扩展程序

![img.png](img/img1.png)

- 打开右上角开发者模式，然后将你的插件文件夹直接拖进去，就安装好了

![img.png](img/img2.png)


- 注意，每修改一次代码需要在这里刷新一次，不然Chrome不会重新加载

## 完成注入css实战，让网页变灰

在style.css中写入如下内容

```css
body {
  filter: grayscale(1);
}
```

刷新重新加载插件文件，并打开 [百度](https://www.baidu.com/) 可以看到整个网页都变灰了

![img.png](img/img3.png)

## 完成注入JS脚本实战，注入并弹出alert提示

在index.js中写入如下内容

```javascript
alert("插件生效");
```

刷新网页，看到弹出提示

![img.png](img/img4.png)

## 分析百度广告，实现屏蔽功能

![img.png](img/img5.png)

随意搜索一些内容，发现在搜索页的广告都是在一个id叫`content_left`下的第一个div里  
并且这个div没有id属性，而正常的搜索结果有id属性

则可以在插件中写入如下代码：

```javascript
const content_left = document.getElementById("content_left");
if (content_left && content_left.children > 0 && !content_left.children[0].id) {
  content_left.children[0].remove();
}
```

达到屏蔽搜索结果广告的目的

## 动态加载的广告如何去除

随意打开百度百科的一个词条，发现右边有一些烦人的广告，和我们的搜索的结果毫无关系

![img.png](img/img6.png)

使用同样的方法去分析这两个广告 右侧图片广告为className叫`right-ad`的元素 右侧词条广告为id是`side_box_unionAd`的元素

写入代码将其移除

```javascript
// 百科右侧图片广告
const right_ad = document.getElementsByClassName("right-ad");
if (right_ad[0]) {
  right_ad[0].remove();
}

// 百科右侧恶心的广告
const side_box_unionAd = document.getElementById("side_box_unionAd");
if (side_box_unionAd) {
  side_box_unionAd.remove();
}
```

发现并没有效果，是因为这两个广告都是动态载入的，而我们的js脚本是在document创建结束的时候就已经执行了

对于这两种广告，我们可以监听dom变化，来完成移除

以下是两种监听dom变化的方法，
`DOMSubtreeModified`事件在DOM2中就已经提供
`MutationObserver`在DOM3中被写入浏览器标准API

```javascript
// 监听dom结构变化 DOM2
document.body.addEventListener("DOMSubtreeModified", () => {
  console.log("DOMSubtreeModified");
});

// 监听dom结构变化 DOM3
const observer = new MutationObserver(() => {
  console.log("MutationObserver");
});

observer.observe(document.body, {
  childList: true,
  // attributes: true,
  subtree: true,
});
```

我们使用`MutationObserver`来监听dom树的变化

将`childList`和`subtree`设置为`true`，表示监听子节点及其后代的结构变化

在创建`MutationObserver`时传入回调函数

在回调函数中传入移除广告的方法

重载插件并刷新百科页面，发现广告已经被移除了

```javascript
/**
 * 通过监听dom树的变化，移除异步加载的的广告
 */
function removeAdAsync() {
  // 百科右侧图片广告
  const right_ad = document.getElementsByClassName("right-ad");
  if (right_ad[0]) {
    right_ad[0].remove();
  }

  // 百科右侧恶心的广告
  const side_box_unionAd = document.getElementById("side_box_unionAd");
  if (side_box_unionAd) {
    side_box_unionAd.remove();
  }
}

// 监听dom结构变化 DOM2
// document.body.addEventListener("DOMSubtreeModified", removeAdAsync);


// 监听dom结构变化 DOM3
const observer = new MutationObserver(removeAdAsync);

observer.observe(document.body, {
  childList: true,
  // attributes: true,
  subtree: true,
});
```

## 新的思考

提供三个视频网站移除播放广告的方法  
大家可以尝试制作相关插件

爱奇艺视频广告

```javascript
$(".skippable-after").click()
```

腾讯视频广告

```javascript
$(".txp_ad").find("txpdiv").find("video")[0].currentTime = 100;
$(".txp_ad").find("txpdiv").find("video")[1].currentTime = 100;
```

优酷视频广告

```javascript
$(".h5-ext-layer").find("div").remove();
$(".control-play-icon").click();
```
