前言

之前笔者始终想理解一些对于谷歌插件的相干常识,通过这个谷歌插件也能够更好的意识到这个谷歌的调试工具,正好最近须要进行分享,这两个星期去学习和理解了谷歌插件,而后写了这一篇文章,把自己所理解的和一些思考点写了下来。同时,也想着能够应用谷歌插件去写一些小工具,既学习了新的货色,又有肯定的趣味性。当然,因为工夫的起因,如果笔者对于这一块的意识有不对的中央,欢送批评指正~

什么是谷歌插件

谷歌插件,全名谷歌浏览器扩大程序。那什么是谷歌浏览器扩大程序,官网阐明如下:

扩大程序容许您为 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.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>

于此同时,面板和划词翻译的面板都曾经有了,再考虑一下如何实现 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 = falsewindow.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 在发送申请。

右键中转翻译页面

当敞开划词翻译的时候,间接不能再去翻译选中内容也不是很敌对,这个时候能够为点击右键的时候呈现翻译菜单项。因为这部分内容须要始终存在就加在 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.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(文本须要翻译成的语言) 可通过扭转对应的值反对其余语言的翻译
  • 款式欠缺,实现先选中图标在进行翻译
    多增加一步感觉对交互更敌对,不必去进行开关的操作

代码构造

介绍完了划词翻译插件,笔者本来是打算再分享一个对于谷歌 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