到现如今最流行的浏览器,那么一定是chrome,无论是它的速度,还是它的稳定性,还是它的简洁,都让人爱不释手,此外,更多的人选择它的理由是它有着丰富的扩展插件,这些扩展插件让你的浏览器变得异常强大,让你的浏览器不仅仅是浏览器。
chrome的扩展是以.crx结尾的安装包,如果你把它下载下来,并把它重命名为.rar压缩包文件,然后你就可以使用压缩软件对它进行解压,加压之后,就会发现其实chrome的扩展包里面就是一些js,css,html文件,可以说你只要会写前端,那么开发一个chrome扩展插件将会非常容易。
在这些文件中,有一个manifest.json文件,它是扩展的描述文件,定义了扩展的名称和版本号等信息。
{
"name": "BrowserActionExtension",
"version": "0.0.1"
"manifest_version": 2,
"browser_action": {
"default_title": "That's the tool tip",
"default_popup": "popup.html"
}
}
在这个配置文件中,你还可以添加其它属性,只要你的扩展需要的属性,你都可以在这里添加配置。
每一个扩展都有一个被浏览器运行的背景页,此外还有事件页面,背景页面一直都是激活状态,而事件页面只是在触发事件的时候才会激活,因此为了节省内存和提高浏览器的性能,尽可能选择事件页面。两者通过persistent属性进行区分。
"background": {
"scripts": ["background.js"],
"persistent": false/true
}
当我们的扩展想要访问浏览器当前页面的dom树的时候,我们需要使用内容脚本,这些脚本会在页面刷新的时候执行。
"content_scripts": [
{
"matches": ["https://*/*", "https://*/*"],
"js": ["content.js"]
}
]
对于扩展的UI界面,我们可以通过browser_action属性进行配置,通过此属性,我们可以设置扩展的图标,设置点击弹出的页面。
"browser_action": {
"default_icon": {
"19": "icons/19x19.png",
"38": "icons/38x38.png"
},
"default_title": "That's the tool tip",
"default_popup": "popup.html"
}
除了browser_action可以配置扩展图标之外,page_action可以配置图标,两者的区别是,browser_action总是显示在扩展栏,而page_action则是满足一定条件才会显示,比如页面有vue脚本时候才会显示vue调试图标。
"page_action": {
"default_icon": {
"19": "images/icon19.png",
"38": "images/icon38.png"
},
"default_title": "Google Mail",
"default_popup": "popup.html"
}
chrome被开发人员所喜爱的另一个原因是它提供了非常强大的调试工具栏,而我们的扩展也是可以加入到调试工具栏的。
通过使用devtools_page属性,我们就可以将我们的扩展加入到调试工具栏的一个tab中。
"devtools_page": "devtools.html"
我们在devtools.html中只需要添加一个js引入语句就可以。
<script src="devtools.js"></script>
在devtools.js文件里,我可以可以放入我们实际的扩展内容。
chrome.devtools.panels.create(
"MyExtension",
"img/icon16.png",
"index.html",
function() {
}
);
扩展能够做什么主要取决于浏览器为我们提供了哪些API,庆幸的是,chrome为我们提供了足够多好用的API。
总之,chrome几乎为我们提供了完整控制浏览器的扩展api,正是有了这些api,才诞生了几十万的扩展插件。
在我们本地开发好扩展之后,我们可以通过本地浏览器进行调试。
首先,我们需要先进入扩展程序页面,打开开发者模式
然后,我们可以通过选择加载已解压的扩展程序加载我们的扩展。
最后,我们通过在控制台输出调试信息来调试我们的扩展。
manifest.json
{
"name": "BrowserExtension",
"version": "0.0.1",
"manifest_version": 2,
"description" : "Description ...",
"icons": { "16": "icons/16x16.png", "48": "icons/48x48.png", "128": "icons/128x128.png" },
"omnibox": { "keyword" : "yeah" },
"browser_action": {
"default_icon": { "19": "icons/19x19.png", "38": "icons/38x38.png" },
"default_title": "That's the tool tip",
"default_popup": "browseraction/popup.html"
},
"background": {
"scripts": ["background.js"],
"persistent": false
},
"chrome_url_overrides" : {
"newtab": "newtab/newtab.html"
},
"content_scripts": [{
"matches": ["http://*/*", "https://*/*"],
"js": ["content.js"]
}],
"devtools_page": "devtools/devtools.html"
}
background.js
// omnibox
chrome.omnibox.onInputChanged.addListener(function(text, suggest) {
suggest([
{content: "color-divs", description: "Make everything red"}
]);
});
chrome.omnibox.onInputEntered.addListener(function(text) {
if(text=="color-divs") colorDivs();
});
chrome.extension.onMessage.addListener(function(request, sender, sendResponse) {
switch(request.type) {
case "color-divs":
colorDivs();
break;
}
return true;
});
chrome.extension.onConnect.addListener(function (port) {
port.onMessage.addListener(function (message) {
switch(port.name) {
case "color-divs-port":
colorDivs();
break;
}
});
});
// send a message to the content script
var colorDivs=function() {
chrome.tabs.getSelected(null, function(tab){
chrome.tabs.sendMessage(tab.id, {type: "colors-div", color: "#F00"});
// setting a badge
chrome.browserAction.setBadgeText({text: "red!"});
});
}
popup.html
<script type="text/javascript" src="popup.js"></script>
<div style="width:200px">
<button id="button">Color all the divs</button>
</div>
popup.js
window.onload=function() {
document.getElementById("button").onclick=function() {
chrome.extension.sendMessage({
type: "color-divs"
});
}
}
devtools.html
window.onload=function() {
var port=chrome.extension.connect({ name: "color-divs-port" });
document.getElementById("button").onclick=function() {
port.postMessage({ type: "color-divs"});
}
}
content.js
chrome.extension.onMessage.addListener(function(message, sender, sendResponse) {
switch(message.type) {
case "colors-div":
var divs=document.querySelectorAll("div");
if(divs.length===0) {
alert("There are no any divs in the page.");
} else {
for(var i=0; i<divs.length; i++) {
divs[i].style.backgroundColor=message.color;
}
}
break;
}
});
chrome浏览器的扩展开发其实并不难,用到的知识都是基础的js,html,css,我们只需要知道一些和浏览器交互的属性和操作的api,就可以开发出一个属于自己的浏览器扩展。
技术者写文章,基本少不了Markdown了,但是很多自媒体平台(大而全那种),往往都是坑爹的富文本编辑器(还很多是魔改UEditor,人家官方三年没更新了喂)。
小白学逻辑,内行看门道。
类似这种:
这是很麻烦的一件事,尤其是那些没有代码块的编辑器,没错,说的就是你,头条!这种坑爹玩意儿,就得让程序员手动粘贴代码过来,然后遇到排版不友好的,呵呵,对,说的还是你,头条! 于是吧,我就想着,奶奶个熊,没有我就自己写个插件来搞吧。
事实上,我自己的网站上有自己依赖marked做的一套编辑器,还挺好用,但是由于图床问题,还是得每次把富文本粘贴到头条后,删除图片,重新上传,没办法,穷是本命。 咳咳,最后做出来了,但是发现,没卵用……喵的,Markdown有代码块,人家富文本还是不支持啊……总之写出来分享下方案与思路。
manifest.json 配置
这要是看下content_scripts,这个说是scripts,你也可以看到,是可以塞一些css进去的,不过这里就看js。 util.js主要提供一个编辑时候使用的函数,作用是避免每次编辑触发input都转义Markdown2HTML,也就是debounce消抖了。
核心如下(附带throttle节流):
然后是turndown.js,这个是marked.js的反向。marked是把Markdown2HTML,那么turndown就是把HTML2Markdown了。这种东西当然是轮子了,安全好用(npm)。
至于content/index.js,就是核心页面插入的js(不是注入inject,这俩有差,这里不细说),就是document有了就运行的函数,一般都是document_start。这个等下结合插件的js说。
这个文件最后就是看popup.html,这个文件名随意区,作用是点击插件显示的那个小窗户,拿FeHelper看就是这样的:
看下内容:
常规内容,长这样:
就一个输入框和header,没了,监听这个输入框变化。
然后引入js,marked.js就不用说了,popup.js就是这个页面核心js了,下面细说。
到这里,功能页面与资源齐全了(不算icon什么的)。
一共上面4个核心问题处理,这个简易版插件就完成了(虽然没什么卵用)。
问题1
popup.js
具体都是chrome插件的api,主要看逻辑即可。
问题2,3,4
content/index.js
没错,灵魂是哪个alert,YES!
bug是有的,因为我也没去优化,反正也没用。而且头条这富文本标签挺奇葩的,得去魔改下marked.js才行。
主要是分享下逻辑,以及熟悉下chrome的api。
简单的知识点搭配合适的业务场景,往往能起到意想不到的效果。这篇文章会用三个最基础人人都知道的前端知识来说明如何助力运营小姐姐、公司48+前端开发同学的日常工作,让他们的工作效率得到极大地提升。
看完您可以会收获
因为接下来的两个小工具都是基于油猴脚本来实现的,所以我们提前先了解一下它
油猴脚本(Tampermonkey)是一个流行的浏览器扩展,可以运行用户编写的扩展脚本,来实现各式各样的功能,比如去广告、修改样式、下载视频等。
1. 安装油猴
以chrome浏览器扩展为例,点击这里先安装
安装完成之后可以看到右上角多了这个
2. 新增示例脚本 hello world
//==UserScript==// @name hello world // 脚本名称
// @namespace http://tampermonkey.net/
// @version 0.1
// @description try to take over the world!
// @author You
// @match https://juejin.cn/* // 表示怎样的url才执行下面的代码
// @icon https://www.google.com/s2/favicons?domain=juejin.cn
// @grant none
//==/UserScript==(function() {
'use strict';
alert('hello world')
// Your code here...
})();
复制代码
没错当打开任意一个https://juejin.cn/*掘金的页面时,都会弹出hello world,而其他的网页如https://baidu.com则不会。
到此你就完成了一个最简单的油猴脚本,接下来我们看一下用同样简单的代码,来解决一个实际问题吧!O(∩_∩)O
1. 有一天运营小姐姐要在几个系统之间配置点东西
一顿操作,终于把事情搞定了,心情美美的。
但是她心想,为啥每个系统都要我登录一次,不开心 o( ̄ヘ ̄o#)
2. 下午一觉醒来,领导让把上午的配置重新改一下(尽职的小姐姐马上开始操作)
但是让她没想到的是:上午的登录页面仿佛许久没有见到她一样,又和小姐姐来了一次亲密接触
此时,她的内心已经开始崩溃了
3. 但是这不是结束,以后的每一天她都是这种状态
看完上面的动图,我猜你已经在替小姐姐一起骂娘了,这做的什么玩意,太垃圾了。SSO是统一登录,你们这搞的是什么东西。
是的,我的内心和你一样愤愤不平, 一样有一万个草泥马在奔腾,这是哪个sb设计的方案,简直不配做人,一天啥事也不干,尽是跳登录页,输入用户名密码点登录按钮了,久而久之,朋友间见面说的第一句话不是“你吃了吗?”,而是“你登录了吗?”。
不过吐槽完,我们还是要想想如何通过技术手段解决这两个痛点,达到只需要登录一次的目的。
1. 在A系统登录之后,跑到其他系统需要重新登录。
2. 登录时效只有2小时,2小时后,需要重新登录
根本原因还是公司的SSO统一登录方案设计的有问题,所以需要推动他们修改,但是这是一个相对长期的过程,短期内有没有什么办法能让我们愉快的登录呢?
痛点1: 1. 在A系统登录之后,跑到其他系统需要重新登录。已无力回天
痛点2: 2. 登录时效只有2小时,2小时后,需要重新登录已无力回天
我们不好直接侵入各个系统去改造登录逻辑,改造其登录时效,但是却可以对登录页面(示例)做点手脚
最关键的是:
所以可以借助油猴脚本,在DOMContentLoaded的时候,插入一下代码,来实现自动登录,减少手动操作的过程,大概原理如下。
//==UserScript==// @name SSO自动登录
// @namespace http://tampermonkey.net/
// @version 0.1
// @description try to take over the world!
// @author You
// @match https://*.xxx.com/login* // 这里是SSO登录页面地址,表示只有符合这个规则的才注入这段代码
// @grant none
//==/UserScript==document.querySelector('#username').value='xxx' // 用户名
document.querySelector('#password').value='yyy' // 密码
document.querySelector('#login-submit').click() // 自动提交登录
复制代码
是不是太简单了,简单到令人发指,令人痛恨,令人想吐口水!!!,没有一点技术含量
是不是太简单了,简单到令人发指,令人痛恨,令人想吐口水!!!,没有一点技术含量
是不是太简单了,简单到令人发指,令人痛恨,令人想吐口水!!!,没有一点技术含量
是的,就这 ,第一次帮小姐姐解决了困扰她许久的问题,晚上就请我吃了麻辣烫,还夸我"技术"好(此处不是开车)
gif中前半部分没有开启自动登录的脚本需要手动登录,后半部开启了就可以自动登录了。
前端常见的调试方式
这些方式都有各自的优缺点,比如chrome inspect第一次需要翻墙才能使用,只适用于安卓; vconsole不方便直接调试样式; weinre只适用于调试样式等。
基于这些原因,公司很久之前搞了一个远程调试工具,可以很方便的增删DOM结构、调试样式、查看请求、查看application 修改后手机上立即生效。
远程调试平台使用流程
他的使用流程大概是这样的
看完流程你应该大概知道问题在哪里了, 远程调试页面列表不仅仅包含我自己的页面,还包括很多其他人的,导致很难快速找到自己想要调试的页面
问题解析
有什么办法能让我快速找到自己想要调试的页面呢?其实观察解析这个页面会发现列表是
拦截请求
所以聪明的你已经猜到了,我们可以通过Object.defineProperty拦截fetch请求,过滤设备让列表中只存在我们指定的设备(毕竟平时开发时调试的设备基本是固定的,而设备完全相同的概率是很低的,所以指定了设备其实就是唯一标识了自己)页面。
具体如何做呢?
//==UserScript==// @name 前端远程调试设备过滤
// @namespace http://tampermonkey.net/
// @version 0.1
// @description try to take over the world!
// @author You
// @match https://chii-fe.xxx.com/ // 指定脚本生效的页面
// @grant none
// @run-at document-start // 注意这里,脚本注入的时机是document-start
//==/UserScript==;(()=> {
const replaceRe=/\s*/g
// 在这里设置设备白名单
const DEVICE_WHITE_LIST=[
'Xiaomi MI 8',
'iPhone9,2',
].map((it)=> it.replace(replaceRe, '').toLowerCase())
const originFetch=window.fetch
const recordListUrl='record-list'
const filterData=(source)=> {
// 数据过滤,返回DEVICE_WHITE_LIST指定的设备的数据
// 详细过程省略
return data
}
// 拦截fetch请求
Object.defineProperty(window, 'fetch', {
configurable: true,
enumerable: true,
get () {
return function (url, options) {
return originFetch(url, options).then((response)=> {
// 只处理指定的url
if (url.includes(recordListUrl)) {
if (response.clone) {
const cloneRes=response.clone()
return new Promise((resolve, reject)=> {
resolve({
text: ()=> {
return cloneRes.json().then(json=> {
return filterData(JSON.stringify(json))
});
}
})
})
}
}
return response
})
}
}
})
})()
复制代码
通过下图可以看出,过滤前有37个页面,过滤后只剩3个,瞬间就找到你要调试页面,再也不用从几百个页面中寻找你自己的那个啦!
通过插件一键设置ua,模拟用户登录状态,提高开发效率。
插件使用方式
插件使用结果
团队48+小伙伴也使用起来了
日常c端业务中有很多场景都需要用户登录后才能正常进行,而开发阶段基本都是通过chrome模拟手机设备来开发,所以往往会涉及到在chrome浏览器中模拟用户登录,其涉及以下三步(这个步骤比较繁琐)。
备注:保持用户的登录态一般是通过cookie,但也有通过header来做,比如我们公司是改写ua来做的
来看一段对话
隔壁98年刚毕业妹子:
又过期了,谁又把我挤下去了嘛
好的,稍等一会哈,我换个账号测测
好麻烦哎!模拟一个用户信息,要这么多步骤,好烦呀!!!
我,好奇的大叔:
“细心”了解下,她正在做一个h5活动项目,场景复杂,涉及的状态很多,需要用不同的账号来做测试。
模拟一两个用户还好,但是此刻小姐姐测这么多场景,已经模拟了好多个(谁都会烦啊)
公司的登录体系是单点登录,一个好不容易模拟的账号,有可能别人也在用,结果又被顶掉了,得重新生成,我TM
看着她快气哭的小眼神,作为隔壁桌友好的邻居,此刻我心里只想着一件事...!帮她解决这个恼人的问题。
通过上面的介绍您应该可以感觉到我们开发阶段遇到需要频繁切换账号做测试时的烦恼,相对繁琐的ua生成过程导致了它一定是个费时费力的麻烦事。
有没有什么办法让我们的开发效率得到提升,别浪费在这种事情上呢?一起一步步做起来
需求有哪些
提供一种便捷地模拟ua的方式,助力开发效率提升。
如何解决
为什么是chrome插件
篇幅原因,这里只做示例级别的简单介绍,如果您希望详细了解chrome插件的编写可以参考这里
接下来我们会以下页面为例,说明用vue如何写出来。
基本功能
content部分
借助chrome浏览器可以向网页插入脚本的特性,我们会演示如何插入脚本并且在网页加载的时候弹一个hello world
popup与background通信部分
popup完成用户的主要交互,在viewA页面点击获取自定义的ua信息
修改ajax请求ua部分
会演示如果通过chrome插件修改请求header
1. manifest.json
几乎所有的东西都要在这里进行声明、权限、资源、页面等等
{
"manifest_version": 2, // 清单文件的版本,这个必须写
"name": "hello vue extend", // 插件的名称,等会我们写的插件名字就叫hello vue extend
"description": "hello vue extend", // 插件描述
"version": "0.0.1", // 插件的版本
// 图标,写一个也行
"icons": {
"48": "img/logo.png"
},
// 浏览器右上角图标设置,browser_action、page_action、app必须三选一
"browser_action": {
"default_icon": "img/logo.png",
"default_title": "hello vue extend",
"default_popup": "popup.html"
},
// 一些常驻的后台JS或后台页面
"background": {
"scripts": [
"js/hot-reload.js",
"js/background.js"
]
},
// 需要直接注入页面的JS
"content_scripts": [{
"matches": ["<all_urls>"],
"js": ["js/content.js"],
"run_at": "document_start"
}],
// devtools页面入口,注意只能指向一个HTML文件
"devtools_page": "devcreate.html",
// Chrome40以前的插件配置页写法
"options_page": "options.html",
// 权限申请
"permissions": [
"storage",
"webRequest",
"tabs",
"webRequestBlocking",
"<all_urls>"
]
}
复制代码
2. background script
后台,可以认为是一个常驻的页面,权限很高,几乎可以调用所有的API,可以与popup、content script等通信
3. content script
chrome插件向页面注入脚本的一种形式(js和css都可以)
4. popup
popup是点击browser_action或者page_action图标时打开的一个小窗口网页,焦点离开网页就立即关闭。
比如我们要用vue做的页面。
manifest.json对文件引用的结构基本决定了打包后的文件路径
打包后的路径
// dist目录用来chrome扩展导入
├── dist
│ ├── favicon.ico
│ ├── img
│ │ └── logo.png
│ ├── js
│ │ ├── background.js
│ │ ├── chunk-vendors.js
│ │ ├── content.js
│ │ ├── hot-reload.js
│ │ └── popup.js
│ ├── manifest.json
│ └── popup.html
复制代码
源码目录
├── README.md
├── babel.config.js
├── package-lock.json
├── package.json
├── public
│ ├── favicon.ico
│ ├── index.html
│ └── js
│ └── hot-reload.js
├── src
│ ├── assets
│ │ ├── 01.png
│ │ ├── disabled.png
│ │ └── logo.png
│ ├── background
│ │ └── background.js
│ ├── content
│ │ └── content.js
│ ├── manifest.json
│ ├── popup
│ │ ├── App.vue
│ │ ├── main.js
│ │ ├── router.js
│ │ └── views
│ │ ├── viewA.vue
│ │ ├── viewB.vue
│ │ └── viewC.vue
│ └── utils
│ ├── base.js
│ ├── fixCaton.js
│ └── storage.js
└── vue.config.js
复制代码
修改vue.config.js
主需要稍微改造变成可以多页打包,注意输出的目录结构就可以了
const CopyWebpackPlugin=require('copy-webpack-plugin')
const path=require('path')
// 这里考虑可以添加多页
const pagesObj={}
const chromeName=['popup']
const plugins=[
{
from: path.resolve('src/manifest.json'),
to: `${path.resolve('dist')}/manifest.json`
},
{
from: path.resolve('src/assets/logo.png'),
to: `${path.resolve('dist')}/img/logo.png`
},
{
from: path.resolve('src/background/background.js'),
to: `${path.resolve('dist')}/js/background.js`
},
{
from: path.resolve('src/content/content.js'),
to: `${path.resolve('dist')}/js/content.js`
},
]
chromeName.forEach(name=> {
pagesObj[name]={
css: {
loaderOptions: {
less: {
modifyVars: {},
javascriptEnabled: true
}
}
},
entry: `src/${name}/main.js`,
filename: `${name}.html`
}
})
const vueConfig={
lintOnSave:false, //关闭eslint检查
pages: pagesObj,
configureWebpack: {
entry: {},
output: {
filename: 'js/[name].js'
},
plugins: [new CopyWebpackPlugin(plugins)]
},
filenameHashing: false,
productionSourceMap: false
}
module.exports=vueConfig
复制代码
我们希望修改插件源代码进行打包之后,chrome插件对应的页面能主动更新。为什么叫热刷新而不是热更新呢?因为它其实是全局刷新页面,并不会保存状态。
这里推荐一个github上的解决方案crx-hotreload
文件目录结构
├── popup
│ ├── App.vue
│ ├── main.js
│ ├── router.js
│ └── views
│ ├── viewA.vue
│ ├── viewB.vue
│ └── viewC.vue
复制代码
main.js
import Vue from 'vue'
import App from './App.vue'
import router from './router'
Vue.config.productionTip=false
new Vue({
router,
render: h=> h(App)
}).$mount('#app')
复制代码
router.js
import Vue from 'vue'
import Router from 'vue-router'
import ViewA from './views/viewA.vue'
import ViewB from './views/viewB.vue'
import ViewC from './views/viewC.vue'
Vue.use(Router)
export default new Router({
mode: 'history',
base: process.env.BASE_URL,
routes: [
{
path: '/',
name: 'home',
redirect: '/view/a'
},
{
path: '/view/a',
name: 'viewA',
component: ViewA,
},
{
path: '/view/b',
name: 'viewB',
component: ViewB,
},
{
path: '/view/c',
name: 'viewC',
component: ViewC,
},
]
})
复制代码
App.vue
<template>
<div id="app">
<div class="app-router">
<router-view />
</div>
<div class="app-tab">
<div class="app-tab-item" v-for="(tabName, i) in tabs" :key="i" @click="onToView(tabName)">
{{ tabName }}
</div>
</div>
</div>
</template>
<script>
export default {
name: 'App',
data () {
return {
tabs: [
'viewA',
'viewB',
'viewC',
]
}
},
methods: {
onToView (name) {
this.$router.push({
name
})
}
}
}
</script>
<style lang="less">
#app {
font-family: Avenir, Helvetica, Arial, sans-serif;
-webkit-font-smoothing: antialiased;
-moz-osx-font-smoothing: grayscale;
text-align: center;
color: #2c3e50;
width: 375px;
height: 200px;
padding: 15px;
box-sizing: border-box;
display: flex;
justify-content: space-between;
flex-direction: column;
.app-router{
flex: 1;
}
.app-tab{
display: flex;
align-items: center;
justify-content: space-between;
.app-tab-item{
font-size: 16px;
color: coral;
cursor: pointer;
}
}
}
</style>
复制代码
viewA、viewB、viewC
三个页面基本长得是一样的,只有背景色和文案内容不一样,这里我就只贴viewA的代码了。
需要注意的是这里会演示popup与background,通过sendMessage方法获取background后台数据
<template>
<div class="view-a">我是A页面
<button @click="onGetCUstomUa">获取自定义ua</button>
</div>
</template>
<script>
export default {
name: 'viewA',
methods: {
onGetCUstomUa () {
chrome.runtime.sendMessage({type: 'getCustomUserAgent'}, function(response) {
alert(JSON.stringify(response))
})
}
}
}
</script>
<style lang="less">
.view-a{
background-color: cadetblue;
height: 100%;
font-size: 60px;
}
</style>
复制代码
background.js
const customUa='hello world ua'
// 请求发送前拦截
const onBeforeSendCallback=(details)=> {
for (var i=0; i < details.requestHeaders.length; ++i) {
if (details.requestHeaders[i].name==='User-Agent') {
details.requestHeaders.splice(i, 1);
break;
}
}
// 修改请求UA为hello world ua
details.requestHeaders.push({
name: 'User-Agent',
value: customUa
});
return { requestHeaders: details.requestHeaders };
}
// 前面的sendMessage获取getCustomUserAgent,会被这里监听
const onRuntimeMessageListener=()=> {
chrome.runtime.onMessage.addListener(function (msg, sender, callback) {
if (msg.type==='getCustomUserAgent') {
callback({
customUa
});
}
});
}
const init=()=> {
onRuntimeMessageListener()
onBeforeSendHeadersListener()
}
init()
复制代码
content.js
演示如何往网页中插入代码
function setScript({ code='', needRemove=true }=params) {
let textNode=document.createTextNode(code)
let script=document.createElement('script')
script.appendChild(textNode)
script.remove()
let parentNode=document.head || document.documentElement
parentNode.appendChild(script)
needRemove && parentNode.removeChild(script)
}
setScript({
code: `alert ('hello world')`,
})
复制代码
大体上和小例子差不都,只是功能相对复杂一些,会涉及到
这里就不贴详细的代码实现了。
工作中咱们时长会遇到一些阻碍我们提高工作效率的问题,这些问题或许是因为老方案设计不合理、或许是因为流程又臭又长,又或许是现有功能不满足新的需求。等等,如果能做到这几点,不仅对自己的成长有所帮助,对团队也会有所贡献。
以上就是这篇文章的全部内容啦!愿大家晚安,下次再见。
*请认真填写需求信息,我们会在24小时内与您取得联系。