当前位置: 首页 > Web前端 > CSS

数据栈技术干货:从0到1实现谷歌插件开发探索与应用

时间:2023-03-30 16:01:59 CSS

本文整理自:技术干货丨谷歌插件开发探索与应用gitee上有个有意思的开源项目:FlinkX,记得给我们一个star!星星!星星!gitee开源项目:https://gitee.com/dtstack_dev...github开源项目:https://github.com/DTStack/fl...FlinkX是一个基于Flink批处理的统一数据同步工具,它可以采集静态数据,如MySQL、HDFS等,也可以采集实时变化的数据,如MySQLbinlog、Kafka等。是一个集全局、异构、批流于一体的数据同步引擎。有兴趣的欢迎来到github社区一起玩~1.前言笔者一直想了解一些关于谷歌插件的相关知识。希望通过谷歌插件,可以更好的了解谷歌的调试工具。同时,我也觉得可以利用谷歌插件写一些小工具。发现了新事物,也有一定的乐趣。最近正好需要分享,所以用了两周的时间学习和了解了关于谷歌插件的知识。本文将与大家分享笔者在学习过程中的一些思考。当然,由于时间原因,如果笔者对这方面的理解有误,欢迎批评指正~2、什么是GooglePlug-in?先介绍一下GooglePlug-in的主要组件,因为目前比较常用的GooglePlug-in版本是2.0版本,所以本文以2.0版本为准。3.0版比2.0版更容易。感兴趣的同学可以点击文末链接了解更多。(1)配置文件Google插件的核心文件是配置文件——manifest.json(列表)文件。其中manifest.json文件最基本的API如下:{"name":"chromeextension","version":"1.0.0","manifest_version":2,"description":"Alitllechromeextensiondemo》}【点击拖动移动】主要包括编写的Google插件的名称、版本及相关说明,其中manifest_version表示manifest文件的版本。manifest.json是谷歌插件的核心部分。笔者认为,这个文件相当于插件的一个入口配置文件。开发者只需要在该文件中配置相应的js,调用谷歌浏览器提供的API即可。以达到完善本插件的目的。一、Api的基本使用manifest文件中有很多Api,就不一一列举了。下面介绍几个我认为比较重要的Apis。通过以下Apis的介绍,希望读者对Google插件更加熟悉。对开发过程有一个大致的了解。1)browser_action{..."browser_action":{"default_icon":{"16":"images/get_started16.png","32":"images/get_started32.png"},"default_title":"Googlewordtranslate","default_popup":"popup.html"},...}【点击拖动移动】browser_action可以设置浏览器右上角的图标和名称。default_popup可以配置点击图标后出现的小窗口,这里可以做一些临时操作2)permissions{..."permissions":["activeTab","storage","tabs","contextMenus"],...}[点击拖动移动]permissions可以配置google插件权限应用,如contextMenus(右键菜单)、tabs(标签)和storage(插件本地存储)。3)content_scripts{..."content_scripts":{"matches":[""],"css":["content/content_script.css"],"js":["content/content_script.js"]},...}content-scripts实际上是在Google插件中将脚本注入页面的一种形式(虽然叫脚本,但实际上可以包含CSS)。该页面注入JS和CSS。4)background{···"background":{"scripts":["background.js"],"persistent":false},···}[点击拖动移动]background是永久页面,它的生命周期是插件中所有类型页面中最长的。它在浏览器打开时打开,在浏览器关闭时关闭。代码放在后台。笔者还绘制了上述脚本在浏览器中的分布图,如下图:以上是笔者认为比较重要的一些API。介绍完之后有兴趣的同学可以开始写几个简单的工具了。它用来实现一个人的远大抱负和理想,升华一个人高尚的灵魂。可能有的同学不知道开发谷歌插件的前提条件,这里为大家简单介绍一下。首先,您需要打开管理扩展并打开开发者模式。点击Loadthedecompressedprogram按钮加载本地谷歌插件。如果在开发过程中更新了代码,需要刷新加载的插件,点击关闭再打开,不需要刷新开发页面。【点击拖动移动】了解前提条件后,下面笔者将从0-1与大家分享谷歌单词翻译插件的实现过程。通过开发这个工具,您还可以加深对谷歌插件的理解。3.谷歌翻译插件谷歌翻译是我使用比较频繁的一个插件。对于网页上不懂的英文单词或句子,我可以直接用鼠标选择对应的中文,方便快捷。因此,在学习的过程中,笔者在思考谷歌浏览器插件的翻译工具是如何实现的?(1)在思考如何制作一个词标翻译插件时,首先要考虑的是:如何实现翻译效果如何选择我们需要的元素如何显示词标翻译面板后selectingelements所有浏览器选项卡都需要支持翻译效果想到以上几点,带着这些疑惑,笔者在下面一一解答,同时也列举一些遇到的点。(2)翻译字标面板首先先不考虑插件的功能,先记下字标翻译面板的风格。实现的效果如下:HTML代码如下:

