chrome插件开发初体验

412 阅读5分钟

        公司内部的邮件不支持复制内容,但很多流程单的内容格式调整起来很麻烦。为了解决一下工作流程中的小麻烦。我便开始了第一次开发chrome插件之旅,记录一下开发过程中所学习的知识、遇到的问题及解决办法。

一、定义

        chrome插件,严格来说应该叫做Chrome扩展(Chrome Extension),是一个用Web技术开发、用来增强浏览器功能的软件,它其实就是一个由HTML、CSS、JS、图片等资源组成的一个.crx后缀的压缩包。

        Chrome提供了很多实用API供开发者使用,包括书签控制、下载控制、窗口控制、标签控制、网络请求控制、各类事件监听、自定义原生菜单、完善的通信机制等。

        另外,Chrome插件还可以配合dll动态链接库(PPAPI)实现一些更底层的功能,例如全屏幕截图。

二、开发与调试

        从右上角菜单->更多工具->扩展程序可以进入 插件管理页面,也可以直接在地址栏输入 chrome://extensions 访问。

        打包可以通过chrome扩展管理界面打包和加载,打包扩展程序会将项目文件夹打包为crx后缀文件 以前的chrome浏览器在打开开发者模式下,直接拖入crx即可安装扩展,现在没有上架chrome扩展商店的应用无法安装。勾选开发者模式即可以文件夹的形式直接加载插件;

三、核心组成

1、manifest.json清单文件

        提供有关扩展程序的各种信息。

1.png

2、popup

        popup是点击browser_action或者page_action图标时打开的一个小窗口网页,焦点离开网页就立即关闭,一般用来做一些临时性的交互。

        popup可以包含任意你想要的HTML内容,并且会自适应大小。可以通过default_popup字段来指定popup页面,也可以调用setPopup()方法。

         需要特别注意的是,由于单击图标打开popup,焦点离开又立即关闭,所以popup页面的生命周期一般很短,需要长时间运行的代码千万不要写在popup里面。

3、content-scripts

        content-scripts,其实就是Chrome插件中向页面注入脚本的一种形式(虽然名为script,其实还可以包括css的)。

        content-scripts和原始页面共享DOM,但是不共享JS。

4、background

        background是一个常驻的页面,它的生命周期是插件中所有类型页面中最长的,它随着浏览器的打开而打开,随着浏览器的关闭而关闭,所以通常把需要一直运行的、启动就运行的、全局的代码放在background里面。

        background的权限非常高,几乎可以调用所有的Chrome扩展API(除了devtools),而且它可以无限制跨域,也就是可以跨域访问任何网站而无需要求对方设置CORS。

四、通信

        content_scripts、background、popup三者可以通过chrome.runtime.onMessage、chrome.runtime.sendMessage来通信。chrome.tabs.query可以拿到当前激活态的tab页面。示例如下:

2.png

        PS:content_scripts向popup主动发消息的前提是popup必须打开!否则需要利用background作中转;

        PS:如果background和popup同时监听,那么它们都可以同时收到消息,但是只有一个可以sendResponse,一个先发送了,那么另外一个再发送就无效;

五、存储API

1、功能

        Chrome 为扩展应用提供了存储 API,以便将扩展中需要保存的数据写入本地磁盘,它与 localStorage 相比有以下区别:         ①用户数据可以通过 Chrome 浏览器的同步功能自动同步(使用 storage.sync)         ②content_scripts 可以直接读取数据,而不必通过 background 页面         ③在隐身模式下仍然可以读出之前存储的数据         ④读写速度更快         ⑤用户数据可以以对象的类型保存(localStorage API 以字符串方式存储数据)。         对于第二点要进一步说明一下。首先 localStorage 是基于域名的。而 content_scripts 是注入到用户当前浏览页面中的,如果 content_scripts 直接读取 localStorage,所读取到的数据是用户当前浏览页面所在域中的。         所以通常的解决办法是 content_scripts 通过 runtime.sendMessage 和 background 通信,由 background 读写扩展所在域(通常是 chrome-extension://extension-id/)的 localStorage,然后再传递给 content_scripts。

2、用法

        Chrome 存储 API 提供了 2 种储存区域,分别是 sync 和 local。两种储存区域的区别在于,sync 储存的区域会根据用户当前在 Chrome 上登陆的 Google 账户自动同步数据,当无可用网络连接可用时,sync 区域对数据的读写和 local 区域对数据的读写行为一致。

3.png

六、开发中遇到的问题

        1、开发chrome插件中实现复制内容到剪贴板,控制台执行报错 DOMException: Document is not focused。

        原因及解决办法:在chrome中使用navigator.clipboard 。为了防止滥用,只有当页面处于活动选项卡时才允许剪贴板访问。当在popup页面中点击“复制”按钮时,当前活动的页面就已经变成了popup,而非聚焦到原始页面。所以需要将剪贴板复制操作放在popup.js里。

        2、content_scripts与popup之间通信,返回值一直为空。         原因及解决办法:chrome.runtime.sendMessage的回调函数默认是同步的,而且超时后直接执行,返回undefined,如果要异步执行,必须在处理函数中return true。         chorme.storage的读取数据有一定延时,会导致content_scripts通过 runtime.sendMessage 和 popup 通信时,content_scripts还没读取到storage的数据,就已经sendResponse()了,此时返回值为空。在sendResponse()后面增加一句return true;来显示申明你要异步回调response函数,即可解决这个问题。