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

本文来自 「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,一经查实,立即删除!

相关文章

解析 react、vue等路由参数的库 path-to-regexp

React-router的路由系统内部中使用的是 path-to-regexp 解析匹配路由参数。具体用法如下 const { pathToRegexp } require("path-to-regexp")const regexp pathToRegexp("/:foo/:bar"); // keys [{ name: foo, prefix: /, ... }, { name: bar, prefix:…

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

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

交叉渡线道岔规格_交叉渡线道岔选择基本原则

交叉渡线道岔简介道岔是由一条线路分支进入或超越另一条线路的连接及交叉设备分支。道岔是铁路轨道结构中必不可少的一个重要组成部分。各国铁路道岔与线路的比例&#xff0c;随铁路运量、密度的不同而有很大差异。我国是铁路运量、密度较大的国家之一&#xff0c;因此我国铁路…

Jest 只MOCK模块中的某个功能实现

单元测试某些场景下只想模拟模块中的某个功能&#xff0c;并且保留模块原有的功能。这时候我可以用 jest.requireActual 配合 jest.mock 进行实现。 jest.requireActual 该API返回实际模块而不是模拟模块&#xff0c;绕过所有有关该模块是否应接收模拟实施的检查。为什么用 je…

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

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

jest 单元测试模拟模块设置动态值

在单元测试中需要对组件进行动态、极端、正常状态测试&#xff0c;如果组件里使用了api那么我们想对组件进行接口多种不同响应进行测试时候就需要对模块进行动态值设置。 mockFun.mockImplementation 接受应作为模拟的实现使用的函数。模拟本身仍然会记录所有进入的调用以及来…

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;虽…

React 性能优化之批量处理 unstable_batchedUpdates

手动批量处理 unstable_batchedUpdates 可用于手动批量更新state&#xff0c;可以指定多个setState合并为一个更新请求。 import { useState, useLayoutEffect } from "react"; import { unstable_batchedUpdates } from "react-dom";function App() {con…

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

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

python语言中的注释符_各种语言中的注释符总结

各种语言中的注释符c语言/* 注释内容*/// 注释内容Pascal&#xff1a;{ 注释内容}Vb&#xff1a;Rem 注释内容‘注释内容Vbscript&#xff1a;‘注释内容rem 注释内容Javascript&#xff1a;// 注释内容/* 注释内容*/Java&#xff1a;// 注释内容/* 注释内容*/Delphi&#xff1…

关于 Taro 的 ScrollView 在Dom结构发生变化会自动回滚到顶部解决方案和原因

使用Taro开发小程序时候发现 ScrollView 会在同级节点发生增删情况下会自动会滚ScrollView到顶部&#xff0c;经过多次验证和查阅Taro原理发现这是Taro自身bug 出现回滚顶部bug的演示代码 下面有一个列表和按钮&#xff0c;点击按钮会出现加载更多的字样。但是当我们点击按钮时…

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

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

Node.js Event loop 图解

直接上自己制作的流程图

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

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

mysql_unbuffered_query的_用mysql_unbuffered_query函数取大数据

昨天在做项目的时候&#xff0c;因为涉及到数据表结构的改动&#xff0c;需要进行大量数据的导入&#xff0c;那么如何高效的进行是我比较关注的。本文暂且从使用PHP脚本层面上来说&#xff0c;因为使用其他语言或其他方式也可以进行数据的重导。 在讨论这个问题的时候&#xf…

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

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

lisp修改界址线属性_地籍与房产测量 A卷答案

地籍与房产测量 A卷答案一、单项选择题(共10个小题&#xff0c;每小题2分&#xff0c;共20分。)1、地籍测绘规范规定 界址点的精度分( C )A.一级 B.二级 C.三级 D.四级2、界址点的精度要求最高为 AA.5cm B.10cm C.15cm D.20cm3、城镇地区城区地籍图的比例尺一般采用( A )A. 1:1…

mysql获取数据库名_mysql获取数据库名

{"moduleinfo":{"card_count":[{"count_phone":1,"count":1}],"search_count":[{"count_phone":4,"count":4}]},"card":[{"des":"阿里云数据库专家保驾护航&#xff0c;为用户…

scala mysql连接池_Java与Scala的两种简易版连接池

Java版简易版连接池&#xff1a;import java.sql.Connection;import java.sql.DriverManager;import java.util.LinkedList;/*** 简易版的连接池*/public class ConnectionPool {//静态的Connectionprivate static LinkedListconnections;//加载驱动static {try {Class.forName…