前言

简略的知识点搭配适合的业务场景,往往能起到意想不到的成果。这篇文章会用三个最根底人人都晓得的前端常识来阐明如何助力经营小姐姐、公司48+前端开发同学的日常工作,让他们的工作效率失去极大地晋升。

看完您能够会播种

  1. 用vue从零开始写一个chrome插件
  2. 如何用Object.defineProperty拦挡fetch申请`
  3. 如何应用油猴脚本开发一个扩大程序
  4. 日常提效的一些思考

助力全公司45+前端开发 - chrome插件的始与终

通过插件一键设置ua,模仿用户登录状态,进步开发效率。

先看后果

插件应用形式

插件应用后果

团队48+小伙伴也应用起来了

背景和问题

日常c端业务中有很多场景都须要用户登录后能力失常进行,而开发阶段根本都是通过chrome模拟手机设施来开发,所以往往会波及到在chrome浏览器中模仿用户登录,其波及以下三步(这个步骤比拟繁琐)。

备注:放弃用户的登录态个别是通过cookie,但也有通过header来做,比方咱们公司是改写ua来做的

  1. 获取ua: 返回公司UA生成平台输出手机号生成ua
  2. 增加ua: 将ua复制到chrome devtool设置/批改device
  3. 应用ua: 抉择新增加的ua,刷新页面,从新开发调试

来看一段对话

隔壁98年刚毕业妹子:

又过期了,谁又把我挤下去了嘛

好的,稍等一会哈,我换个账号测测

好麻烦哎!模仿一个用户信息,要这么多步骤,好烦呀!!!

我,好奇的大叔:

“仔细”理解下,她正在做一个h5流动我的项目,场景简单,波及的状态很多,须要用不同的账号来做测试。

模仿一两个用户还好,然而此刻小姐姐测这么多场景,曾经模仿了好多个(谁都会烦啊)

公司的登录体系是单点登录,一个好不容易模仿的账号,有可能他人也在用,后果又被顶掉了,得从新生成,我TM

看着她快气哭的小眼神,作为隔壁桌敌对的街坊,此刻我心里只想着一件事...!帮她解决这个宜人的问题。

剖析和解决问题

通过下面的介绍您应该能够感觉到咱们开发阶段遇到须要频繁切换账号做测试时的懊恼,绝对繁琐的ua生成过程导致了它肯定是个费时费力的麻烦事。

有没有什么方法让咱们的开发效率失去晋升,别节约在这种事件上呢?一起一步步做起来

需要有哪些

提供一种便捷地模仿ua的形式,助力开发效率晋升。
  1. 根本诉求:本地开发阶段,心愿有更便捷的形式来模仿用户登录
  2. 多账号: 一个我的项目须要多个账号,不同我的项目间的账号能够共享也能够不同
  3. 指定域: 只有指定的下才须要模仿ua,不能影响浏览器失常应用
  4. 过期解决: 账号过期后,能够被动生成,无需手动从新获取

如何解决

  1. 需要1:联合后面生成ua阶段,咱们能够通过某种形式让用户能间接在以后页面生成ua,无需跳出,一键设置省略手动过程
  2. 需要2:提供多账号治理性能,能间接选中切换ua
  3. 需要3:限定指定域,该ua才失效
  4. 需要4:当应用到过期账号时,可一键从新生成即可

为什么是chrome插件

  1. 浏览器中发送ajax申请的ua无奈间接批改,然而chrome插件能够批改申请的ua(很重要的一点
  2. chrome插件popup模式可间接在以后页面关上,无需跳出开发页面,缩小跳出过程

用vue从零开始写一个chrome插件

篇幅起因,这里只做示例级别的简略介绍,如果您心愿具体理解chrome插件的编写能够参考这里

从一个小例子开始

接下来咱们会以下页面为例,阐明用vue如何写进去。

基本功能

  1. 底部tab切换区域viewAviewBviewC
  2. 两头内容区域:切换viewA、B、C别离展现对应的页面

content局部

借助chrome浏览器能够向网页插入脚本的个性,咱们会演示如何插入脚本并且在网页加载的时候弹一个hello world

popup与background通信局部

popup实现用户的次要交互,在viewA页面点击获取自定义的ua信息

批改ajax申请ua局部

会演示如果通过chrome插件批改申请header

1. 理解一个chrome插件的形成

  1. manifest.json
  2. background script
  3. content script
  4. popup

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做的页面。

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')`,})

对于一键设置ua插件

大体上和小例子差不都,只是性能绝对简单一些,会波及到
  1. 数据本地存储chrome.storage.sync.get|setchrome.tabs.query等API
  2. popup与background通信、content与background通信
  3. 拦挡申请批改UA
  4. 其余的大体就是惯例的vue代码编写啦!

这里就不贴具体的代码实现了。

油猴脚本入门示例

