博客 谷歌插件开发探索及其应用

谷歌插件开发探索及其应用

   数栈君   发表于 2023-02-14 14:07  192  0

前言


笔者之前一直想了解一些关于谷歌插件的相关知识,希望通过谷歌插件可以更好的认识到谷歌的调试工具,同时也想着可以使用谷歌插件去写一些小工具,既学习了新的东西,又有一定的趣味性。恰逢近段时间需要分享,因此花了两周时间学习和了解谷歌插件相关知识,本篇文章就将笔者在学习过程中的一些思考分享给大家。当然,因为时间的原因,如果笔者对于这一块的认识有不对的地方,欢迎批评指正~


什么是谷歌插件


谷歌插件,全名谷歌浏览器扩展程序,官方说明如下:

扩展程序允许您为 Chrome 浏览器增加功能,而不需要深入研究本机代码。您可以使用您在网页开发中已经很熟悉的核心技术(HTML、CSS 与 JavaScript)为 Chrome 浏览器创建新的扩展程序。

有同学可能会疑惑:为什么又叫做谷歌插件?那这就正如鲁迅所说的那句话:世上本没有路,走的人多了,也就有路了。谷歌浏览器扩展程序本来并不是谷歌插件,谷歌插件其实是浏览器更为底层的东西,奈何叫的人太多了,谷歌浏览器扩展程序也就成了“谷歌插件”。本文也使用谷歌插件来统称谷歌浏览器扩展程序。


谷歌插件的基本使用


下面先介绍一下谷歌插件的主要组成部分,因为目前谷歌插件使用比较普遍的版本为 2.0 版本,因此本文都是基于 2.0 版本进行使用说明,3.0 版本相较于 2.0 版本更为简便,感兴趣的同学可以点击文章末尾处的链接了解更多相关知识。


(一)配置文件

谷歌插件的核心文件就是配置文件--manifest.json(清单)文件。

其中,manifest.json 文件最基本的 Api 如下:

{    "name": "chrome extension",    "version": "1.0.0",    "manifest_version": 2,    "description": "A litlle chrome extension demo"}


主要是包含所写谷歌插件的名称、版本以及相关描述,其中 manifest_version 表示清单文件版本。

manifest.json 作为谷歌插件的核心部分,笔者认为该文件对插件来说就相当于一个入口配置文件,开发人员只需要在这个文件通过配置相应的 js,调用谷歌浏览器提供的 Api,就能实现达到完善这个插件的目的。


1、基本使用Api

在清单文件中有 许多的Api,笔者就不一一列举了,下面为大家介绍几个笔者认为比较重要的 Api,通过以下几个 Api 的介绍,希望可以使读者对于谷歌插件的开发过程有一个大概的认识。


1)browser_action

{    ...
"browser_action": { "default_icon": { "16": "images/get_started16.png", "32": "images/get_started32.png" }, "default_title": "谷歌划词翻译", "default_popup": "popup.html" },
...}

browser_action可设置浏览器右上角的图标及名称。
default_popup 可配置点击图标后会出现的一个小窗口,这里可以做一些临时性的操作


2)permissions

{    ...
"permissions": [ "activeTab", "storage", "tabs", "contextMenus" ], ...}

permissions 可配置谷歌插件权限申请,如 contextMenus(右键菜单)、 tabs(标签)和storage(插件本地存储)。

3)content_scripts
{    ...
"content_scripts": { "matches": ["<all_urls>"], "css": ["content/content_script.css"], "js": ["content/content_script.js"] }, ...}

content-scripts其实就是谷歌插件中向页面注入脚本的一种形式(虽然名为 script,其实还可以包括 CSS 的),借助 content-scripts 可以实现通过配置的方式轻松向指定页面注入 JS 和 CSS。

4)background

{    ···
"background": { "scripts": ["background.js"], "persistent": false },
···}

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

笔者也画了一个上面涉及到的脚本在浏览器中的分布,如下图:

http://dtstack-static.oss-cn-hangzhou.aliyuncs.com/2021bbs/files_user1/article/664d368f05f51cc272a667694f994369..jpg


以上就是笔者认为比较重要的一些Api,在介绍完之后,感兴趣的同学就可以开始着手写几个简单的工具,用来实现自己远大的抱负以及理想,升华自己高贵的灵魂。


一些同学可能不太知道开发谷歌插件的前置条件,在这里为大家简单介绍下。

