溫馨提示×

您好,登錄后才能下訂單哦!

密碼登錄×
登錄注冊(cè)×
其他方式登錄
點(diǎn)擊 登錄注冊(cè) 即表示同意《億速云用戶服務(wù)條款》

怎么用VuePress開發(fā)一個(gè)代碼復(fù)制插件

發(fā)布時(shí)間:2022-02-14 15:21:36 來源:億速云 閱讀:396 作者:iii 欄目:編程語言

今天小編給大家分享一下怎么用VuePress開發(fā)一個(gè)代碼復(fù)制插件的相關(guān)知識(shí)點(diǎn),內(nèi)容詳細(xì),邏輯清晰,相信大部分人都還太了解這方面的知識(shí),所以分享這篇文章給大家參考一下,希望大家閱讀完這篇文章后有所收獲,下面我們一起來了解一下吧。

怎么用VuePress開發(fā)一個(gè)代碼復(fù)制插件

本地開發(fā)

但是如果你在配置文件中要做的事情太多了,最好還是將它們提取到單獨(dú)的插件中,然后通過設(shè)置絕對(duì)路徑或者通過 require 來使用它們:

module.exports = {
  plugins: [
    path.resolve(__dirname, './path/to/your-plugin.js'),
    require('./another-plugin'),
  ],
}

那就讓我們開始吧!

初始化項(xiàng)目

我們?cè)?.vuepress 文件夾下新建一個(gè) vuepress-plugin-code-copy 的文件夾,用于存放插件相關(guān)的代碼,然后命令行進(jìn)入到該文件夾,執(zhí)行 npm init,創(chuàng)建 package.json,此時(shí)文件的目錄為:

.vuepress
├─ vuepress-plugin-code-copy 
│  └─ package.json
└─ config.js

我們?cè)?vuepress-plugin-code-copy下新建一個(gè) index.js 文件,參照官方文檔插件示例中的寫法,我們使用返回對(duì)象的函數(shù)形式,這個(gè)函數(shù)接受插件的配置選項(xiàng)作為第一個(gè)參數(shù)、包含編譯期上下文的 ctx 對(duì)象作為第二個(gè)參數(shù):

module.exports = (options, ctx) => {
   return {
      // ...
   }
}

再參照官方文檔 Option API 中的 name,以及生命周期函數(shù)中的 ready 鉤子,我們寫一個(gè)初始的測(cè)試代碼:

module.exports = (options, ctx) => {
    return {
        name: 'vuepress-plugin-code-copy',
        async ready() {
            console.log('Hello World!');
        }
    }
 }

此時(shí)我們運(yùn)行下 yarn run docs:dev,可以在運(yùn)行過程中看到我們的插件名字和打印結(jié)果:

怎么用VuePress開發(fā)一個(gè)代碼復(fù)制插件

插件設(shè)計(jì)

現(xiàn)在我們可以設(shè)想下我們的代碼復(fù)制插件的效果了,我想要實(shí)現(xiàn)的效果是:

在代碼塊的右下角有一個(gè) Copy 文字按鈕,點(diǎn)擊后文字變?yōu)?Copied!然后一秒后文字重新變?yōu)?Copy,而代碼塊里的代碼則在點(diǎn)擊的時(shí)候復(fù)制到剪切板中。

插件開發(fā)

如果是在 Vue 組件中,我們很容易實(shí)現(xiàn)這個(gè)效果,在根組件 mounted 或者 updated的時(shí)候,使用 document.querySelector獲取所有的代碼塊,插入一個(gè)按鈕元素,再在按鈕元素上綁定點(diǎn)擊事件,當(dāng)觸發(fā)點(diǎn)擊事件的時(shí)候,代碼復(fù)制到剪切板,然后修改文字,1s 后再修改下文字。

那 VuePress 插件有方法可以控制根組件的生命周期嗎?我們查閱下 VuePress 官方文檔的 Option API,可以發(fā)現(xiàn) VuePress 提供了一個(gè) clientRootMixin 方法:

指向 mixin 文件的路徑,它讓你可以控制根組件的生命周期

看下示例代碼:

// 插件的入口
const path = require('path')

module.exports = {
  clientRootMixin: path.resolve(__dirname, 'mixin.js')
}
// mixin.js
export default {
  created () {},
  mounted () {}
}

這不就是我們需要的嗎?那我們動(dòng)手吧,修改 index.js的內(nèi)容為:

const path = require('path');

