看完您可以会收成
用vue从零开始写一个chrome插件如何用Object.defineProperty拦截fetch要求`如何利用油猴脚本开拓一个扩展程序日常提效的一些思考油猴脚本入门示例由于接下来的两个小工具都是基于油猴脚本来实现的,以是我们提前先理解一下它
油猴脚本(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中前半部分没有开启自动登录的脚本须要手动登录,后半部开启了就可以自动登录了。
拦截fetch要求,只留你想要的页面问题是什么?
前端常见的调试办法
chrome inspectvconsoleweinre等等这些办法都有各自的优缺陷,比如chrome inspect第一次须要翻墙才能利用,只适用于安卓; vconsole未便利直接调试样式; weinre只适用于调试样式等。
基于这些缘故原由,公司良久之前搞了一个远程调试工具,可以很方便的增删DOM构造、调试样式、查看要求、查看application 修正夹帐机上立即生效。
远程调试平台利用流程
他的利用流程大概是这样的
打开远程调试页面列表此页面包含测试环境所有人打开的调试页面链接, 多的时候有上百个点击你要调试的页面,就可以进入像chrome掌握台一样调试了看完流程你该当大概知道问题在哪里了, 远程调试页面列表不仅仅包含我自己的页面,还包括很多其他人的,导致很难快速找到自己想要调试的页面
该如何办理?问题解析
有什么办法能让我快速找到自己想要调试的页面呢?实在不雅观察解析这个页面会创造列表是
通过发送一个要求获取的相应中包含设备关键字拦截要求
以是聪明的你已经猜到了,我们可以通过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个,瞬间就找到你要调试页面,再也不用从几百个页面中探求你自己的那个啦!
助力全公司45+前端开拓 - chrome插件的始与终
通过插件一键设置ua,仿照用户登录状态,提高开拓效率。
先当作果插件利用办法
插件利用结果
团队48+小伙伴也利用起来了
背景和问题
日常c端业务中有很多场景都须要用户登录后才能正常进行,而开拓阶段基本都是通过chrome仿照手机设备来开拓,以是每每会涉及到在chrome浏览器中仿照用户登录,其涉及以下三步(这个步骤比较繁琐)。
备注:保持用户的登录态一样平常是通过cookie,但也有通过header来做,比如我们公司是改写ua来做的
获取ua: 前往公司UA天生平台输入手机号天生ua添加ua: 将ua复制到chrome devtool设置/修正device利用ua: 选择新添加的ua,刷新页面,重新开拓调试来看一段对话
隔壁98年刚毕业妹子:
又过期了,谁又把我挤下去了嘛
好的,稍等一会哈,我换个账号测测
好麻烦哎!
仿照一个用户信息,要这么多步骤,好烦呀!
!
!
我,好奇的大叔:
“细心”理解下,她正在做一个h5活动项目,场景繁芜,涉及的状态很多,须要用不同的账号来做测试。
仿照一两个用户还好,但是此刻小姐姐测这么多场景,已经仿照了好多个(谁都会烦啊)
公司的登录体系是单点登录,一个好不容易仿照的账号,有可能别人也在用,结果又被顶掉了,得重新天生,我TM
看着她快气哭的小眼神,作为隔壁桌友好的邻居,此刻我心里只想着一件事...!
帮她办理这个恼人的问题。
通过上面的先容您该当可以觉得到我们开拓阶段碰着须要频繁切换账号做测试时的烦恼,相对繁琐的ua天生过程导致了它一定是个费时费力的麻烦事。
有没有什么办法让我们的开拓效率得到提升,别摧残浪费蹂躏在这种事情上呢?一起一步步做起来
需求有哪些
供应一种便捷地仿照ua的办法,助力开拓效率提升。
基本诉求:本地开拓阶段,希望有更便捷的办法来仿照用户登录多账号: 一个项目须要多个账号,不同项目间的账号可以共享也可以不同指定域: 只有指定的域下才须要仿照ua,不能影响浏览器正常利用过期处理: 账号过期后,可以主动天生,无需手动重新获取如何办理
需求1:结合前面天生ua阶段,我们可以通过某种办法让用户能直接在当前页面天生ua,无需跳出,一键设置省略手动过程需求2:供应多账号管理功能,能直接选中切换ua需求3:限定指定域,该ua才生效需求4:当利用到过期账号时,可一键重新天生即可为什么是chrome插件
浏览器中发送ajax要求的ua无法直接修正,但是chrome插件可以修正要求的ua(很主要的一点)chrome插件popup模式可直接在当前页面打开,无需跳出开拓页面,减少跳出过程用vue从零开始写一个chrome插件篇幅缘故原由,这里只做示例级别的大略先容,如果您希望详细理解chrome插件的编写可以参考这里
从一个小例子开始接下来我们会以下页面为例,解释用vue如何写出来。
基本功能
底部tab切换区域:viewA、viewB、viewC中间内容区域:切换viewA、B、C分别展示对应的页面content部分
借助chrome浏览器可以向网页插入脚本的特性,我们会演示如何插入脚本并且在网页加载的时候弹一个hello world
popup与background通信部分
popup完成用户的紧张交互,在viewA页面点击获取自定义的ua信息
修正ajax要求ua部分
会演示如果通过chrome插件修正要求header
1. 理解一个chrome插件的构成manifest.jsonbackground scriptcontent scriptpopup1. 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做的页面。
2. 改写vue.config.js
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复制代码
3. 热刷新
我们希望修正插件源代码进行打包之后,chrome插件对应的页面能主动更新。为什么叫热刷新而不是热更新呢?由于它实在是全局刷新页面,并不会保存状态。
这里推举一个github上的办理方案crx-hotreload
4. 完成小例子编写文件目录构造
├── 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 = falsenew 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')`,})复制代码
大体上和小例子差不都,只是功能相对繁芜一些,会涉及到
数据本地存储chrome.storage.sync.get|set、chrome.tabs.query等APIpopup与background通信、content与background通信拦截要求修正UA其他的大体便是常规的vue代码编写啦!这里就不贴详细的代码实现了。
日常提效的一些思考事情中咱们时长会碰着一些阻碍我们提高事情效率的问题,这些问题或许是由于老方案设计不合理、或许是由于流程又臭又长,又或许是现有功能不知足新的需求。等等,如果能做到这几点,不仅对自己的发展有所帮助,对团队也会有所贡献。
主人翁心态: 创造了问题主动考试测验去办理问题,不做察看犹豫者保持学习力: 创造问题之后,办理方案如果不在你的知识储备范围,一定要考试测验去学习新的东西(惭愧,没写一键设置UA插件之前,我自己完备没写过chrome插件),走出舒适圈,会学会更多保持热心态:每个人碰着的问题是不一样的,主动和同事或者朋友谈论,须要时伸出你的双手实行力:把影响效率(举例,还有其他)的事情算作妖怪,立时行动起来,达到妖怪,不要一拖再拖学会推广:大概一开始你写的插件只是办理了自己的问题,但同样的事情环境,别人大概也会碰着,要学会往外分享和推广相约再见以上便是这篇文章的全部内容啦!
愿大家晚安,下次再见。