首先需要打开管理扩展程序,打开开发者模式。
点击加载已解压的程序按钮即可加载本地谷歌插件,开发的时候代码如果有更新的话,需要刷新已加载插件,点击关闭后再开启,不必刷新开发页面。


在了解完前置条件后,笔者将在下文中为大家分享谷歌划词翻译插件从0-1的实现过程,通过开发这个工具也可以加深对于大家谷歌插件的认识。


谷歌划词翻译插件


谷歌翻译算是笔者使用比较频繁的插件,对于在网页上看到的不懂的英文单词或者句子,直接使用鼠标选中,轻松快捷的翻译出相应的中文。因此在学习的过程中,笔者就在想谷歌浏览器插件的翻译工具是如何实现的呢?


(一)思考

如何去做一个划词翻译插件,首先要考虑的有以下几点:
  • 如何实现翻译效果
  • 如何选中我们需要的元素
  • 选中元素之后如何展示划词翻译面板
  • 所有的浏览器 Tab 都需要支持翻译效果
思考完上面的这些点后,带着这几个疑惑,笔者将在下文一一解答,同时也列举一下遇到的一些点。


(二)划词翻译面板

首先不去考虑该插件的功能,先写下划词翻译的面板的样式,所达到的效果如下:

http://dtstack-static.oss-cn-hangzhou.aliyuncs.com/2021bbs/files_user1/article/adcba34cb647ddfb9f48c286c5a54e32..jpg

HTML 代码如下:
<div class="translate-panel show">    <header>谷歌划词翻译插件<span class="close">X</span></header>    <main>        <div class="source">            <div class="title">英文</div>            <div class="content">test</div>        </div>        <div class="result">            <div class="title">简体中文</div>            <div class="content">...</div>        </div>    </main></div>

将上面的样式简单写好之后,开始考虑如何将划词翻译的面板展示在浏览器当前页面。对于谷歌浏览器来说,在网页上进行的交互是属于 content_scripts 的,需要引入划词翻译面板所需要的 JS 或者 CSS来生成当前面板。

其次,在配置文件中配置 content_scripts引入 JS 文件,动态的生成 DOM 元素。大致的思路就是通过监听到鼠标松开后,去生成翻译面板,在生成的元素上面添加 opacity 样式控制显隐,使用谷歌免费翻译 Api 进行翻译。

其中代码如下所示:
// manifest.json{    ...
"content_scripts": { "matches": ["<all_urls>"], "css": ["content_script.css"], "js": ["content_script.js"] }, "permissions": [ "activeTab" ],
...}
// content_script.jsclass TranslatePanel {
createPanel = () => { let wrapper = document.createElement('div') wrapper.innerHTML = ` <header>谷歌划词翻译插件<span class="close">X</span></header> <main> <div class="source"> <div class="title">英文</div> <div class="content">test</div> </div> <div class="result"> <div class="title">简体中文</div> <div class="content">...</div> </div> </main> ` wrapper.classList.add('translate-panel') wrapper.querySelector('.close').onclick = () => { this.wrapper.classList.remove('show') } document.body.appendChild(wrapper) this.wrapper = wrapper
}
showPanel = () => { this.wrapper.classList.add('show') }
translateSelect = (content) => { const source = this.wrapper.querySelector('.source .content') const result = this.wrapper.querySelector('.result .content') source.innerHTML = content result.innerHTML = '翻译中...'
fetch(`https://translate.google.cn/translate_a/single?client=at&sl=en&tl=zh-CN&dt=t&q=${content}`) .then(res => res.json()) .then(res => { result.innerHTML = res[0][0][0] }) }
locationPanel = (target) => { this.wrapper.style.top = target.y + 'px' this.wrapper.style.left = target.x + 'px' }}
let panel = new TranslatePanel()panel.createPanel()
window.onmouseup = (target) => { // 获取选中内容 const content = window.getSelection().toString().trim()
if (!content) return panel.locationPanel({ x: target.pageX, y: target.pageY }) panel.translateSelect(content) panel.showPanel()
}

在上面过程的中,笔者使用了谷歌免费的翻译接口,但是这个接口按照目前的设置还是有一点问题,我们暂时不表。现在划词翻译的面板就已经基本写好了。


(三)脚本通信

