We recently used Vuepress to package the community’s typeScript translations into an online document, but some plug-ins the community didn’t have or didn’t implement for their own custom needs, so we decided to customize one. The culture uses the gold copy code feature as an example. Take a look at the finished interface

Take a look at the official architecture

Plug-ins run in the Node environment
CommonJS

Like Vue, Vuepress has a life cycle

  • ready

This can be simply interpreted as initialization to complete the call

  • updated

Page update call

  • generated

The production environment build completes the call

Implementation approach

Vuepress will package the MD file into multiple HTML files, so we need to update our component after each change of the file address. According to the life cycle above, our requirements can be realized when updated

As for how to insert the copy-pasted component into the specified code, we can search all specified nodes after the page is loaded and then insert the component through appendChild

The project structure

Copy ├─ ├─ clipboard ├─ copy.vue ├─ index.jsCopy the code
  • index.js

Export documents exposed

  • copy.vue

Implement the copy code component

  • clipboard.js

Responsible for implementing clipboard text

  • clientRootMixin.js

Responsible for the implementation of inserting components into different pages

index.js

It says that we are going to develop a plug-in that duplicates the code, so let’s start by simply defining three parameters

  • The first is the scope of the selector
  • The second is to copy the text displayed by the code
  • The third is a callback function that receives the change message and implements custom animations

In the official example given, there are two ways to use the plug-in

// 例1
module.exports = {
  plugins: [["vuepress-plugin-xxx",
      {
        /* options */}}]].// 例2
module.exports = {
  plugins: {
    xxx: {
      /* options */}}};Copy the code

As you can see, if there is an argument that can be passed in this way, then the first step of our plug-in definition is to handle this argument, or not accept it, if not, just return an object

module.exports = {
  // ...
};
Copy the code

Let’s define a function that simply accepts options

/ / object type

module.exports = {
  define: {
    selector: options.selector || 'div[class*="language-"] pre'.copyText: options.copyText || "Copy code".change: options.change
  }
};
// Function:
module.exports = (options, context) = > ({
  define() {
    return {
      selector: options.selector || 'div[class*="language-"] pre'.copyText: options.copyText || "Copy code".change: options.change }; }});Copy the code

Note that the return must be in CommonJS form. Above, we define the global variable for internal use of our plug-in through the define attribute. It supports two forms of function and object, which can be understood as the data attribute of vue

This step is relatively simple and will not be explained too much, above we mentioned the need to inject components into all pages, this step is the responsibility of clientRootMixin.js, here is to implement it, we need to introduce it in index.js, a complete index.js should look like this

const path = require("path");

module.exports = (options = {}, ctx) = > ({
  define: {
    // ...
  },
  clientRootMixin: path.resolve(__dirname, "clientRootMixin.js")});Copy the code

clientRootMixin.js

ClientRootMixin allows us to control the lifecycle of the root component by listening for the updated event and then inserting copy.vue into the current page

import CodeCopy from "./copy.vue";
import Vue from "vue";

export default {
  updated() {
    // Wait for dom loading to complete
    this.$nextTick((a)= > {
      this.update();
    });
  },
  methods: {
    update() {
      // Get all the DOM, then insert vue components on all the code blocks
      const dom = Array.from(document.querySelectorAll(selector));
      dom.forEach(el= > {
        // Check whether the current node is already inserted
        if (/v-copy/.test(el.className)) {
          return;
        }
        // Create a copy component
        const C = Vue.extend(CodeCopy);
        const copy = new C();
        // Below are the props and some private properties of the component
        copy.copyText = copyText;
        copy.code = el.textContent;
        copy._parent = el;
        copy.$mount();
        el.className += ` v- copy`; el.appendChild(copy.$el); }); }}};Copy the code

OK, now that we’re done, how do I put code into the clipboard

clipboard.js

  • Navigator.clipboard supports asynchronous clipboards
  • Document.execcommand () is more compatible, but only synchronizes the clipboard

The above are the two native methods, but here because it is used as a library, compatibility issues need to be considered, so I choose the already packaged Clipboard. js as the implementation of copy and paste, the following is the specific packaging method, this step can be skipped

import ClipboardJS from "clipboard";
// Encapsulates the clipboard event
const btn = document.createElement("div");
btn.style.display = "none";
document.body.appendChild(btn);

function setUpText(text = "") {
  return new Promise((resolve, reject) = > {
    const cli = new ClipboardJS(btn, {
      text() {
        returntext; }});// Trigger the click event \
    const click = new Event("click");
    cli.on("success".function() {
      resolve(text);
      // Delete with or without success
      cli.destroy();
    });

    cli.on("error".function(e) {
      reject(e.action);
      // Delete with or without success
      cli.destroy();
    });
    btn.dispatchEvent(click);
  });
}

export default setUpText;
Copy the code

OK, now that we’re basically done with the preparation, let’s get back to our familiar vUE component development

copy.vue

This step is simple, just define some CSS properties and execute clipboard.js when you click the button

<template> <span> <span ref="btn" class="v-copy-code-btn" @click="copyClick">{{ copyText }}</span> </span> </template> <script> import clipboard from "./clipboard"; Export default {props: {copyText: {type: String, default: "copy code"}, code: String}, methods: { copyClick() { clipboard(this.code); }}}; </script> <style> <! </style>Copy the code

The last

After this step, the code is published to NPM for everyone to use. This process is not described any more.

Finally, I am ready to find a job now. I would be grateful if you could push it inside