As a front-end developer, you probably can’t live without Chrome, but when you use Chrome, do you encounter some pain points?

Have you ever thought of developing a plug-in to solve your own inconvenience? The Chrome plugin already provides a number of useful apis, so you can develop your own plugin according to your own needs or interests.

The Chrome plugin does not have strict project structure requirements, but only requires the root directory to have a manifest.json file similar to the Androidmanifest.xml in Android development. This is a very important feature manifest file, according to which Chrome opens the extension to the corresponding function permissions.

So chrome extensions can be developed using a familiar stack, such as React (Vue, Angular). Here is how I developed Just Proxy with React

Stores:Chrome.google.com/webstore/de…

First, we need to determine which features our plugin needs to use in Chrome and which user interface to provide.

Since we are now developing an agent plug-in, we provide users with the following functions:

  • Configuration interface (options_page, configure proxy server Settings)
  • The proxy status of the current tag (shown by the extended Icon in the upper right corner, highlighted for the current tag using proxy, gray for not)
  • Proxy switching (Click the extended Icon interaction in the upper right corner to switch the state between proxy and unproxy)
  • Persistent storage of proxy Settings

Based on the above requirements, we have identified the contents of manifest.json as follows.

{
  "manifest_version": 2."name": "Just proxy"."description": "A simple proxy tool"."version": "1.0.2"."permissions": ["proxy"."storage"."tabs"]."browser_action": {
    "default_icon": "emoticon.png"
  },
  "icons": {
    "64": "emoticon.png"
  },
  "options_page": "index.html"."background": {
    "scripts": ["background.js"]."persistent": false}}Copy the code

On the manifest. Json more detailed content can view developer.chrome.com/extensions/…

Hot update configuration

Then we can use Webpack (recommended) as the extension building tool, because chrome extensions need to read files from the hard disk, we want to improve our experience of extension development (do not change the code every time, need to rebuild the extension, support hot update), We need the write-file-webpack-plugin to write our in-memory compilation files back to disk (loosely). Since we used React to build our extension, we also needed react-hot-loader to implement hot updates. The configuration of Webpack-dev-server is the same as when we developed the web page, which is not explained here.

Because the Chrome extension is not a web page, and it has more restrictive permissions than the web, we need to modify our manifest.json to support hot updates to the extension

{
  "manifest_version": 2."name": "Just proxy"."description": "A simple proxy tool"."version": "1.0.2"."permissions": ["proxy"."storage"."tabs"."Http://127.0.0.1:8000/ *"]."browser_action": {
    "default_icon": "emoticon.png"
  },
  "icons": {
    "64": "emoticon.png"
  },
  "options_page": "index.html"."background": {
    "scripts": ["background.js"]."persistent": false
  },
  "content_security_policy": "The default - the SRC 'self' http://127.0.0.1:8000 http://localhost:8000; Script-src 'self' http://127.0.0.1:8000 http://localhost:8000 'safe-eval'; Connect-src http://127.0.0.1:8000 http://localhost:8000 ws://127.0.0.1:8000 ws://localhost:8000; style-src * 'unsafe-inline' 'self' blob:; img-src 'self' data:;"
}
Copy the code

The one item of interest here is the content_security_policy item. I won’t go into detail here, but if you want to know more, click the link below

Developer.mozilla.org/zh-CN/docs/…

Note When configuring webpack.config.js, specify publicPath.

module.exports = {
  // ...
  output: {
    // ...
    publicPath: "http://127.0.0.1:8000/"
  }
  // ...
};
Copy the code

With the above Settings we can have a good development experience (development environment and packaging environment need different configuration, mainly for hot update)

Persistence of data

For project state management I use Redux, and for state persistence we can use redux-persist. Redux-persist uses localStorage by default, but the Chrome extension does not support it. You need to implement the Chrome plug-in version of storage yourself. The code is as follows:

export default class ChromeLocalStorage {
  getItem(key: string) {
    return new Promise(resolve= > {
      chrome.storage.local.get(key, item => resolve(item[key]));
    });
  }
  setItem(key: string, value: string) {
    return new Promise(resolve= >
      chrome.storage.local.set({ [key]: value }, resolve)
    );
  }
  removeItem(key: string) {
    return new Promise(resolve= >chrome.storage.local.remove(key, resolve)); }}Copy the code

Redux automatically sets up data synchronization between agents and multiple stores

There are two troublesome areas in our extension

  1. How to ensure that chrome proxy Settings are consistent with redux Store proxy Settings
  2. How to ensure store data consistency across multiple environments (options_page and background_script exist in this extension)

To handle the above two problems, we can implement a Redux middleware to simplify the above operations. The implementation is as follows:

const chromeProxyMiddleware = store= > {
  // Handle actions sent from other environments
  chrome.runtime.onMessage.addListener(request= >{ store.dispatch({ ... request,passed: true });
  });

  return next= > action => {
    // Take action for other environments
    if (passingActions.indexOf(action.type) >= 0 && !action.passed) {
      chrome.runtime.sendMessage(action);
    }
    // Set the proxy
    chrome.proxy.settings.set(/* proxy config */);
  };
};

export default chromeProxyMiddleware;
Copy the code

Project Address:Github.com/0jinxing/ju…

Plug-in address:Chrome.google.com/webstore/de…