学习风`宇blog
- md文档转html(markdown-it的使用)
- 语法高亮、行号、一键复制
- toc生成目录
- sticky粘性定位
<style lang="scss">
@import url(//at.alicdn.com/t/c/font_4004562_9v94jccafmc.css);
@import url('https://fonts.font.im/css?family=Roboto');* {font-family: 'Roboto';
}a {text-decoration: none;
}ul,
ol,
li {margin: 0;padding: 0;list-style: none;
}h1,
h2,
h3,
h4,
h5,
h6 {margin: 0;margin-bottom: 10px;
}/* 行号样式 */
.line-numbers-rows {position: absolute;pointer-events: none;top: 18px;font-size: 100%;left: 0.5em;width: 2.1em;letter-spacing: -1px;border-right: 1px solid #0e0f12;-webkit-user-select: none;-moz-user-select: none;user-select: none;background-color: #282c34;
}pre {position: relative;padding-left: 2.6em;line-height: 1.3em;
}.line-numbers-rows>span {display: block;counter-increment: linenumber;
}pre {background-color: #282c34;border-radius: 8px;
}pre code {border-radius: 8px;
}.line-numbers-rows>span:before {content: counter(linenumber);color: #999;display: block;padding-right: 0.8em;text-align: right;
}.language-name {position: absolute;top: 9px;color: #999999;right: 43px;font-size: 0.8em;-webkit-user-select: none;-moz-user-select: none;user-select: none;
}.copy-btn {position: absolute;right: 8px;top: 8px;background-color: #525252;border: none;padding: 3px 6px;border-radius: 3px;color: #cccccc;cursor: pointer;display: none;
}pre:hover .copy-btn {display: block;
}.copy-textarea {position: absolute;left: -9999px;top: -9999px;
}/* 封面图下移效果 */
@keyframes slidedown {0% {opacity: 0.3;transform: translateY(-60px);}100% {opacity: 1;transform: translateY(0px);}
}.slidedown {animation: slidedown 1s;
}/* 内容上移效果 */
@keyframes slideup {0% {opacity: 0.3;transform: translateY(60px);}100% {opacity: 1;transform: translateY(0px);}
}.slideup {animation: slideup 1s;
}/* 从小变大效果 */
@keyframes scaleup {0% {transform: scale(0.3);}100% {transform: scale(1);}
}.scaleup {animation: scaleup 1s;
}body {overflow-x: hidden;overflow-y: scroll;/* 背景渐变 */background: linear-gradient(90deg, rgba(247, 149, 51, .1), rgba(243, 112, 85, .1) 15%, rgba(239, 78, 123, .1) 30%, rgba(161, 102, 171, .1) 44%, rgba(80, 115, 184, .1) 58%, rgba(16, 152, 173, .1) 72%, rgba(7, 179, 155, .1) 86%, rgba(109, 186, 130, .1));
}.verticle-line {margin: 10px;
}* {box-sizing: border-box;
}.toc-link {text-overflow: ellipsis;white-space: nowrap; /* white-space属性为nowrap时,不会因为超出容器宽度而发生换行 */overflow: hidden;
}.banner {height: 400px;background-image: url(@/assets/bg3.jpg);background-size: cover;background-position: center;position: relative;color: #eee;.banner-content {position: absolute;bottom: 25%;width: 100%;text-align: center;text-shadow: 0.05rem 0.05rem 0.1rem rgb(0 0 0 / 30%);.post-title {font-size: 30px;margin-bottom: 20px;}.post-intro1,.post-intro2 {font-size: 14px;line-height: 1.5;i {margin-right: 5px;}}}
}.post-wrapper {display: flex;// border: 1px solid red;max-width: 1200px;margin: 40px auto;.post-content-wrapper {// border: 1px solid red;width: 75%;padding: 10px;.post-content {padding: 40px;background-color: #fff;border-radius: 10px;box-shadow: 0 4px 8px 6px rgba(7, 17, 27, .06);}}.post-sider-wrapper {// border: 1px solid red;padding: 7.5px;width: 25%;padding: 12px;.post-sider {border-radius: 10px;position: sticky;top: 20px;.toc-wrapper {flex-basis: 25%;box-shadow: 0 4px 8px 6px rgba(7, 17, 27, .06);padding: 20px;border-radius: 10px;margin-bottom: 15px;background-color: #fff;// border: 1px solid red;.toc-header {display: flex;align-items: center;margin-bottom: 5px;}#toc-content {position: sticky;top: 20px;box-sizing: border-box;// border: 1px solid red;a {transition: all 0.3s;text-decoration: none;display: block;line-height: 1.6em;padding-left: 10px;border-left: 4px solid transparent;color: #666261;&::before {display: none;}&.is-active-link {font-weight: normal;color: #fff;background-color: #00c4b6;border-left-color: #009d92;}}}}.latest-article {background-color: #fff;// border: 1px solid red;border-radius: 10px;padding: 20px;.latest-article-header {margin-right: 5px;margin-bottom: 8px;color: #555555;i {font-weight: bold;margin-right: 8px;}}.latest-article-item {display: flex;margin-bottom: 10px;.latest-post-cover {width: 60px;height: 60px;flex-shrink: 0;border-radius: 5px;overflow: hidden;img {height: 100%;width: 100%;object-fit: cover;transition: all 0.3s;&:hover {transform: scale(1.2);}}}.latest-post-info {margin-left: 10px;overflow: hidden;flex: 1;padding-top: 3px;display: flex;flex-direction: column;justify-content: space-between;line-height: 1.2;.latest-post-title {color: #53504f;display: -webkit-box;word-break: break-all;-webkit-box-orient: vertical;-webkit-line-clamp: 2;overflow: hidden;font-size: 16px;&:hover {color: #00c4b6;}}.latest-post-desc {color: #989898;font-size: 13px;}}}}}}
}
</style><template><div><navbar /><div class="banner slidedown"><div class="banner-content"><div class="post-title">Spring Security框架方法注解源码</div><div class="post-intro1"><i class="iconfont icon-rili"></i><span>发表于 2023-03-07</span><span class="verticle-line">|</span><i class="iconfont icon-gengxinshijian"></i><span>更新于 2023-03-07</span><span class="verticle-line">|</span><i class="iconfont icon-fenlei"></i><span>Java</span></div><div class="post-intro2"><i class="iconfont icon-wenben"></i><span>字数统计: 2.4k</span><span class="verticle-line">|</span><i class="iconfont icon-shichang"></i><span>阅读时长: 6分钟</span><span class="verticle-line">|</span><i class="iconfont icon-liulan-2"></i><span>阅读量: 79</span><span class="verticle-line">|</span><i class="iconfont icon-wodepinglun"></i><span>评论数: 1</span></div></div></div><div class="post-wrapper slideup"><div class="post-content-wrapper"><div class="post-content"><div v-html="htmlContent" id="article-content"></div></div></div><div class="post-sider-wrapper"><div class="post-sider"><div class="toc-wrapper scaleup"><div class="toc-header"><img src="@/assets/svg/menu.svg" style="margin-right: 8px;" width="16px" height="16px" alt=""><span>目录</span></div><div id="toc-content"></div></div><div class="latest-article scaleup"><div class="latest-article-header"><i class="iconfont icon-gengxinshijian"></i><span>最新文章</span></div><a href="#" class="latest-article-item"><div class="latest-post-cover"><img src="@/assets/post4.jpg" alt=""></div><div class="latest-post-info"><div class="latest-post-title">mybatismybatismybatismybatismybatismybatismybatis复杂结果映射mybatis复杂结果映射mybatis复杂结果映射</div><div class="latest-post-desc">2023-03-07</div></div></a><a href="#" class="latest-article-item"><div class="latest-post-cover"><img src="@/assets/post2.jpg" alt=""></div><div class="latest-post-info"><div class="latest-post-title">mybatis</div><div class="latest-post-desc">2023-03-07</div></div></a><a href="#" class="latest-article-item"><div class="latest-post-cover"><img src="@/assets/post3.jpg" alt=""></div><div class="latest-post-info"><div class="latest-post-title">mybatimybatis复杂结果映射mybatis复杂结果映射mybatis复杂结果映射</div><div class="latest-post-desc">2023-03-07</div></div></a></div></div></div></div><div style="height: 500px;"></div></div>
</template><script>
import Navbar from './Navbar.vue';
import { getArticle } from '@/api/articleApi'import ClipboardJS from 'clipboard'
console.log(ClipboardJS);import MarkdownIt from 'markdown-it'
const MarkdownIt2 = require('markdown-it')import hljs from 'highlight.js'
import 'highlight.js/styles/atom-one-dark.css'
import tocbot from 'tocbot'
console.log('tocbot', tocbot);let md1 = new MarkdownIt()
let md2 = new MarkdownIt2()
console.log(md1);
console.log(md2);console.log(md1.render('# markdown-it rulezz!'));/* h1>markdown-it rulezz!</h1> */
console.log(md2.render('# markdown-it rulezz!'));/* h1>markdown-it rulezz!</h1> */const md = new MarkdownIt({html: true,linkify: true,typographer: true,breaks: true,highlight: function (str, lang) {/* str-> @Configuration@EnableWebMvc@EnableGlobalMethodSecurity(prePostEnabled = true) // 因为要控制controller中的方法访问,所以此注解要加到子容器中@ComponentScan(basePackages = "com.zzhua.controller",excludeFilters = {@ComponentScan.Filter(type = FilterType.ANNOTATION,classes = Service.class)})public class MyWebConfig implements WebMvcConfigurer {@Overridepublic void configureDefaultServletHandling(DefaultServletHandlerConfigurer configurer) {// 开启静态资源访问configurer.enable();}}lang-> java {name: 'Java', aliases: Array(1), keywords: {…}, illegal: /<\/|#/, contains: Array(23), …}*/console.log('str->', str, 'lang->', lang, hljs.getLanguage('java'));/* <span class="hljs-meta">@Configuration</span><span class="hljs-meta">@EnableWebMvc</span><span class="hljs-meta">@EnableGlobalMethodSecurity(prePostEnabled = true)</span> <span class="hljs-comment">// 因为要控制controller中的方法访问,所以此注解要加到子容器中</span><span class="hljs-meta">@ComponentScan(basePackages = "com.zzhua.controller",excludeFilters = {@ComponentScan.Filter(type = FilterType.ANNOTATION,classes = Service.class)})</span><span class="hljs-keyword">public</span> <span class="hljs-keyword">class</span> <span class="hljs-title class_">MyWebConfig</span> <span class="hljs-keyword">implements</span> <span class="hljs-title class_">WebMvcConfigurer</span> {<span class="hljs-meta">@Override</span><span class="hljs-keyword">public</span> <span class="hljs-keyword">void</span> <span class="hljs-title function_">configureDefaultServletHandling</span><span class="hljs-params">(DefaultServletHandlerConfigurer configurer)</span> {<span class="hljs-comment">// 开启静态资源访问</span>configurer.enable();}}*/console.log(hljs.highlight(str, { language: lang }).value);if (lang && hljs.getLanguage(lang)) {try {let highlightedHtml = hljs.highlight(str, { language: lang }).value// 生成行号let lineNum = highlightedHtml.split('\n').length - 1let lineNumbersRowsStart = `<span aria-hidden="true" class="line-numbers-rows">`let lineNumbersRowsEnd = `</span>`for (let i = 0; i < lineNum; i++) {lineNumbersRowsStart += `<span></span>`}const lineNumbersRows = lineNumbersRowsStart + lineNumbersRowsEndlet languageName = `<b class="language-name">${lang}</b>`// 当前时间加随机数生成唯一的id标识var d = new Date().getTime();if (window.performance && typeof window.performance.now === "function") {d += performance.now();}const codeIndex = "xxxxxxxx-xxxx-4xxx-yxxx-xxxxxxxxxxxx".replace(/[xy]/g,function (c) {var r = (d + Math.random() * 16) % 16 | 0;d = Math.floor(d / 16);return (c == "x" ? r : (r & 0x3) | 0x8).toString(16);});// 复制功能需要一个textarea(这个id需要前面加个字母啥的,不能以数字开头)let textAreaHtml = `<textarea class="copy-textarea" id="copy${codeIndex}">${str}</textarea>`let copyButton = `<button class="copy-btn iconfont icon-fuzhi" data-clipboard-action="copy" data-clipboard-target="#copy${codeIndex}" type="button"></button>`/* 如果返回的不含pre code标签,它会自己加上;如果返回的含有pre code标签,它就不加了 */return `<pre><code class="language-${lang} hljs">${highlightedHtml}</code>${lineNumbersRows}${languageName}${copyButton}${textAreaHtml}</pre>`;} catch (__) { }}return ""}
}).use(require("markdown-it-sub")).use(require("markdown-it-sup")).use(require("markdown-it-mark")).use(require("markdown-it-abbr")).use(require("markdown-it-container")).use(require("markdown-it-deflist")).use(require("markdown-it-emoji")).use(require("markdown-it-footnote")).use(require("markdown-it-ins")).use(require("markdown-it-katex-external")).use(require("markdown-it-task-lists"));console.log('未用markdown-it-sub时: ', md1.render('H~2~0')); /* 未用markdown-it-sub时: <p>H~2~0</p> */
const md3 = new MarkdownIt().use(require('markdown-it-sub'))
console.log('使用markdown-it-sub时: ', md3.render('H~2~0')); /* 使用markdown-it-sub时: <p>H<sub>2</sub>0</p> */console.log(md3.render('Hello from mars :satellite:'));
md3.use(require('markdown-it-emoji')) /* <p>Hello from mars :satellite:</p> */
console.log(md3.render('Hello from mars :satellite:'));/* <p>Hello from mars 📡</p> */export default {name: 'Post',components: {Navbar},data() {return {mdContent: '',htmlContent: '',}},mounted() {getArticle(29).then(data => {this.mdContent = data.mdContent})},watch: {mdContent(newVal, oldVal) {let _this = thisthis.htmlContent = md.render(newVal)this.$nextTick(() => {var clipboard = new ClipboardJS('.copy-btn');clipboard.on('success', function (e) {console.info('Action:', e.action);console.info('Text:', e.text);console.info('Trigger:', e.trigger);_this.$toast('success', '复制成功了哦');e.clearSelection();});clipboard.on('error', function (e) {console.error('Action:', e.action);console.error('Trigger:', e.trigger);});// console.log('$nextTick...');let articleContent = document.querySelector('#article-content')let headingTag = ['h1', 'h2', 'h3']let children = Array.from(articleContent.children)for (let i = 0; i < children.length; i++) {const e = children[i];e.id = `h-${i}`}hljs.highlightAll()tocbot.init({// Where to render the table of contents.tocSelector: '#toc-content',// Where to grab the headings to build the table of contents.contentSelector: '#article-content',// Which headings to grab inside of the contentSelector element.headingSelector: 'h1, h2, h3, h4, h5, h6',// For headings inside relative or absolute positioned containers within content.hasInnerContainers: true,});})}}
}
</script>