2
头图

Preface

Simple knowledge points combined with appropriate business scenarios can often achieve unexpected results. This article will use the three most basic front-end knowledge that everyone knows to explain how to help the daily work of the operating lady and the company's 48+ front-end development students, so that their work efficiency can be greatly improved.

You may gain after watching

  1. use vue to write a chrome plug-in from scratch
  2. how to use Object.defineProperty to intercept fetch requests`
  3. How to use the oil monkey script to develop an extension program
  4. some thoughts daily Increasing Efficiency

Example of getting started with oil monkey script

Because the next two gadgets are implemented based on the oil monkey script, so let’s learn about it in advance

What is the oil monkey script?

Script (1612d710d8a0d8 Tampermonkey ) is a popular browser extension that can run user-written extension scripts to achieve various functions, such as removing ads, modifying styles, downloading videos, etc.

How to write an oil monkey script?

1. Install oil monkey

Take chrome browser extension as an example, click here to install

After the installation is complete, you can see this in the upper right corner

image.png

2. Add sample script 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...
})();

That's right, when you open any https://juejin.cn/* hello world will pop up, while other pages such as https://baidu.com will not.

At this point you have completed one of the simplest oil monkey scripts. Next, let's take a look at using the same simple code to solve a practical problem! O(∩_∩)O

3 lines of code to let SSO automatically log in

What's the question?

1. One day the operating

After an operation, the matter was finally settled, and I was in a beautiful mood.

But she thought to herself, why do I have to log in once for each system, not happy o( ̄ヘ ̄o#)

1.gif

2. Woke up in the afternoon, the leader asked to change the configuration in the morning start to operate immediately)

But what she didn't expect was that the login page in the morning seemed like she hadn't seen her for a long time, and she had another close contact with the young lady 😭

At this time, her heart has begun to collapse

2.gif

3. But this is not the end, she will be in this state every day from now on

3.gif

Where is the pain point?

After reading the animated picture above, I guess you are already scolding your mother for the little sister. What kind of stuff are you doing? It's too rubbish. SSO is a unified login. What are you doing here?

Yes, my heart is the same as you . There are ten thousand grass mud horses not worthy of being a human being. I don’t do anything for a day. I just jump to the login page and enter the user. I clicked the login button with my name and password, and over time, the first sentence that my friends said when they met was not "Have you eaten?", but "Did you log in?".

But after complaining, we still have to think about how to solve these two pain points through technical means to achieve only need to log in once.

1. After logging in to system A, you need to log in again to other systems.

2. The login time is only 2 hours, after 2 hours, you need to log in

How to solve it?

The root cause is that the company's SSO unified login scheme is designed to be problematic, so they need to be promoted to modify it, but this is a relatively long-term process. Is there any way to make us log in happily in the short term?

Pain point 1: 1. After logging in to system A, it needs to log in again when running to other systems. Can't recover

Pain point 2: only 2 hours. After 2 hours, the need to log in again is unable to recover.

It is not easy for us to directly invade various systems to modify the login logic and modify the login timeliness, but we can do some on the 1612d710d8a574 login page (example)

image.png

The key is:

  1. User name input box
  2. password input box
  3. Click the button

Therefore, you can use the oil monkey script to insert the code when DOMContentLoaded to realize automatic login and reduce the manual operation process. The general principle is as follows.

结构图.jpg

// ==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() // 自动提交登录

is not too simple, so simple that it is horrible, hateful, and spitting! ! ! , There is no technical content

is too simple, so simple that it is horrible, hateful, and spitting! ! ! , There is no technical content

is not too simple, so simple that it is horrible, hateful, and spitting! ! ! , There is no technical content

image.png

Yes, on this 😄, little sister for the first time to help her solve the problem for a long time at night to ask me to eat Spicy, I also boast "technology" Good ( here instead of driving)

Try the effect

The script that does not enable automatic login in the first half of the gif requires manual login, and you can log in automatically when the second half is enabled.

autoLogin.gif

Intercept the fetch request, leaving only the pages you want

What's the question?

Common debugging methods of the front end

  1. chrome inspect
  2. vconsole
  3. weinre
  4. and many more

These methods have their own advantages and disadvantages. For example, chrome inspect needs to be used over the wall for the first time, which is only applicable to Android; vconsole is not convenient for direct debugging styles; weinre is only suitable for debugging styles.

For these reasons, the company developed a remote debugging tool a long time ago, which can easily add and delete DOM structure, debug style, view request, and view application modification to take effect immediately on the phone after

autoLogin2.gif

remote debugging platform use process

His usage process is probably like this

  1. Open remote debugging page list

    This page contains a link to the debugging page opened by everyone in the hundreds of

image.png

  1. Click on the page you want to debug, you can enter debugging like chrome console

image.png

After reading the process, you should probably know where the problem is. The remote debugging page list contains not only my own pages, but also many others, which makes it difficult to quickly find the page you want to debug.

How to solve it?

problem analysis

Is there any way I can quickly find the page I want to debug? In fact, by observing and parsing this page, you will find that the list is

  1. Obtained by sending a request
  2. Device keyword included in the response

image.png

Interception request

So clever you have guessed, we can intercept the fetch Object.defineProperty , filter the device so that only the device we specify is in the list ( after all, the device debugged during the usual development is basically fixed, and the probability of the device being exactly the same is very low. Yes, so specifying a device actually uniquely identifies itself) page.

How to do it in detail?


// ==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
        })
      }
    }
  })
})()

Try the effect

As you can see from the figure below, there are 37 pages before filtering, and only 3 are left after filtering. You can find the page you want to debug in an instant, and you no longer have to look for your own one from hundreds of pages!

image.png

Helping the company's 45+ front-end development-the beginning and end of chrome plug-ins

One-click setting of ua through the plug-in simulates the user login status and improves development efficiency.

Look at the result first

plug-in usage

new.gif

plug-in usage results

Team 48+ friends are also using it

image.png

image.png

Background and problems

There are many scenarios in the daily c-side business that require the user log in before they can proceed normally, and the development phase is basically developed through chrome simulation of mobile devices, so it often involves simulating user login in the chrome browser, which involves the following three Step ( this step is more cumbersome).

Note: Keeping the user's login status is generally through cookies, but also through headers. For example, our company rewrites ua to do

  1. Get ua: Go to the company's UA generation platform and enter the mobile phone number to generate ua
  2. add ua: copy ua to chrome devtool set/modify device
  3. use ua: select the newly added ua, refresh the page, re-develop and debug

ua.gif

Look at a conversation

The girl who just graduated in 1998 next door:

expired again, who squeezed me down again?

Okay, wait a while, I’ll change my account to test

so troublesome! It takes so many steps to simulate a user's information, which is so annoying! ! !

Me, curious uncle:

Under "careful" understanding, she is working on an h5 activity project. The scene is complex and involves many states. You need to use different accounts for testing.

simulate one or two users, but at the moment, my lady has tested so many scenes and has simulated many (anyone will be annoyed)

company’s login system is single sign-on. An account that has finally been simulated may be used by others, but the result is rejected again. I have to regenerate it. I TM

Seeing her crying little eyes, as a friendly neighbor at the next table, I only have one thing in my mind at the moment...! Help her solve this annoying problem.

Analyze and solve problems

Through the above introduction, you should be able to feel the troubles when we need to switch accounts frequently for testing during the development stage. The relatively cumbersome ua generation process has caused it to be a time-consuming and laborious trouble

Is there any way to improve our development efficiency and don't waste it on this kind of thing? Do it step by step together

are the

Provide a convenient way to simulate ua to help improve development efficiency.
  1. Basic appeal: In the local development stage, I hope that there will be more convenient way to simulate user login
  2. Multiple accounts: A project requires multiple accounts, and the accounts between different projects can be shared or different
  3. designated domain: only the designated domain is required to simulate ua, which cannot affect the normal use of the browser
  4. Expiration processing: After the account expires, it can be generated actively without manual reacquiring

how to solve

  1. Requirement 1: Combining with the previous generation ua stage, we can in some way allow users to generate ua directly on the current page without jumping out, one-click setting to omit the manual process
  2. Requirement 2: Provide multi-account management function, can directly select switch ua
  3. Requirement 3: Restrict the specified domain, the ua will take effect
  4. Requirement 4: When an expired account is used, it can be regenerated with one click

is a chrome plug-in

  1. sent by the ajax request in the browser cannot be modified directly, but the chrome plug-in can modify the requested ua ( is a very important point )
  2. The chrome plug-in popup mode can be opened directly on the current page without jumping out of the development page, reducing the jumping out process

Write a chrome plugin from scratch with vue

For space reasons, here is only a simple introduction at the sample level. If you want to learn more about the writing of chrome plug-ins, you can refer

Start with a small example

Next, we will take the following page as an example to illustrate how to write it in vue.

ua3.gif

Basic functions

  1. bottom tab switching area : viewA , viewB , viewC
  2. Intermediate content area : Switch view A, B, C to display the corresponding page

content part

With the help of the Chrome browser to insert scripts into web pages, we will demonstrate how to insert scripts and play a hello world

and background communication part

The popup completes the user's main interaction, click on the viewA page to get the customized ua information

modify the ajax request ua part

Will demonstrate if the request header is modified through the chrome plug-in

1. Understand the composition of a chrome plug-in

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

1. manifest.json

Almost everything must be declared here, permissions, resources, pages, etc.

{
  "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

The background can be considered as a resident page, with high permissions, almost all APIs can be called, and it can communicate with popups, content scripts, etc.

3. content script

A form of chrome plug-in injecting scripts into the page (both js and css are available)

4. popup

popup is a small window webpage that opens when you click on the browser_action or page_action icon. The webpage will be closed when the focus leaves the webpage.

For example, we want to use vue to make the page.

image.png

2. Rewrite vue.config.js

The structure of manifest.json's file reference basically determines the file path after packaging

packaged path

// 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

source directory


├── 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

modify vue.config.js

The main needs to be modified slightly to become multi-page packaging, pay attention to the output directory structure.

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. Hot refresh

We hope that after modifying the plug-in source code for packaging, the page corresponding to the chrome plug-in can be actively updated. Why is it called hot refresh instead of hot update? Because it actually refreshes the page globally and does not save the state.

Here is a recommended solution on github crx-hotreload

4. Complete the small example

new.gif

File directory structure


├── 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

The three pages are basically the same. Only the background color and the copy content are different. Here I will only post the code of viewA.

It should be noted that the popup and background will be demonstrated here, and the background background data is obtained through the sendMessage method.


<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

Demonstrate how to insert code into a web page

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

ua3.gif

About one-click setup ua plugin

Generally speaking, it is not the same as the small example, but the function is relatively complicated, which will involve
  1. Data local storage chrome.storage.sync.get|set , chrome.tabs.query and other API
  2. Popup and background communication, content and background communication
  3. Intercept request to modify UA
  4. The rest is roughly the conventional vue code writing!

The detailed code implementation is not posted here.

Some thoughts on daily efficiency improvement

During our work, we will encounter some problems that hinder us from improving work efficiency. These problems may be due to the unreasonable design of the old scheme, or the smelly and long process, or the existing functions that do not meet the new requirements. Wait, if you can do these points, it will not only help your growth, but also contribute to the team.
  1. mentality: Found the problem and actively try to solve the problem, not being a bystander
  2. Keep learning ability: After discovering the problem, if the solution is not in your knowledge reserve, you must try to learn new things ( before writing a one-click setting of the UA plugin) , Out of the comfort zone, will learn more
  3. Keep an enthusiastic attitude: everyone encounters different problems, take the initiative to discuss with colleagues or friends, and stretch out your hands when needed
  4. 1612d710d8caa2 Execution ability that affect efficiency (for example, there are others) as the devil, act immediately to reach the devil, don’t delay
  5. Learn to promote: Maybe the plug-in you wrote at the beginning just solved your own problems, but the same working environment may be encountered by others, so learn to share and promote it outside.

Meet bye

The above is all the content of this article! Good night everyone 🌛 and see you next time.

refer to

  1. [Dry goods] Chrome plug-in (extension) development guide
  2. Oil Monkey Scripting Tutorial

前端胖头鱼
3.7k 声望6.2k 粉丝