谷歌单词翻译插件X
main>English
test
简体中文
...[点击拖动移动]简单写完后以上样式,开始思考如何在浏览器当前页面显示单词翻译面板。对于谷歌浏览器,网页上的交互属于content_scripts,需要导入单词翻译面板所需的JS或CSS生成当前面板。二、在配置文件中配置content_scripts,导入JS文件动态生成DOM元素。大致思路是在听到鼠标松开后生成一个翻译面板,添加一个opacity样式来控制生成元素的显示和隐藏,使用谷歌的免费翻译API进行翻译。代码如下://manifest.json{..."content_scripts":{"matches":[""],"css":["content_script.css"],"js":["content_script.js"]},"permissions":["activeTab"],...}//content_script.jsclassTranslatePanel{createPanel=()=>{letwrapper=document.createElement('div')wrapper.innerHTML=`
谷歌单词翻译插件X
Englishtest简体中文...
`wrapper.classList.add('translate-panel')wrapper.querySelector('.close').onclick=()=>{this.wrapper.classList.remove('展示')}document.body.appendChild(wrapper)this.wrapper=wrapper}showPanel=()=>{this.wrapper.classList.add('show')}translateSelect=(content)=>{constsource=this.wrapper.querySelector('.source.content')constresult=this.wrapper.querySelector('.result.content')source.innerHTML=contentresult.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'}}letpanel=newTranslatePanel()panel.createPanel()window.onmouseup=(target)=>{//获取选择中内容constcontent=window.getSelection().toString().trim()if(!content)returnpanel.locationPanel({x:target.pageX,y:target.pageY})panel.translateSelect(content)panel.showPanel()}[点击拖动移动]在上面的过程中,笔者使用了谷歌的免费翻译界面,但是这个界面根据目前的设置还是有些问题,我们就不在文字中展示了翻译的面板暂时就基本写完了。(3)这里开发了脚本通信文字标记翻译插件。细心的同学应该已经发现,每次选中一个词都会触发词标翻译功能。这时候就急需一个控制翻译功能的开关。这个开关可以放在上面的弹出脚本中。具体样式的实现我就不介绍了,主要看html结构:是否开启分词翻译的基本效果如下:这时候面板和翻译单词的面板已经有了,接下来考虑如何实现它们之间的通信弹出脚本和content_script脚本。首先,在弹出脚本中,当我们打开窗口时,我们需要检查是否有存储单词翻译的状态。同时,当状态发生变化需要存储状态时,我们需要在当前Tab下发送请求。//popup.jsletswitchWrapp=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的作用是同步谷歌浏览器的数据,这样就可以同步不同标签页上的切换状态,同时不需要在后台后台页面保存数据。Storage也有很多API比如监控存储数据变化的onChanged这里就不一一介绍了。content_script.JS发送打开或关闭单词翻译状态后,需要添加监听事件,获取状态后,执行关闭或打开操作。//content_script.jsletchecked=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.jsletpanel=newTranslatePanel()panel.createPanel()window.onmouseup=(target)=>{//获取选中的内容constcontent=window.getSelection().toString().trim()if(!content)returnwindow.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脚本的通信,翻译插件也可以通过弹出窗口上的按钮。同理,你也可以知道其他模块也可以通过这种方式进行通信。不同的是其他脚本需要使用tabs与content_script进行通信。首先找到当前Tab,发送请求。【点击拖动移动】(4)右击直接进入翻译页面。关闭单词翻译后,选中的内容不能直接翻译,不是很友好。这时候,你可以设置翻译菜单项在你点击鼠标右键的时候出现。因为这部分内容需要一直存在,所以添加到后台。//backgrond.js//chrome.runtime.onInstalled.addListener(()=>{chrome.contextMenus.create({"id":"SELECT_TRANSLATE","title":"Translation%s","contexts":[“选择”]})})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中通知后台,后台调用GoogleTranslateApi来避免这种情况。主要原因是后台权限非常高,可以调用几乎所有的Chrome扩展API,并且可以无限制跨域,即可以跨域访问任何网站,不需要对方设置CORS。具体添加的代码如下://content_script.jstranslateSelect=(content)=>{constsource=this.wrapper.querySelector('.source.content')constresult=this.wrapper.querySelector('.result.content')source.innerHTML=contentresult.innerHTML='Translation...'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)})returntrue}})【点击拖动移动】后台发送消息监听事件返回true,保持content_script的消息通道开启,异步发送请求。现在想想,如果用了插件后台,还可以去跨域找借口。如果使用不当,感觉还是很危险的。您可以从其他网站获取一些信息。可见这个操作要慎重。(6)待改进点支持其他语言的翻译。谷歌翻译的接口有两个API,sl(文本翻译前的语言)和tl(文本需要翻译成的语言)。可以通过改变相应的值Translation来支持其他语言;风格完美,首先选择图标进行翻译。多加一步感觉交互更友好,不用去操作开关。代码结构【点击拖动移动】介绍完文字标记翻译插件后,笔者原本打算分享另一个关于Googledevtools的开发工具,开发一个类似ReactDeveloperTools的本地开发工具,但是由于缺少时间,涉及的点比较多,同时查阅了很多资料,这块的介绍比较浅,所以决定重点介绍单词翻译。相信单词翻译的发展可以让读者较快地体会到谷歌插件。除了google插件,有兴趣开发devtool工具插件的同学也可以了解一下。另外可能有的同学会觉得现在的开发效率有点低。现在谷歌插件的开发也可以基于react+antd来开发,同样可以达到高效快速开发一个插件的效果。4、最后笔者在学习谷歌插件的时候遇到了一些问题,比如文档比较少,官方文档都是英文的,经常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