因为接下来的两个小工具都是基于油猴脚本来实现的,所以咱们提前先理解一下它

油猴脚本是什么?

油猴脚本(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

3行代码让SSO主动登录

问题是什么?

1. 有一天经营小姐姐要在几个零碎之间配置点货色

一顿操作,终于把事件搞定了,情绪美美的。

然而她心想,为啥每个零碎都要我登录一次,不开心 o( ̄ヘ ̄o#)

2. 下午一沉睡来,领导让把上午的配置从新改一下(渎职的小姐姐马上开始操作)

然而让她没想到的是:上午的登录页面好像许久没有见到她一样,又和小姐姐来了一次密切接触

此时,她的心田曾经开始解体了

3. 然而这不是完结,当前的每一天她都是这种状态

痛点在哪里?

看完下面的动图,我猜你曾经在替小姐姐一起骂娘了,这做的什么玩意,太垃圾了。SSO是对立登录,你们这搞的是什么货色。

是的,我的心田和你一样愤愤不平, 一样有一万个草泥马在奔流,这是哪个sb设计的计划,几乎不配做人,一天啥事也不干,尽是跳登录页,输出用户名明码点登录按钮了,长此以往,敌人间见面说的第一句话不是“你吃了吗?”,而是“你登录了吗?”。

不过吐槽完,咱们还是要想想如何通过技术手段解决这两个痛点,达到只须要登录一次的目标

1. 在A零碎登录之后,跑到其余零碎须要从新登录。

2. 登录时效只有2小时,2小时后,须要从新登录

该如何解决?

根本原因还是公司的SSO对立登录方案设计的有问题,所以须要推动他们批改,然而这是一个绝对长期的过程,短期内有没有什么方法能让咱们欢快的登录呢?

痛点1: 1. 在A零碎登录之后,跑到其余零碎须要从新登录。已无力回天

痛点2: 2. 登录时效只有2小时,2小时后,须要从新登录已无力回天

咱们不好间接侵入各个系统去革新登录逻辑,革新其登录时效,然而却能够对登录页面(示例)做点手脚

最要害的是:

  1. 用户名输入框
  2. 明码输入框
  3. 点击按钮

所以能够借助油猴脚本,在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申请,只留你想要的页面

问题是什么?

前端常见的调试形式

  1. chrome inspect
  2. vconsole
  3. weinre
  4. 等等

这些形式都有各自的优缺点,比方chrome inspect第一次须要翻墙能力应用,只实用于安卓; vconsole不不便间接调试款式; weinre只实用于调试款式等。

基于这些起因,公司很久之前搞了一个近程调试工具,能够很不便的增删DOM构造、调试款式、查看申请、查看application 批改后手机上立刻失效。

近程调试平台应用流程

他的应用流程大略是这样的

  1. 关上近程调试页面列表

    此页面蕴含测试环境所有人关上的调试页面链接, 多的时候有上百个

  1. 点击你要调试的页面,就能够进入像chrome控制台一样调试了

看完流程你应该大略晓得问题在哪里了, 近程调试页面列表不仅仅蕴含我本人的页面,还包含很多其他人的,导致很难疾速找到本人想要调试的页面

该如何解决?

问题解析

有什么方法能让我疾速找到本人想要调试的页面呢?其实察看解析这个页面会发现列表是

  1. 通过发送一个申请获取的
  2. 响应中蕴含设施关键字

拦挡申请

所以聪慧的你曾经猜到了,咱们能够通过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个,霎时就找到你要调试页面,再也不必从几百个页面中寻找你本人的那个啦!

日常提效的一些思考

工作中咱们时长会遇到一些妨碍咱们进步工作效率的问题,这些问题或者是因为老方案设计不合理、或者是因为流程又臭又长,又或者是现有性能不满足新的需要。等等,如果能做到这几点,不仅对本人的成长有所帮忙,对团队也会有所奉献。
  1. 主人翁心态: 发现了问题被动尝试去解决问题,不做旁观者
  2. 放弃学习力: 发现问题之后,解决方案如果不在你的常识储备范畴,肯定要尝试去学习新的货色(羞愧,没写一键设置UA插件之前,我本人齐全没写过chrome插件),走出舒服圈,会学会更多
  3. 放弃热心态:每个人遇到的问题是不一样的,被动和共事或者敌人探讨,须要时伸出你的双手
  4. 执行力:把影响效率(举例,还有其余)的事件看成魔鬼,马上口头起来,达到魔鬼,不要一拖再拖
  5. 学会推广:兴许一开始你写的插件只是解决了本人的问题,但同样的工作环境,他人兴许也会遇到,要学会往外分享和推广

相约再见

以上就是这篇文章的全部内容啦!愿大家晚安,下次再见。

参考

  1. 【干货】Chrome插件(扩大)开发全攻略
  2. 油猴脚本编写教程