如何在代码中让按钮高亮_各种博客的代码高亮是如何实现的

本文来自 「Vue虚拟实验」的小伙伴  余xiaoy,在做 Lovue 项目的时候,他负责了代码高亮显示功能,目前实现了代码高亮、主题切换、某行代码特殊显示、显示行号等功能,效果如下。

4f5144636a751a9950a1467aee389390.png

下面介绍如何通过 Vue 来完成这个功能:

Vue 为我们提供了很方便的创建组件的方式,组件化开发能够将业务进行解耦,进而提高项目的开发效率。在 Vue 中定义组件非常简单:定

<div id="app">        <lovue>lovue>div><script>    // 1. 使用Vue.component(name,{})进行组件全局注册    Vue.component('lovue', {        template: '

这是lovue组件

'    })l    const vm = new Vue({        el: '#app',        data: {},        methods: {} });script>

以上代码便定义了一个全局的 lovue 组件,随后我们可以很愉快地在页面各个位置通过引入 lovue 标签的形式使用我们的组件了。

对于 demo 型项目,采用这种方式是可以的,但这种形式缺点很明显,比如字符串模板无法代码高亮,无法单独定义 CSS 样式等。因此在实际的项目当中,我们更多的是采用单文件组件的形式来定义组件。

1、单文件组件

在 Vue 中,一个.vue 文件就是一个单文件组件,一个基本的单文件组件代码结构如下:

