Vite + Vue3 + TS + VScode + Volar, who knows? Ok, I used it, I do know, I can do it again, I learned to move.

Introduction:

  • What is theVue3 ?The official documentation
  • What is theElement-plus Github
  • What is theVite ? Next generation front-end development and build tools
  • What is theChrome Extension ?Chrome expand
  • What is theVolar ? Github

This article will not focus too much on the basic use of plug-in internal logic and technology stack. It will cover how to develop a real project from scratch using Vite and Vue3, as well as some of the code design trade-offs and packaging problems encountered in the build and their solutions. About the choice of technology stack, no skills, my project I say calculate, I want to use which on which 😎.

Start with request interception πŸ€”

The bridge between the front-end and the back-end Battle is Ajax requests, which can be connected to the back-end services along the network cable. In a webApp, we almost always use some means to intercept requests, such as mixing some public parameters tokenUID, encrypting some data, deleting some data, and even canceling some requests. Or request response interception, such as unified error code handling, unified data formatting and so on. In the community, the well-known Axios provides two of the above Interceptors to unify the pre – and post-data reporting sessions. So how can we intercept requests without Axios? Some students said, “You can modify the XHR prototype method, you can replace the original fetch, to magic change ~” yes, but I don’t need to. Today I’m going to show you the ability to block requests using Chrome Extension. Damn it, I did it again!

Say dry dry πŸ•Ά

Initialize the warehouse

A few key points to remember:

  • The product of Chrome Extension is multiple HTML, so we want to create a multi-page Vite project, powerful Vite already support
  • The packaged product must havemanifest.jsonThis is equivalent toChrome ExtensionEntry file, each releaseversionAutomatic + 1

Create a project

yarn create @vitejs/app
Copy the code

Follow the prompts to create a vuE-TS project.

createmanifest.json

{
  "name": "Bad Request"."version": "0.0.0"."description": "Bad Request"."permissions": [
    "activeTab"."declarativeContent"."storage".// Get the storage permission to store the API link we want to intercept
    "webRequest".// Get read permission to get things done
    "webRequestBlocking".// Get request abort permission to abort the request
    "<all_urls>" // Get permissions on all urls]."background": {
    "page": "background/index.html"."persistent": true // Make sure background.js is always running in the background and intercepting is always in effect
  },
  "options_page": "options/index.html"."content_security_policy": "script-src 'self' 'unsafe-eval'; object-src 'self'"."page_action": {
    "default_title": "request"."default_icon": "images/logo.png"."default_popup": "popup/index.html"
  },
  "devtools_page": "devtool/index.html"."icons": {
    "16": "images/logo.png"."32": "images/logo.png"."48": "images/logo.png"."128": "images/logo.png"
  },
  "manifest_version": 2."content_scripts": [{"matches": [
        "<all_urls>"]."js": [
        "content.js"]]}}Copy the code

It was transformed into a multi-entrance project

Then according to the official Vite documentation to transform it into a multi-page project. The multi-page vite.config.ts is as follows:

export default defineConfig{
  // other setting...
  build: {
    rollupOptions: {
      input: {
        /** * Click the plug-in icon popup window ** /
        popup: resolve(__dirname, 'popup/index.html'), 
        /** * Chrome Devtool Pane ** /
        devtoolPage: resolve(__dirname, 'devtoolPage/index.html'), 
        /** * The plugin's core JS is always active in the background, listening for all requests ** /
        background: resolve(
          __dirname,
          'background/index.html'
        ),
        /** * Load the portal for chrome DevTool Pane ** /
        devtool: resolve(__dirname, 'devtool/index.html'),
        /** * Plug-in setup page ** /
        options: resolve(__dirname, 'options/index.html'),
        /** */ ** */
        content: resolve(__dirname, 'src/content.ts'),},output: {
        entryFileNames: '[name].js',}}},// other setting...
}
Copy the code

The introduction ofElement Plus

According to its official documentation, the configuration of Vite load on demand, the configuration of Vite.config.ts is as follows:

