在搭建 VuePress 部落格的過程中,也並不是所有的外掛都能滿足需求,所以本篇我們以實現一個程式碼複製外掛為例,教大家如何從零實現一個 VuePress 外掛。
開發外掛第一個要解決的問題就是如何本地開發,我們檢視 VuePress 1.0 官方檔案的「開發外掛」章節,並沒有找到解決方案,但在 VuePress 2.0 官方檔案的「本地外掛」裡,卻有寫道:
推薦你直接將 組態檔 作為外掛使用,因為幾乎所有的外掛 API 都可以在組態檔中使用,這在絕大多數場景下都更為方便。
但是如果你在組態檔中要做的事情太多了,最好還是將它們提取到單獨的外掛中,然後通過設定絕對路徑或者通過 require 來使用它們:
module.exports = { plugins: [ path.resolve(__dirname, './path/to/your-plugin.js'), require('./another-plugin'), ], }
那就讓我們開始吧!
我們在 .vuepress
資料夾下新建一個 vuepress-plugin-code-copy
的資料夾,用於存放外掛相關的程式碼,然後命令列進入到該資料夾,執行 npm init
,建立 package.json
,此時檔案的目錄為:
.vuepress ├─ vuepress-plugin-code-copy │ └─ package.json └─ config.js
我們在 vuepress-plugin-code-copy
下新建一個 index.js
檔案,參照官方檔案外掛範例中的寫法,我們使用返回物件的函數形式,這個函數接受外掛的設定選項作為第一個引數、包含編譯期上下文的 ctx 物件作為第二個引數:
module.exports = (options, ctx) => { return { // ... } }
再參照官方檔案 Option API 中的 name,以及生命週期函數中的 ready 勾點,我們寫一個初始的測試程式碼:
module.exports = (options, ctx) => { return { name: 'vuepress-plugin-code-copy', async ready() { console.log('Hello World!'); } } }
此時我們執行下 yarn run docs:dev
,可以在執行過程中看到我們的外掛名字和列印結果:
現在我們可以設想下我們的程式碼複製外掛的效果了,我想要實現的效果是:
在程式碼塊的右下角有一個 Copy 文字按鈕,點選後文字變為 Copied!然後一秒後文字重新變為 Copy,而程式碼塊裡的程式碼則在點選的時候複製到剪下板中,期望的表現效果如下:
如果是在 Vue 元件中,我們很容易實現這個效果,在根元件 mounted
或者 updated
的時候,使用 document.querySelector
獲取所有的程式碼塊,插入一個按鈕元素,再在按鈕元素上繫結點選事件,當觸發點選事件的時候,程式碼複製到剪下板,然後修改文字,1s 後再修改下文字。
那 VuePress 外掛有方法可以控制根元件的生命週期嗎?我們查閱下 VuePress 官方檔案的 Option API,可以發現 VuePress 提供了一個 clientRootMixin 方法:
指向 mixin 檔案的路徑,它讓你可以控制根元件的生命週期
看下範例程式碼:
// 外掛的入口 const path = require('path') module.exports = { clientRootMixin: path.resolve(__dirname, 'mixin.js') }
// mixin.js export default { created () {}, mounted () {} }
這不就是我們需要的嗎?那我們動手吧,修改 index.js
的內容為:
const path = require('path'); module.exports = (options, ctx) => { return { name: 'vuepress-plugin-code-copy', clientRootMixin: path.resolve(__dirname, 'clientRootMixin.js') } }
在 vuepress-plugin-code-copy
下新建一個 clientRootMixin.js
檔案,程式碼寫入:
export default { updated() { setTimeout(() => { document.querySelectorAll('div[class*="language-"] pre').forEach(el => { console.log('one code block') }) }, 100) } }
重新整理下瀏覽器裡的頁面,然後檢視列印:
接下來就要思考如何寫入按鈕元素了。
當然我們可以使用原生 JavaScript 一點點的建立元素,然後插入其中,但我們其實是在一個支援 Vue 語法的專案裡,其實我們完全可以建立一個 Vue 元件,然後將元件的範例掛載到元素上。那用什麼方法掛載呢?
我們可以在 Vue 的全域性 API 裡,找到 Vue.extend
API,看一下使用範例:
// 要掛載的元素 <div id="mount-point"></div>
// 建立構造器 var Profile = Vue.extend({ template: '<p>{{firstName}} {{lastName}} aka {{alias}}</p>', data: function () { return { firstName: 'Walter', lastName: 'White', alias: 'Heisenberg' } } }) // 建立 Profile 範例,並掛載到一個元素上。 new Profile().$mount('#mount-point')
結果如下:
// 結果為: <p>Walter White aka Heisenberg</p>
那接下來,我們就建立一個 Vue 元件,然後通過 Vue.extend
方法,掛載到每個程式碼塊元素中。
在 vuepress-plugin-code-copy
下新建一個 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>
該元件實現了按鈕的樣式和點選時將程式碼寫入剪下版的效果,整體程式碼比較簡單,就不多敘述了。
我們修改一下 clientRootMixin.js
:
import CodeCopy from './CodeCopy.vue' import Vue from 'vue' export default { updated() { // 防止阻塞 setTimeout(() => { document.querySelectorAll('div[class*="language-"] pre').forEach(el => { // 防止重複寫入 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) } }
這裡注意兩點,第一是我們通過 el.innerText
獲取要複製的程式碼內容,然後寫入到範例的 code
屬性,在元件中,我們是通過 this.code
獲取的。
第二是我們沒有使用 $mount(element)
,直接傳入一個要掛載的節點元素,這是因為 $mount()
的掛載會清空目標元素,但是這裡我們需要新增到元素中,所以我們在執行 instance.$mount()
後,通過 instance.$el
獲取了範例元素,然後再將其 appendChild
到每個程式碼塊中。關於 $el
的使用可以參考官方檔案的 el 章節 。
此時,我們的檔案目錄如下:
.vuepress ├─ vuepress-plugin-code-copy │ ├─ CodeCopy.vue │ ├─ clientRootMixin.js │ ├─ index.js │ └─ package.json └─ config.js
至此,其實我們就已經實現了程式碼複製的功能。
有的時候,為了增加外掛的可拓展性,會允許設定可選項,就比如我們不希望按鈕的文字是 Copy,而是中文的「複製」,複製完後,文字變為 「已複製!」,該如何實現呢?
前面講到,我們的 index.js
匯出的函數,第一個引數就是 options 引數:
const path = require('path'); module.exports = (options, ctx) => { return { name: 'vuepress-plugin-code-copy', clientRootMixin: path.resolve(__dirname, 'clientRootMixin.js') } }
我們在 config.js
先寫入需要用到的選項:
module.exports = { plugins: [ [ require('./vuepress-plugin-code-copy'), { 'copybuttonText': '複製', 'copiedButtonText': '已複製!' } ] ] }
我們 index.js
中通過 options
引數可以接收到我們在 config.js
寫入的選項,但我們怎麼把這些引數傳入 CodeCopy.vue
檔案呢?
我們再翻下 VuePress 提供的 Option API,可以發現有一個 define API,其實這個 define 屬性就是定義我們外掛內部使用的全域性變數。我們修改下 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') } }
現在我們已經寫入了兩個全域性變數,元件裡怎麼使用呢?答案是直接使用!
我們修改下 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> // ...
最終的效果如下:
完整的程式碼檢視:https://github.com/mqyqingfeng/Blog/tree/master/demos/VuePress/vuepress-plugin-code-copy
【相關推薦:】
以上就是【VuePress實戰】手把手帶你開發一個程式碼複製外掛的詳細內容,更多請關注TW511.COM其它相關文章!