這篇文章帶大家了解VuePress實戰,介紹一下如果從零開發一個 VuePress 外掛程式(程式碼複製外掛程式),希望對大家有幫助!
在建立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
【相关推荐:vue.js视频教程】
以上是【VuePress實戰】手把手帶你開發一個程式碼複製插件的詳細內容。更多資訊請關注PHP中文網其他相關文章!