module.exports = (options, ctx) => {
    return {
        name: 'vuepress-plugin-code-copy',
        clientRootMixin: path.resolve(__dirname, 'clientRootMixin.js')
    }
 }

vuepress-plugin-code-copy下新建一個(gè) clientRootMixin.js文件,代碼寫入:

export default {
    updated() {
        setTimeout(() => {
            document.querySelectorAll('div[class*="language-"] pre').forEach(el => {
								console.log('one code block')
            })
        }, 100)
    }
}

刷新下瀏覽器里的頁面,然后查看打印:

怎么用VuePress開發(fā)一個(gè)代碼復(fù)制插件

接下來就要思考如何寫入按鈕元素了。

當(dāng)然我們可以使用原生 JavaScript 一點(diǎn)點(diǎn)的創(chuàng)建元素,然后插入其中,但我們其實(shí)是在一個(gè)支持 Vue 語法的項(xiàng)目里,其實(shí)我們完全可以創(chuàng)建一個(gè) Vue 組件,然后將組件的實(shí)例掛載到元素上。那用什么方法掛載呢?

我們可以在 Vue 的全局 API 里,找到 Vue.extendAPI,看一下使用示例:

// 要掛載的元素
<div id="mount-point"></div>
// 創(chuàng)建構(gòu)造器
var Profile = Vue.extend({
  template: '<p>{{firstName}} {{lastName}} aka {{alias}}</p>',
  data: function () {
    return {
      firstName: 'Walter',
      lastName: 'White',
      alias: 'Heisenberg'
    }
  }
})
// 創(chuàng)建 Profile 實(shí)例,并掛載到一個(gè)元素上。
new Profile().$mount('#mount-point')

結(jié)果如下:

// 結(jié)果為:
<p>Walter White aka Heisenberg</p>

那接下來,我們就創(chuàng)建一個(gè) Vue 組件,然后通過 Vue.extend 方法,掛載到每個(gè)代碼塊元素中。

vuepress-plugin-code-copy下新建一個(gè) CodeCopy.vue 文件,寫入代碼如下:

<template>
    <span class="code-copy-btn" @click="copyToClipboard">{{ buttonText }}</span>
</template>

<script>
export default {
    data() {
        return {
            buttonText: 'Copy'
        }
    },
    methods: {
        copyToClipboard(el) {
            this.setClipboard(this.code, this.setText);
        },
        setClipboard(code, cb) {
            if (navigator.clipboard) {
                navigator.clipboard.writeText(code).then(
                    cb,
                    () => {}
                )
            } else {
                let copyelement = document.createElement('textarea')
                document.body.appendChild(copyelement)
                copyelement.value = code
                copyelement.select()
                document.execCommand('Copy')
                copyelement.remove()
                cb()
            }
        },
        setText() {
            this.buttonText = 'Copied!'

            setTimeout(() => {
                this.buttonText = 'Copy'
            }, 1000)
        }
    }
}
</script>

<style scoped>
.code-copy-btn {
    position: absolute;
    bottom: 10px;
    right: 7.5px;
    opacity: 0.75;
    cursor: pointer;
    font-size: 14px;
}

.code-copy-btn:hover {
    opacity: 1;
}
</style>

該組件實(shí)現(xiàn)了按鈕的樣式和點(diǎn)擊時(shí)將代碼寫入剪切版的效果,整體代碼比較簡(jiǎn)單,就不多敘述了。

我們修改一下 clientRootMixin.js

import CodeCopy from './CodeCopy.vue'
import Vue from 'vue'

export default {
    updated() {
        // 防止阻塞
        setTimeout(() => {
            document.querySelectorAll('div[class*="language-"] pre').forEach(el => {
              	// 防止重復(fù)寫入
                if (el.classList.contains('code-copy-added')) return
                let ComponentClass = Vue.extend(CodeCopy)
                let instance = new ComponentClass()
                instance.code = el.innerText
                instance.$mount()
                el.classList.add('code-copy-added')
                el.appendChild(instance.$el)
            })
        }, 100)
    }
}

這里注意兩點(diǎn),第一是我們通過 el.innerText 獲取要復(fù)制的代碼內(nèi)容,然后寫入到實(shí)例的 code 屬性,在組件中,我們是通過 this.code獲取的。

