共计 8656 个字符,预计需要花费 22 分钟才能阅读完成。
前言
之前笔者始终想理解一些对于谷歌插件的相干常识,通过这个谷歌插件也能够更好的意识到这个谷歌的调试工具,正好最近须要进行分享,这两个星期去学习和理解了谷歌插件,而后写了这一篇文章,把自己所理解的和一些思考点写了下来。同时,也想着能够应用谷歌插件去写一些小工具,既学习了新的货色,又有肯定的趣味性。当然,因为工夫的起因,如果笔者对于这一块的意识有不对的中央,欢送批评指正~
什么是谷歌插件
谷歌插件,全名谷歌浏览器扩大程序。那什么是谷歌浏览器扩大程序,官网阐明如下:
扩大程序容许您为 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,来达到欠缺这个插件的目标。
根本应用 Api
在清单文件中还有很多 Api 就不一一列举了,上面只介绍几个笔者认为比拟重要的几个 Api,通过以下几个 Api 能够使得读者对于谷歌插件的开发过程有一个大略的意识。
- 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 可配置点击图标后会呈现的一个小窗口,这里能够做一些临时性的操作。
- permissions
{
...
"permissions": ["activeTab", "storage", "tabs", "contextMenus"],
...
}
permissions 可配置谷歌插件权限申请,如 contextMenus(右键菜单), tabs(标签),storage(插件本地存储)。
- 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。
- background
{
···
"background": {"scripts": ["background.js"],
"persistent": false
},
···
}
background 是一个常驻的页面,它的生命周期是插件中所有类型页面中最长的,它随着浏览器的关上而关上,随着浏览器的敞开而敞开,所以通常把须要始终运行的、启动就运行的、全局的代码放在 background 外面。
笔者也画了一个下面波及到的脚本在浏览器中的散布
以上,介绍完谷歌插件清单文件根本且比拟重要的一些 Api 当前,感兴趣的同学就能够开始着手写几个简略的工具,用来实现本人远大的抱负以及现实,升华本人高贵的灵魂。相应的,笔者也分享一个有意思的谷歌插件工具实现过程,通过开发这个工具也能够加深对于谷歌插件的意识。
前置条件
因为有的同学不太晓得怎么开发谷歌插件,就还是把前置条件说一下。首先须要关上治理扩大程序,关上开发者模式。点击加载已解压的程序按钮即可加载本地谷歌插件,开发的时候代码如果有更新的话,须要刷新已加载插件,点击敞开后再开启,不用刷新开发页面。
谷歌划词翻译插件
笔者对于谷歌插件应用次数比拟频繁的还是翻译工具,对于在网页上看到的不懂的英文单词或者句子,间接应用鼠标选中,轻松快捷的翻译出相应的中文。那么谷歌浏览器插件的翻译工具是如何实现的呢?
思考
如何去做一个划词翻译插件,首先要思考的有以下几点:
- 如何实现翻译成果
- 如何选中咱们须要的元素
- 选中元素之后如何展现划词翻译面板
- 所有的浏览器 Tab 都须要反对翻译成果
思考完下面的点后,带着这几个纳闷,笔者在下文一一解答,同时也列举一下遇到的一些点。
划词翻译面板
首先不去思考该插件的性能,先写下划词翻译的面板的款式,所达到的成果如下:
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.js
class 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>
于此同时,面板和划词翻译的面板都曾经有了,再考虑一下如何实现 popup 脚本与 content_script 脚本之间的通信。首先,在 popup 脚本下面,咱们在关上窗口的时候须要去查问是否有存储开启划词翻译的状态,同时,当状态产生变更的时候须要将其存储,再在以后的 Tab 上面发送申请。
// popup.js
let 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.js
let 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.js
let 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 在发送申请。
右键中转翻译页面
当敞开划词翻译的时候,间接不能再去翻译选中内容也不是很敌对,这个时候能够为点击右键的时候呈现翻译菜单项。因为这部分内容须要始终存在就加在 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 须要同源的状况下能力失常调用该接口,而后就只能在谷歌翻译的页面中应用划词翻译,局面一度非常难堪 …
那么,失常来说这个划词翻译应用起来也是非常不合理的,接下来就须要解决一下这个跨域的问题。
笔者过后想要尝试的是应用 JSONP,也就是去应用嵌入脚本去进行跨域,发现还是会有一些问题,次要是谷歌的翻译的接口不反对回调函数,同时也去查阅了一些材料,发现是能够在 content_script 中告诉 background,background 后盾去调用谷歌翻译的 Api 是来防止这个状况的,次要因为 background 的权限十分高,简直能够调用所有的 Chrome 扩大 Api,而且它能够无限度跨域,也就是能够跨域拜访任何网站而无需要求对方设置 CORS,具体增加的代码如下:
// content_script.js
translateSelect = (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.js
chrome.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(文本须要翻译成的语言)可通过扭转对应的值反对其余语言的翻译
- 款式欠缺,实现先选中图标在进行翻译
多增加一步感觉对交互更敌对,不必去进行开关的操作
代码构造
介绍完了划词翻译插件,笔者本来是打算再分享一个对于谷歌 devtool 开发工具,开发一个相似于 React Developer Tools 的本地开发工具,然而因为工夫也是不太够,波及到的点比拟多,同时查阅了很多的材料对于这一块的介绍也是比拟的浅,所以还是决定着重于介绍划词翻译,通过划词翻译的开发使得读者也可能比拟疾速的意识到谷歌插件,谷歌浏览器的一些,触类旁通,如果对于 devtool 工具插件开发有趣味的同学也能够去理解一下。另外,有的同学可能会认为目前开发的效率是有一点低的,当初的话谷歌插件的开发也是能够基于 react + antd 去进行开发的,也是能够达到高效疾速的去开发一个插件的成果。
结尾
在学习谷歌插件的时候,笔者遇到了一些问题,比如说文档比拟少,官网文档又是英文的还常常 404,不过呢,好在谷歌浏览器有提供了很多 Api,笔者这边在写插件的时候也感觉到了很多趣味性。本篇文章还是写的比拟通俗易懂,如果有什么点写的不对或着不清晰的中央,欢送踊跃举手发言
更多
- 官网文档:https://developer.chrome.com/docs/extensions/mv3/
- react + antd 脚手架:https://github.com/jhen0409/react-chrome-extension-boilerplate
- 残缺代码:https://github.com/ting0130/chrome-extensions-translate