划词翻译插件开发到这里,细心的同学应该发现了,每次选中单词时都会触发划词翻译功能,此时急需一个控制翻译功能的开关,这个开关就可以放在 popup 脚本上面。
具体的样式的实现就不去介绍了,主要看一下 HTML 结构。
<div class="switch-wrapper">    <div class="switch-desc">是否启用划词翻译</div>    <input type="checkbox" class="switch" /></div>

基本效果如下:

http://dtstack-static.oss-cn-hangzhou.aliyuncs.com/2021bbs/files_user1/article/cb0ba9c30d8875ed020b0c44f7b24085..jpg



此时面板和划词翻译的面板都已经有了,再考虑一下如何实现 popup 脚本与 content_script 脚本之间的通信。首先,在
popup 脚本,我们在打开窗口的时候需要去查询是否有存储开启划词翻译的状态,同时,
同时当状态发生变更的时候需要将其存储时,再在当前的Tab下面发送请求。
// popup.jslet switchWrapp = document.querySelector('.switch')
chrome.storage.sync.get(['checked'], (target) => { if (target) { switchWrapp.checked = target.checked }})
switchWrapp.onclick = (e) => { chrome.storage.sync.set({ checked: e.target.checked })
chrome.tabs.query( {active: true, currentWindow: true }, (tabs) => { chrome.tabs.sendMessage(tabs[0].id, { checked: e.target.checked }) })}

上面代码中的 chrome.storage 可用于存储数据,追踪数据。storage.sync 的作用是让谷歌浏览器的数据同步,这使得在不同 Tab 页上面切换的状态也是可以同步的,同时也不用将数据保存在 background 后台页面中,storage还有很多Api比如监听 storage数据变化的onChanged,这里就不一一介绍了。

将开启或关闭划词翻译的状态发送后,content_script.JS 需要添加监听事件,获取到该状态后,进行关闭或开启操作。
// content_script.jslet checked = false
window.onmouseup = (target) => { ···
if (!content || !checked) return
···
}
chrome.storage.sync.get(['checked'], (target) => { if (target) checked = target.checked})
chrome.runtime.onMessage.addListener((target) => { if (target) { checked = target.checked }})

在开发过程中,发现在当前的 Tab 是可以去完成这个操作的,但是当开启了多个 Tab 的情况下就会出现开启翻译却不能展示翻译面板的情况。
针对以上情况笔者思考了一下,此时应该将checked储存起来,不应该放在 content_script 脚本当中。
// content_script.jslet panel = new TranslatePanel()panel.createPanel()
window.onmouseup = (target) => { // 获取选中内容 const content = window.getSelection().toString().trim()
if (!content) return
window.chrome.storage.sync.get(['checked'], (result) => { if (result.checked) { panel.locationPanel({ x: target.pageX, y: target.pageY }) panel.translateSelect(content) panel.showPanel() } })}
chrome.runtime.onMessage.addListener((target) => { if (target.type == 'CHECKED') { chrome.storage.sync.set({ checked: target.checked }) }})

以上,popup 脚本和 content_script 脚本之间就实现了通信,翻译插件也可以通过 popup 上面的按钮,进行开启或关闭翻译功能。
同理,也可以知道其他模块也是可以通过这种方式去进行通信,不同的是其他脚本向 content_script 通信是需要使用 tabs,先查找到当前的 Tab 在发送请求。
http://dtstack-static.oss-cn-hangzhou.aliyuncs.com/2021bbs/files_user1/article/6c352ffe35b72f8b35c02a040155d154..jpg


(四)右键直达翻译页面

当关闭划词翻译的时候,直接无法翻译选中内容也不是很友好,这个时候可以设置为点击右键的时候出现翻译菜单项。因为这部分内容需要一直存在就加在 background 中。
// backgrond.js// 当扩展程序第一次安装、更新至新版本或 Chrome 浏览器更新至新版本时产生chrome.runtime.onInstalled.addListener(() => {    chrome.contextMenus.create({        "id": "SELECT_TRANSLATE",        "title": "翻译 %s",        "contexts": ["selection"]    })})
chrome.contextMenus.onClicked.addListener((target) => { if (target.menuItemId == 'SELECT_TRANSLATE') { chrome.tabs.create({url: `https://translate.google.cn/?sl=en&tl=zh-CN&text=${target.selectionText}&op=translate`}) }})

(五)跨域问题

开发过程中,有的同学也看出来了一个问题,比如说谷歌的这个翻译的 Api 需要同源的情况下才能正常调用该接口,然后就只能在谷歌翻译的页面中使用划词翻译,场面一度十分尴尬...