第二是我們沒有使用 $mount(element),直接傳入一個(gè)要掛載的節(jié)點(diǎn)元素,這是因?yàn)?$mount() 的掛載會(huì)清空目標(biāo)元素,但是這里我們需要添加到元素中,所以我們?cè)趫?zhí)行 instance.$mount()后,通過 instance.$el獲取了實(shí)例元素,然后再將其 appendChild 到每個(gè)代碼塊中。關(guān)于 $el的使用可以參考官方文檔的 el 章節(jié) 。

此時(shí),我們的文件目錄如下:

.vuepress
├─ vuepress-plugin-code-copy 
│  ├─ CodeCopy.vue
│  ├─ clientRootMixin.js
│  ├─ index.js
│  └─ package.json
└─ config.js

至此,其實(shí)我們就已經(jīng)實(shí)現(xiàn)了代碼復(fù)制的功能。

插件選項(xiàng)

有的時(shí)候,為了增加插件的可拓展性,會(huì)允許配置可選項(xiàng),就比如我們不希望按鈕的文字是 Copy,而是中文的「復(fù)制」,復(fù)制完后,文字變?yōu)?「已復(fù)制!」,該如何實(shí)現(xiàn)呢?

前面講到,我們的 index.js導(dǎo)出的函數(shù),第一個(gè)參數(shù)就是 options 參數(shù):

const path = require('path');

module.exports = (options, ctx) => {
    return {
        name: 'vuepress-plugin-code-copy',
        clientRootMixin: path.resolve(__dirname, 'clientRootMixin.js')
    }
 }

我們?cè)?config.js先寫入需要用到的選項(xiàng):

module.exports = {
    plugins: [
      [
        require('./vuepress-plugin-code-copy'),
        {
          'copybuttonText': '復(fù)制',
          'copiedButtonText': '已復(fù)制!'
        }
      ]
    ]
}

我們 index.js中通過 options參數(shù)可以接收到我們?cè)?config.js 寫入的選項(xiàng),但我們?cè)趺窗堰@些參數(shù)傳入 CodeCopy.vue 文件呢?

我們?cè)俜?VuePress 提供的 Option API,可以發(fā)現(xiàn)有一個(gè) define API,其實(shí)這個(gè) define 屬性就是定義我們插件內(nèi)部使用的全局變量。我們修改下 index.js

const path = require('path');

module.exports = (options, ctx) => {
    return {
        name: 'vuepress-plugin-code-copy',
        define: {
            copybuttonText: options.copybuttonText || 'copy',
            copiedButtonText: options.copiedButtonText || "copied!"
        },
        clientRootMixin: path.resolve(__dirname, 'clientRootMixin.js')
    }
 }

現(xiàn)在我們已經(jīng)寫入了兩個(gè)全局變量,組件里怎么使用呢?答案是直接使用!

我們修改下 CodeCopy.vue 的代碼:

// ...
<script>
export default {
    data() {
        return {
            buttonText: copybuttonText
        }
    },
    methods: {
        copyToClipboard(el) {
            this.setClipboard(this.code, this.setText);
        },
        setClipboard(code, cb) {
            if (navigator.clipboard) {
                navigator.clipboard.writeText(code).then(
                    cb,
                    () => {}
                )
            } else {
                let copyelement = document.createElement('textarea')
                document.body.appendChild(copyelement)
                copyelement.value = code
                copyelement.select()
                document.execCommand('Copy')
                copyelement.remove()
                cb()
            }
        },
        setText() {
            this.buttonText = copiedButtonText

            setTimeout(() => {
                this.buttonText = copybuttonText
            }, 1000)
        }
    }
}
</script>
// ...

以上就是“怎么用VuePress開發(fā)一個(gè)代碼復(fù)制插件”這篇文章的所有內(nèi)容,感謝各位的閱讀!相信大家閱讀完這篇文章都有很大的收獲,小編每天都會(huì)為大家更新不同的知識(shí),如果還想學(xué)習(xí)更多的知識(shí),請(qǐng)關(guān)注億速云行業(yè)資訊頻道。

向AI問一下細(xì)節(jié)

免責(zé)聲明:本站發(fā)布的內(nèi)容(圖片、視頻和文字)以原創(chuàng)、轉(zhuǎn)載和分享為主,文章觀點(diǎn)不代表本網(wǎng)站立場(chǎng),如果涉及侵權(quán)請(qǐng)聯(lián)系站長(zhǎng)郵箱:is@yisu.com進(jìn)行舉報(bào),并提供相關(guān)證據(jù),一經(jīng)查實(shí),將立刻刪除涉嫌侵權(quán)內(nèi)容。

AI