<template>    <div>                <h3>这是lovue单文件组件h3>    div>template><script>export default {    // JS代码部分,包含组件内定义的属性及方法    name: "Lovue",    data() {        return {};    }};script><style scoped>/* CSS代码,添加scoped属性将定义为局部样式 */style>

将内嵌的组件定义改造为单文件组件形式后,我们可以在其他页面中,通过 import 语法进行导入,并采取全局或者局部的方式进行注册:

import Lovue from '@/components/Lovue'// 1.全局注册Vue.components('lovue', Lovue)// 2.Vue实例内部局部注册const vm = new Vue({  el: '#app',  components: {    Lovue  }})

使用单文件组件,能让我们的项目文件结构较为清晰,使用 vue-cli创建的项目,脚手架已经帮我们创建好了专门放置组件的文件夹 `components` 和 `views` ,`components` 一般用来放置小型组件,在其他页面可能会被多次引用,`views`用来放置页面级组件,一般不可复用。

2、代码高亮组件实现(基于 Prismjs)

学会使用单文件组件后,我们便可以定制自己的组件了。在 Lovue 项目,我们便创建了代码高亮组件,其功能是针对指定的代码块进行高亮显示,同时组件还实现了代码块内部的局部代码高亮及代码块的主题切换功能,下面使用迭代的思想介绍该组件的实现过程。

创建组件之前,我们需要清楚组件的使用场景,这样能让我们更好地对组件功能进行划分,并从用户角度去设计一款体验良好的组件。

具体到 Lovue 的代码高亮组件,我们需要能够对文章作者指定的代码块进行高亮显示,同时对于代码块中的重点代码区域可以进行着重显示,这样对于文章的读者来说,可以很快速的了解到作者想讲解的代码片段。同时,为了适应不同读者对于代码显示风格的喜好,组件还需实现多种预定义主题的切换功能。

分析完成后,我们就可以开始创建代码高亮组件了。

对于代码高亮功能的实现,目前已有像 Prismjs, highlight.js 等比较成熟,使用程度较高的库了,这里我们选用 Prismjs 来实现我们的代码高亮组件。

因为选用了第三方库,因此第一步我们需要将 Prismjs 引入到我们的项目当中:

npm i prismjs babel-plugin-prismjs -S

prismjs 为核心库,babel-plugin-prismjs 可以配置 prismjs 附带的多种插件;若 npm 下载慢,可以使用 cnpm

安装完成后,我们需要更新项的目配置文件 babel.config.js,添加如下配置项:

"plugins": [        ["prismjs",            {                "languages": ["html", "javascript", "css", "markup"],                "css": true            }        ]    ]

以上配置定义了我们需要高亮显示的代码语言,配置好之后,我们就可以搭建我们组件的代码了。

<template>     <pre>         <code class="language-javascript">            const vm = new Vue({                el: '#app',                data: {},                methods: {}            })      code> pre>template><script>import Prism from "prismjs";export default {    name: "CodeHighLight",    mounted() {        // Prismjs内置的代码高亮方法        Prism.highlightAll();    }};script><style>style>

上述代码通过仅使用 Prismjs 库自带的方法,已经实现了一个最基本且可用的代码高亮组件了。是的,就是这么简单,你现在可以在你的页面中引入这个组件了,如无意外,你将看到如下效果:

a5c29cf854e928599171f1cfa765a5bd.png

但该组件目前过于简单,且代码块写死在组件中,用户无法自定义,因此需要在此基础上进行改进。这是一个很好的做项目/写代码的方式,首先实现最基本的功能,然后在此基础上进行更新迭代,慢慢添加新的功能。

考虑我们前面分析的使用场景,用户在父页面中引入我们的组件时可定义自己的代码块,因此该代码块是在父组件中定义,但是要在子组件中可以使用的。解决这个问题,我们需要使用到 Vue 中常用的父子组件传值方法,群里其他同学分享过具体的使用,这里不再赘述,直接上代码:

<template>    <div>        <pre v-if="code">            <code :class="codeClass" v-text="code">code>        pre>    div>template><script>import Prism from "prismjs";import "prismjs/themes/prism-tomorrow.css";export default {    name: "CodeHighLight",    props: {        // 父组件的代码块变量        code: {            type: String,            required: true        },        // 代码语言,默认为JS        lang: {            type: String,            default: "javascript"        }    },    computed: {        codeClass: function() {            return "language-" + this.lang;        }    },    mounted() {        Prism.highlightAll();    },    updated() {        Prism.highlightAll();    }};script><style>style>

这里我们将从父页面传递过来的 code 变量设置为必需项,若用户未定义,则控制台打印错误。同时考虑我们使用 javascript 语言较多,我们将代码语言默认设置为 javascript。

代码改造后,我们就可以通过以下方式在父页面中使用了,其中 code,code1 为用户定义的代码变量:

:code="code"> :code="code1" lang=

如无意外,你可以在页面中可看到类似下面的效果:

74386be9c0176b2d467b9e39c07e023a.png

到这里,其实用户已经可以使用该组件了,但为了更好的用户体验及提高组件的灵活性,我们还需要在此基础上进行进一步的更新迭代。

可选的但不限于:显示代码的行号,显示代码的语言,对代码块中的局部代码进行着重显示等。

查阅 Prismjs 官方文档后发现,官方已经为我们提供了很多可选的插件,找到对应的插件名后,我们就可以在 babel.config.js 中对 prismjs 的插件进行配置了。

'plugins': [        ["prismjs",            {                "languages": ['html', "javascript", "css", "markup"],                "plugins": ['match-braces', 'autolinker', 'inline-color', 'normalize-whitespace', 'copy-to-clipboard', 'show-language', 'line-numbers', 'line-highlight'],                "css": true            }        ]    ]

以上的插件名及对应的作用可以查阅 Prismjs 官方文档,完成插件配置后,我们需要增加几个组件的 prop,用于接收父组件定义的局部高亮代码行数,高亮背景色等变量,具体可到 Lovue 项目 github 仓库查看。

至此,我们重启项目,已经可以看到类似下面的效果了:

ea91accae7ecc7442f5d9cb815e5a7ca.png

效果不错,保存好这版代码,我们继续往下,看下如何实现代码高亮主题切换的功能。

Vue 本身为我们提供了 class 及 style 的数据绑定功能,也有同学分享过相关的使用方法。但因为 Prismjs 的内部实现是将用户代码根据语言类型解析并拆分成不同的字符分组,然后针对不同分组在其上应用不同的 class。在 Prismjs 提供的不同主题文件中,相同分组的 class 名均相同,因此我们无法直接通过使用 class 数据绑定的形式进行主题的切换。

使用该思路,一种可行的方式是采用 Scss 等 css 预编译工具对不同主题的 css 代码统一增加前缀 class 类名(不同主题类名不同),然后使用数据绑定功能进行类名/主题切换。但操作起来很不方便,这里不讨论。

这里采用另一种方式,在页面中通过 link 标签引入主题 css 文件,在用户切换不同的主题时,替换 link 标签的 href 值为对应的主题 css 文件,实现代码主题切换。该方式的核心代码如下,感兴趣的可到 Lovue 项目仓库查看:

methods: {    addCss(theme) {        let link = document.createElement("link");        let head = document.getElementsByTagName("head")[0];        link.rel = "stylesheet";        link.type = "text/css";        link.href = this.cssPrefix + theme + ".css";        head.appendChild(link);    },    loadCss(theme) {        let cssLink = "link[href^='" + this.cssPrefix + "']";        let link = document.head.querySelectorAll(cssLink);        if (link.length) {            // 判断主题css文件是否存在            for (let i = 0; i < link.length; i++)                link[i].href = this.cssPrefix + theme + ".css";            return;        }        this.addCss(theme);        console.log("loadCss done.");    }},watch: {    // 监听theme变化,加载对应主题的css文件    theme: function(newVal) {        this.loadCss(newVal);        localStorage.setItem("codeTheme", newVal);    }},mounted() {    this.loadCss(theme);}

这里使用了 localStorage 来读取和保存用户选择的主题,可提升用户体验

至此,Lovue 项目中的代码高亮组件已基本完成。整个过程不复杂,感兴趣的可以动手创建属于自己的组件了。


推荐阅读:不写一行代码,即可跨域的技巧Vue 模板的功臣 · 指令第六阶段 · 期待已久的 Vue第四阶段 · JavaScript 专题结束,有电子书

a2e9450e94f93cc563bff02d673dd020.png

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/563840.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

如何把表格做成源代码_他来了,他来了,文字、表格、公式图片识别神器V0.1测试版...

他来了&#xff0c;他来了&#xff0c;Mathpix拜拜了~~~文字、表格、公式图片识别神器V0.1测试版俺不是标题党&#xff01;&#xff01;&#xff01;开发背景日常工作中经常遇到大量的图片版文本、表格、公式需要编辑&#xff1b;用手敲&#xff1f;也太OUT了吧&#xff0c;好歹…

离线登陆_iphone手机,苹果手机如何登陆网易163邮箱

在使用iPhone系统邮箱的时候&#xff0c;我们在配置的时候可能会遇到各种各样的问题&#xff0c;导致无法在系统邮件里面使用163邮箱。主要是手机客户端的密码和网页登陆的密码是不一样的。常见问题下面就是有人在使用的时候遇到的问题&#xff1a;登陆密码错误有人知道怎么在i…

Vue 封装echarts饼状图(Pie)组件

目的&#xff1a;减少重复代码&#xff0c;便于维护 效果显示&#xff1a; 组件代码 <template><div class"ldw-data-content-box"><div class"ldw-chilren-box"><div class"title"><div>{{ title }}</div>…

积分上下限无穷_数学分析|第九章 定积分利用等价无穷小量和定积分定义解决数列极限问题总结...

当公式或文字展示不完全时&#xff0c;记得向左←滑动哦&#xff01;摘要&#xff1a; 当我们利用等价无穷小量时&#xff0c;不仅仅可以利用等价替换&#xff0c;有的时候我们需要利用极限的定义语言来解决问题&#xff0c;当等价无穷小量和连加数列结合在一起时&#xff0c;虽…

关于配置Webapck的 exclude 不过滤 node_modules Babel却没有处理转换node_modules的源码

最近对公司的项目引入了 nanoid 替换 uuid 的使用。但是在sentry日志中发现Unexpected token >的错误。立马查看编译后bunld发现 nanoid 箭头函数没有被转换。所以对此记录一下原因和解决办法。 报错的原因 1.nanoid 源码是没有经过babel转换的。 查看nanoid的源码&#x…

android 贝塞尔曲线_OpenGL 实践之贝塞尔曲线绘制

说到贝塞尔曲线&#xff0c;大家肯定都不陌生&#xff0c;网上有很多关于介绍和理解贝塞尔曲线的优秀文章和动态图。以下两个是比较经典的动图了。二阶贝塞尔曲线&#xff1a;三阶贝塞尔曲线&#xff1a;由于在工作中经常要和贝塞尔曲线打交道&#xff0c;所以简单说一下自己的…

Node.js Event loop 图解

直接上自己制作的流程图

支持串行隔离级别_从0到1理解数据库事务(上):并发问题与隔离级别

最近准备写一篇关于Spanner事务的分享&#xff0c;所以先分享一些基础知识&#xff0c;涉及ACID、隔离级别、MVCC、锁&#xff0c;由于太长&#xff0c;只好拆分成上下两篇&#xff1a;上&#xff1a;并发问题与隔离级别主要讲事务所要解决的问题、思路&#xff0c;先理解为什么…

如何发布接口_Devops下的接口全生命周期管理与测试

什么是devops&#xff1f;随着时间的推移&#xff0c;devops的定义也在不断的演进。对于其定义可能出现千人千面&#xff0c;但从核心观点&#xff0c;整体业界还是保持着一致的认识。DevOps不是单一的技术或者工具&#xff0c;甚至不只是一个流程&#xff0c;而是包含应用设计…

查看mysql日志post_(转)MySQL 日志组提交

原文:https://jin-yang.github.io/post/mysql-group-commit.html组提交 (group commit) 是为了优化写日志时的刷磁盘问题&#xff0c;从最初只支持 InnoDB redo log 组提交&#xff0c;到 5.6 官方版本同时支持 redo log 和 binlog 组提交&#xff0c;大大提高了 MySQL 的事务处…

like语句太慢 sqlserver_SQLServer找出执行慢的SQL语句

SELECT(total_elapsed_time / execution_count)/1000 N平均时间ms,total_elapsed_time/1000 N总花费时间ms,total_worker_time/1000 N所用的CPU总时间ms,total_physical_reads N物理读取总次数,total_logical_reads/execution_count N每次逻辑读次数,total_logical_reads N逻辑…

苹果cms10自适应模板_哪里有苹果cms10自适应模板?

1&#xff0c;苹果CMSv10大图轮播高端大气自适应视频网站模板源码苹果cms10自适应模板下载地址&#xff1a;https://www.mytheme.cn/maccms/54.html第一款大图宽屏的海报轮播幻灯样式&#xff0c;宽屏模板支持DIY扩展自适应影视模板苹果cms10自适应模板苹果cms10自适应模板2&am…

python实现进程通信_python进程间的通讯实现

1&#xff1a;进程间通讯的方法&#xff1a;apply_async()非阻塞式通讯 apply()阻塞式通讯2&#xff1a;使用Queue实现对Process创建的进程间通讯&#xff0c;Queue本身是一个消息队列程序&#xff0c;Queue常用方法&#xff1a;Queue.qsize():返回当前消息队列的消息数量Q…

vscode php formatter mac配置_Mac上配置Vs code时,遇到的几个“坑”!

在写一些简单的程序时&#xff0c;你喜欢用什么编译器呢&#xff1f;之前我一直用的是sublime&#xff0c;它打开的速度快&#xff0c;占用的内存小。但是有个麻烦的地方&#xff0c;配置的时候要花一些时间&#xff0c;如果你的网不好还时不时给你来个timeout。最近在Youtube上…

模为2的逆元是什么_两种求模m逆元的方法

在a|b(a能整除b)的前提下&#xff0c;计算(b/a)mod m的时候转化为 计算(b*x)mod m ; 这时的x就是a的逆元(a模m的逆元)&#xff1b;此时x满足 (a*x mod m 1)&#xff1b; 这个x的求法有一下两种&#xff1a;1)扩展欧几里得算法求解 a*xm*y1; 因为 a*x mod m 1 <> a…

java值栈_Struts2 中的值栈是什么?

7.1值栈7.1.1值栈是什么简单的说&#xff1a;值栈是对应每一个请求对象的轻量级的内存数据中心。Struts2中一个很激动人心的特性就是引入了值栈&#xff0c;在这里统一管理着数据&#xff0c;供Action、Result、Interceptor等Struts2的其他部分使用&#xff0c;这样一来&#x…

java try的用法_Java中try、catch的使用方法

Java中有两种处理异常的方式&#xff0c;分别是用throws抛出异常、用try、catch捕获异常。try-catch在Javatry-catch语句的语法格式&#xff1a;try{//代码块}catch(Exception1 e){//抛出异常后所要进行的操作}当try语句当中捕获到异常时&#xff0c;会将异常抛出到catch语句中…

java not a jpeg file_javax.imageio.IIOException: Not a JPEG file: starts with 0x47 0x49

java处理图片时出现异常javax.imageio.IIOException: Not a JPEG file: starts with 0x47 0x49at com.sun.imageio.plugins.jpeg.JPEGImageReader.readImageHeader(Native Method)at com.sun.imageio.plugins.jpeg.JPEGImageReader.readNativeHeader(Unknown Source)at com.sun…

java if用法_java中if语句的写法

if语句if 语句的语法如下&#xff1a;if(布尔表达式){//如果布尔表达式为true将执行的语句}如果布尔表达式的值为 true&#xff0c;则执行 if 语句中的代码块&#xff0c;否则执行 if 语句块后面的代码。免费视频教程推荐&#xff1a;java视频教程if...else语句if 语句后面可以…

java子类和父类实例_java中父类与子类之间的转换示例

java中父类与子类之间的转换示例有以下三点&#xff1a;示例一父类强制转子类pre class"brush:php;toolbar:false">Father f new Son();Son s (Son)f;//可以创建一个父类的实例&#xff0c;想要强制把父类对象转换成子类的&#xff0c;不行&#xff01;通俗的想…