export default defineConfig{
  // other setting...
  plugins: [
    vue(),
    styleImport({
      libs: [{libraryName: 'element-plus'.esModule: true.ensureStyleFile: true.resolveStyle: (name) = > {
            return `element-plus/lib/theme-chalk/${name}.css`
          },
          resolveComponent: (name) = > {
            return `element-plus/es/${name}`},},],},// other setting...
}
Copy the code

Element Plus pins don’t poke! Big work done, let us happy development ~

How do I block requests in Chrome Extension? πŸ–

One line of code is enough

Put a line like this in background.ts:

chrome.webRequest.onBeforeRequest.addListener( handlerRequest, { urls: [' < all_urls > '],}, / / definition for what permissions [' blocking ', 'requestBody', 'extraHeaders'])Copy the code

In the handlerRequest, we get the Details argument. Depending on the return value of this function, Chrome does the following:

  • return { redirectUrl: newurl}, forward the request
  • return { cancel: true }.abortrequest
/ / the type is chrome. WebRequest. WebRequestDetails
function handlerRequest(
  details: chrome.webRequest.WebRequestDetails
) {
  // Note that proxy and block need to be defined by yourself
  /** * proxy forward */
  if (proxy) {
    return {
      redirectUrl: details.url.replace(
        proxy.origin,
        proxy.target
      ),
    }
  }

  /** * request to intercept ** /
   if (block) {
       return { cancel: true}}}Copy the code

Master pass, point so far; Intercepting a request is as simple as that.

Add some details πŸ“

Once we know how, we can refine our overall plug-in requirements

  • Supports interception of corresponding requests, such as towww.baidu.comRequest or compare key interfaces
  • Make a switch for blocking. We can turn blocking on or off. Click the plugin icon to pop up the switch
  • Listen for special requests, such as buried requests, and log our buried flow in a new DevTool Pane

After reviewing these three requirements, let’s make the design draft. Let’s make the design draft of the plug-in popover and devtoolPane first

πŸ• πŸ•‘ πŸ•’ πŸ•“

The design is ready, above

Popup window

Use Element-Plus for basic layout and form controls

devtool Pane

Reference Vue Devtool panel to do the design

Switch code design

  • Use the built-in storage of Extension. Similar to localStorage, the switch status is stored
  • backgroud.jsThe stored value can be read to determine whether to intercept
  • Read this storage every time you activate Chrome Extension Popup and display it

The code is as follows:

<! -- I also use setup syntax sugar -->
<script setup lang="ts">
import {
  ElIcon,
  ElForm,
  ElFormItem,
  ElInput,
  ElSwitch,
} from 'element-plus'
import { ref, watch } from 'vue'
/** * Storage status */
function saveCurrentStatus(
  type: string,
  value: boolean | string | Array<any>
) {
  // eslint-disable-next-line no-undefchrome.storage? .sync? .set({ [type]: value },() = > {
    console.log('Setup successful')})}// Define the switch
const blocking = ref(false)
// Use Vue 3's watch to store the state each time the value changes

watch([blocking], () = > {
  saveCurrentStatus('blocking', blocking.value)
})
Setup is equivalent to the Created lifecycle
const initStatus = () = > {
    conststorage = chrome.storage? .sync storage? .get('blocking'.(data) = > {
      blocking.value = data.blocking || false
    })
}
initStatus()
<script>

<template>
    <el-form>
        <el-form-item label="Intercept" size="mini">
            <el-switch
            v-model="blocking"
            active-color="#2F86F6"
            />
        </el-form-item>
    </el-form>
</template>
Copy the code

Background.js listens for storage changes

/** * listen for storage changes */
chrome.storage.onChanged.addListener((changes) = > {
    console.log(changes)
})
Copy the code

DevtoolPane code design

The key here is the communication between background.js and devtoolPane, which is easy to PostMessage

  • DevtoolPane creates the connection and sends the message
const backgroundPageConnection = chrome.runtime?.connect({
    name: 'devtool-page', }) backgroundPageConnection? .postMessage({name: 'init'.tabId: chrome.devtools.inspectedWindow.tabId, // Currently devtoolPane tabId
})
Copy the code
  • background.jsInterface message, get the Pane
let devtool = null
const connections: Record<string.any> = {}

chrome.runtime.onConnect.addListener((port) = > {
    port.onMessage.addListener(message= > {
        if (message.name === 'init') {
            connections[message.tabId] = port
            devtool = port
        }
    })
})
// Then use Devtool to send messages to and from the devtoolPane
// Here we send the request body of the buried point to parse
function devtoolandler(details: any) {
  devtool && devtool.postMessage(details)
}
Copy the code

Build and publish πŸ“¦

Json version +1 = manifest.json version +1 = manifest.json version +1 = manifest.json

  • copy, the use ofrollup-plugin-copyAfter each build, copy the file todistVery soon

The configuration of vite.config.ts is as follows

export default defineConfig{
  // other setting...
  plugins: [
    copy({
      verbose: true,
      hook: 'writeBundle',
      targets: [
        {
          src: 'manifest.json',
          dest: 'dist',
        },
      ],
    }),
  ],
  // other setting...
}
Copy the code
  • The version number automatically increases by 1

    • Upgrade the version number with Node-semver
    • Use sed or node fs to modify the file
  • Once you’ve built it, you need to compress the entire Dist, which is required to publish it in the Chrome Extension Store

To sum up, we get our release script

#! /usr/bin/env zx

const semverInc = require('semver/functions/inc')

let manifest = require('.. /manifest.json')
console.log(
  chalk.yellow.bold(`Current verion: ${manifest.version}`))let types = ['patch'.'major'.'minor']
let type = await question(
  chalk.cyan(
    'Release type? Press Tab twice for suggestion \n'
  ),
  {
    choices: types,
  }
)
let version = ' '
if (type! = =' ' || types.includes(type)) {
  version = semverInc(manifest.version, type)
  console.log(
    chalk.green.bold(`Release verion? ${version}') // Use the sed command to change version $' sed -i' ' s/${manifest.version}/${version}/g manifest.json`
} else {
  await $`exit'} // build await $' yarn build '// git await $' git add.' await $' git commit -m'Update version to ${version}'`
await $`git tag v${version}`
await $`git push origin refs/tags/v${version}`
await $`git push origin HEAD:refs/for/master '// compress await $'cddist && zip -r bundle.zip * && mv bundle.zip .. / `Copy the code

Then you can go to the Chrome Extension Store and publish

Note:

  • Developers need to pay$5, can publish the code toChrome Extension Store
  • Review time is variable, and it can be a long time during the pandemic, as Google is on holiday every day

So far, I have used Vue3+ Element Plus + TS + Vite to develop a Chrome Extension. The efficiency is very high, the code is very handsome, I did not cheat me.

yyx! yyds!

Finally, what would you like to teach the author, direct message, welcome you to communicate with me ~

πŸ‘‹