那么,正常来说这个划词翻译使用起来也是十分不合理的,接下来就需要解决一下这个跨域的问题。

http://dtstack-static.oss-cn-hangzhou.aliyuncs.com/2021bbs/files_user1/article/1d1164de6c54c80a6a306331160a38f8..jpg
笔者当时想要尝试的是使用 JSONP,也就是去使用嵌入脚本去进行跨域,发现还是会有一些问题,主要是谷歌的翻译的接口不支持回调函数。
同时也去查阅了一些资料,发现是可以在 content_script 中通知 background,background 后台去调用谷歌翻译的 Api 是来避免这个情况的。
主要因为 background 的权限非常高,几乎可以调用所有的 Chrome 扩展 Api,而且它可以无限制跨域,也就是可以跨域访问任何网站而无需要求对方设置 CORS。

具体添加的代码如下:
// content_script.jstranslateSelect = (content) => {    const source = this.wrapper.querySelector('.source .content')    const result = this.wrapper.querySelector('.result .content')    source.innerHTML = content    result.innerHTML = '翻译中...'
chrome.runtime.sendMessage({ type: 'QUERY_TRANSLATE', queryContent: content }, (res) => { result.innerHTML = res[0][0][0] })}
// background.jschrome.runtime.onMessage.addListener((request, sender, callBack) => { if (request.type == 'QUERY_TRANSLATE') { fetch(`https://translate.google.cn/translate_a/single?client=at&sl=en&tl=zh-CN&dt=t&q=${request.queryContent}`) .then(res => res.json()) .then(res => { callBack(res) }) return true }})

background 中的发送消息的监听事件返回 true 是为了与 content_script 的消息通道保持打开,通过异步的方式发送请求。

现在想想,如果使用插件的 background 就可以去跨域去进行请求一些借口,使用不得当的话感觉还是很危险的,可以去获取其他网站的一些信息,由此可见,还是要慎重的进行此操作。


(六)待完善的点

  • 支持其他语言的翻译,谷歌翻译的接口有两个 Api,sl(文本翻译之前的语言) 和 tl(文本需要翻译成的语言) 可通过改变对应的值支持其他语言的翻译;
  • 样式完善,实现先选中图标在进行翻译。多添加一步感觉对交互更友好,不用去进行开关的操作。


代码结构


http://dtstack-static.oss-cn-hangzhou.aliyuncs.com/2021bbs/files_user1/article/6e039647f6d1b4f87afea4e3e98d2bce..jpg

介绍完了划词翻译插件,笔者原本是打算再分享一个关于谷歌 devtool 开发工具,开发一个类似于 React Developer Tools 的本地开发工具,但是由于时间也是不太够,涉及到的点比较多,同时查阅了很多的资料对于这一块的介绍也是比较的浅,所以还是决定着重于介绍划词翻译。

相信通过划词翻译的开发能使得读者比较快速的认识到谷歌插件,除了谷歌插件外,如果对于 devtool 工具插件开发有兴趣的同学也可以去了解一下。

另外,有的同学可能会认为目前开发的效率是有一点低的,现在的话谷歌插件的开发也是可以基于 react + antd 去进行开发的,也是可以达到高效快速的去开发一个插件的效果。


结尾


在学习谷歌插件的时候,笔者遇到了一些问题,比如说文档比较少,官方文档又是英文的还经常 404,不过呢,好在谷歌浏览器有提供了很多 Api,笔者这边在写插件的时候也感觉到了很多趣味性。本篇文章还是写的比较通俗易懂,如果有什么点写的不对或着不清晰的地方,欢迎大家留言进行探讨。


相关资源及参考:

  • 袋鼠云在大数据领域深耕7年,拥有丰富的大数据平台建设经验和成熟的产品体系,想了解或咨询更多有关袋鼠云大数据产品、行业解决方案、客户案例的朋友,浏览袋鼠云官网:https://www.dtstack.com/?src=bbs

    同时,欢迎对大数据开源项目有兴趣的同学加入「袋鼠云开源框架钉钉技术群」,交流最新开源技术信息,群号码:30537511,项目地址:https://github.com/DTStack

0条评论
社区公告
  • 大数据领域最专业的产品&技术交流社区,专注于探讨与分享大数据领域有趣又火热的信息,专业又专注的数据人园地

最新活动更多
微信扫码获取数字化转型资料
钉钉扫码加入技术交流群