【油猴脚本 Greasemonkey】GM_xmlhttpRequest内部实现原理
微wx笑 2022-11-01【前端开发】 2 0关键字: 油猴脚本 Greasemonkey
多数扩展脚本都会用到网络请求功能,对应的【油猴脚本 Greasemonkey】就是调用 GM_xmlhttpRequest 函数,这个函数内部是怎么实现的呢?拿到一段油猴脚本想用在自己开发的Chrome扩展中,但是用到了 GM_xmlhttpRequest 函数怎么办?
本文来讲一下实现原理,附代码。
多数扩展脚本都会用到网络请求功能,对应的【油猴脚本 Greasemonkey】就是调用 GM_xmlhttpRequest 函数,这个函数内部是怎么实现的呢?拿到一段油猴脚本想用在自己开发的Chrome扩展中,但是用到了 GM_xmlhttpRequest 函数怎么办?
本文来讲一下实现原理,附代码。
缘起
网页上的即时翻译功能,大家用的较多的就是 划词翻译 了吧,但是今年以来互联网开始由开放转为封闭,很多原来免费的功能现在转为收费了,很多免费使用的翻译接口,现在虽然还没有完全收费,但是也需要自己去申请之后才能使用,这就是在收集用户信息了,只是第一步,接下来估计收费是一个趋势。
相关事件:搜狗翻译的官方平台搜狗深智引擎开放平台发出公告,将于 2022 年 2 月 28 日停止服务,公告具体内容可前往搜狗深智引擎开放平台官网查看。
百度翻译2022年7月14日发送了邮件通知,将从 8 月 1 日起下调每月的免费额度。
调整前免费额度 | 调整后免费额度 | |
---|---|---|
标准版 | 无限量 | 5 万字符 / 月 |
高级版 | 200 万字符 / 月 | 100 万字符 / 月 |
刚好自己最近也在研究Chrome扩展的开发,基本算了入门了一点点,所以就考虑自己实现一个划词翻译的功能。
于是网上找划词翻译的源码参考,就找到了 有道划词翻译 的一段源码分享,是基于油猴脚本的,
有道划词翻译源码
// ==UserScript== // @name 有道划词翻译 // @version 0.111 // @author Jim Lin // @originalAuthor Liu Yuyang(sa@linuxer.me) // @match http://*/* // @description 极简 // @grant GM_xmlhttpRequest // @namespace https://greasyfork.org/users/25855 // ==/UserScript== window.document.body.addEventListener('mouseup', translate, false); var context = new AudioContext(); function translate(e) { var previous = document.querySelector('.youdaoPopup'); if (previous) { document.body.removeChild(previous); } var selectObj = document.getSelection(); if (selectObj.anchorNode.nodeType == 3) { var word = selectObj.toString(); if (word == '') { return; } word = word.replace('-\n', ''); word = word.replace('\n', ' '); var ts = new Date().getTime(); var x = e.clientX; var y = e.clientY; translate(word, ts); } function popup(x, y, result) { var youdaoWindow = document.createElement('div'); youdaoWindow.classList.toggle('youdaoPopup'); var dict = JSON.parse(result); var query = dict['query']; var errorCode = dict['errorCode']; if (dict['basic']) { word(); } else { sentence(); } youdaoWindow.style.zIndex = '1024'; youdaoWindow.style.display = 'block'; youdaoWindow.style.position = 'fixed'; youdaoWindow.style.color = 'black'; youdaoWindow.style.textAlign = 'left'; youdaoWindow.style.wordWrap = 'break-word'; youdaoWindow.style.background = 'lightBlue'; youdaoWindow.style.borderRadius = '5px'; youdaoWindow.style.boxShadow = '0 0 5px 0'; youdaoWindow.style.opacity = '1'; youdaoWindow.style.width = '200px'; youdaoWindow.style.left = x + 10 + 'px'; youdaoWindow.style.padding = '5px'; if (x + 200 + 10 >= window.innerWidth) { youdaoWindow.style.left = parseInt(youdaoWindow.style.left) - 200 + 'px'; } if (y + youdaoWindow.offsetHeight + 10 >= window.innerHeight) { youdaoWindow.style.bottom = '20px'; } else { youdaoWindow.style.top = y + 10 + 'px'; } document.body.appendChild(youdaoWindow); function word() { var basic = dict['basic']; var header = document.createElement('p'); var span = document.createElement('span'); span.innerHTML = query; header.appendChild(span); var phonetic = basic['phonetic']; if (phonetic) { var phoneticNode = document.createElement('span'); phoneticNode.innerHTML = '[' + phonetic + ']'; phoneticNode.style.cursor = 'pointer'; header.appendChild(phoneticNode); phoneticNode.addEventListener('mouseup', function (e) { e.stopPropagation() }, false); var soundUrl = 'https://dict.youdao.com/dictvoice?type=2&audio={}'.replace('{}', query); var promise = new Promise(function () { GM_xmlhttpRequest({ method: 'GET', url: soundUrl, responseType: 'arraybuffer', onload: function (res) { try { context.decodeAudioData(res.response, function (buffer) { phoneticNode.addEventListener('mouseup', function () { var source = context.createBufferSource(); source.buffer = buffer; source.connect(context.destination); source.start(0); }, false); header.appendChild(document.createTextNode('✓')); }) } catch (e) { } } }); }); promise.then(); } header.style.color = 'darkBlue'; header.style.margin = '0'; header.style.padding = '0'; span.style.color = 'black'; youdaoWindow.appendChild(header); var hr = document.createElement('hr'); hr.style.margin = '0'; hr.style.padding = '0'; youdaoWindow.appendChild(hr); var ul = document.createElement('ul'); ul.style.margin = '0'; ul.style.padding = '0'; basic['explains'].map(function (trans) { var li = document.createElement('li'); li.style.listStyle = 'none'; li.style.margin = '0'; li.style.padding = '0'; li.appendChild(document.createTextNode(trans)); ul.appendChild(li); }); youdaoWindow.appendChild(ul); } function sentence() { var ul = document.createElement('ul'); ul.style.margin = '0'; ul.style.padding = '0'; dict['translation'].map(function (trans) { var li = document.createElement('li'); li.style.listStyle = 'none'; li.style.margin = '0'; li.style.padding = '0'; li.appendChild(document.createTextNode(trans)); ul.appendChild(li); }); youdaoWindow.appendChild(ul); } } function translate(word, ts) { var reqUrl = 'http://fanyi.youdao.com/openapi.do?type=data&doctype=json&version=1.1&relatedUrl=' + escape('http://fanyi.youdao.com/#') + '&keyfrom=fanyiweb&key=null&translate=on' + '&q={}'.replace('{}', word) + '&ts={}'.replace('{}', ts); GM_xmlhttpRequest({ method: 'GET', url: reqUrl, onload: function (res) { popup(x, y, res.response); } }); } }
实现原理
上面代码中就用到了 GM_xmlhttpRequest 函数,自己如何实现这个函数呢?
用过 ajax,xmlhttpRequest 的小伙伴看到这个函数的调用方法应该感觉非常熟悉,和 ajax 的调用基本差不多,无非是把调用成功的响应事件由 success 改成了 onload
所以自己定义一个函数,内部封装 ajax 的调用就可以了。
但是需要注意,你的扩展程序运行在别人的网站上,发出的请求基本都是跨域的请求,仅仅封装 ajax 是不行的,这就需要根据Chrome扩展提供的能力、方法来实现跨域请求;
具体实现代码参考:
【Chrome扩展程序】利用 background 实现跨域请求
本文由 微wx笑 创作,采用 署名-非商业性使用-相同方式共享 4.0 许可协议,转载请附上原文出处链接及本声明。
原文链接:https://www.ivu4e.cn/blog/front/2022-11-01/1564.html