当前位置: 首页 > wzjs >正文

公司手机版网站模板专业摄影网站

公司手机版网站模板,专业摄影网站,百度最怕哪个投诉电话,网站建设2017主流代码语言1 引言 在上一篇文章《解决Vditor加载Markdown网页很慢的问题(ViteJSVditor)》中,我们通过设置域内CDN的方式解决Vditor加载Markdown网页很慢的问题。而在这篇文章中,笔者将会开发实现一个前端中很常见的需求:给基于Markdown渲染的文档网页增…

1 引言

在上一篇文章《解决Vditor加载Markdown网页很慢的问题(Vite+JS+Vditor)》中,我们通过设置域内CDN的方式解决Vditor加载Markdown网页很慢的问题。而在这篇文章中,笔者将会开发实现一个前端中很常见的需求:给基于Markdown渲染的文档网页增加一个目录组件。

需要说明的是,原生的Markdown标准并没有规定生成目录的写法,但是国内的博文网站似乎都支持一个拓展来实现目录的生成:

@[toc]

但是这样生成的目录是通常是位于文章页面的最上方,这样就失去了目录的意义。比较好的实现是像CSDN或者掘金一样,额外生成一个目录组件,并且固定在侧栏上方。这样可以在浏览文章的时候,随时定位所在的目录;同时还可以使用目录来导航。
在这里插入图片描述
阅读本文可能需要的前置文章:

  • 《通过JS模板引擎实现动态模块组件(Vite+JS+Handlebars)》
  • 《使用Vditor将Markdown文档渲染成网页(Vite+JS+Vditor)》

2 详叙

2.1 整体结构

将渲染Markdown文档的部分封装成单独的组件(post-article.js、post-article.handlebars和post-article.css),增加一个文章目录组件(post-toc.js、post-toc.handlebars、post-toc.css)。另外post-data.json是我们提前准备的博客文章,里面除了保存有Markdown格式的文档字符串,还有一些文章的相关数据;1.png和2.png则是文章中图片。项目组织结构如下:

my-native-js-app/
├── public/
│ ├── 1.png
│ ├── 2.png
│ └── post-data.json
├── src/
│ ├── components/
│ │ ├── post-article.css
│ │ ├── post-article.handlebars
│ │ ├── post-article.js
│ │ ├── post-toc.css
│ │ ├── post-toc.handlebars
│ │ └── post-toc.js
│ ├── main.js
│ └── style.css
├── index.html
└── package.json

还是按照代码的执行顺序来介绍这个功能的实现。首先还是index.html:

<!DOCTYPE html>
<html lang="en"><head><meta charset="UTF-8" /><link rel="icon" type="image/svg+xml" href="/vite.svg" /><meta name="viewport" content="width=device-width, initial-scale=1.0" /><title>Vite App</title>
</head><body><div id="app"><div id="post-article-placeholder"></div><div id="article-toc-placeholder"></div></div><script type="module" src="/src/main.js"></script>
</body></html>

主要就是增加了post-article-placeholder和article-toc-placeholder这两个元素,分别作为Markdown博文和博文目录的容器。其实这里面还有个页面布局的问题,不过这个问题我们下一篇文章再说。这里还是先看main.js:

import "./style.css";
import "./components/post-article.js";

2.2 博文内容组件

引用了post-article.js,也就是Markdown博文内容组件。那么就进入post-article.js:

import "./post-article.css";
import { CreateTocPanel } from "./post-toc.js";
import Handlebars from "handlebars";
import templateSource from "./post-article.handlebars?raw";import "vditor/dist/index.css";
import Vditor from "vditor";// 初始化文章标签面板
async function InitializePostArticlePanel() {try {   const response = await fetch("/post-data.json");if (!response.ok) {throw new Error("网络无响应");}const blogData = await response.json();// 编译模板const template = Handlebars.compile(templateSource);// 渲染模板const renderedHtml = template({blogMeta: blogData.blogMeta,});// 将渲染好的HTML插入到页面中document.getElementById("post-article-placeholder").innerHTML =renderedHtml;// 显示内容Vditor.preview(document.getElementById("post-content"), blogData.content, {cdn: window.location.origin,markdown: {toc: false,mark: true, //==高亮显示==footnotes: true, //脚注autoSpace: true, //自动空格,适合中英文混合排版},math: {engine: "KaTeX", //支持latex公式inlineDigit: true, //内联公式可以接数字},hljs: {style: "github", //代码段样式lineNumber: true, //是否显示行号},anchor: 2, // 为标题添加锚点 0:不渲染;1:渲染于标题前;2:渲染于标题后lang: "zh_CN", //中文theme: {current: "light", //light,dark,light,wechat},lazyLoadImage:"https://cdn.jsdelivr.net/npm/vditor/dist/images/img-loading.svg",transform: (html) => {// 使用正则表达式替换图片路径,并添加居中样式及题注return html.replace(/<img\s+[^>]*src="\.\/([^"]+)\.([a-zA-Z0-9]+)"\s*alt="([^"]*)"[^>]*>/g,(match, p1, p2, altText) => {// const newSrc = `${backendUrl}/blogs/resources/images/${postId}/${p1}.${p2}`;const newSrc = `${p1}.${p2}`;const imgWithCaption = `<div style="text-align: center;"><img src="${newSrc}" class="center-image" alt="${altText}"><p class="caption">${altText}</p></div>`;return imgWithCaption;});},after() {CreateTocPanel();},});} catch (error) {console.error("获取博客失败:", error);}
}document.addEventListener("DOMContentLoaded", InitializePostArticlePanel);

post-article.js中的内容改进自《通过JS模板引擎实现动态模块组件(Vite+JS+Handlebars)》中的案例,不过略有不同。首先是获取博文数据:

const response = await fetch("/post-data.json");
if (!response.ok) {throw new Error("网络无响应");
}
const blogData = await response.json();// 编译模板
const template = Handlebars.compile(templateSource);// 渲染模板
const renderedHtml = template({blogMeta: blogData.blogMeta,
});// 将渲染好的HTML插入到页面中
document.getElementById("post-article-placeholder").innerHTML =renderedHtml;

在实际项目开发中,应该是从远端API获取数据,这里进行了简化,将数据提前准备好了放置在域内。然后,将这个数据与编译的Handlebars模板一起渲染成HTML元素。从下面的post-article.handlebars中可以看到,博文组件中内容不仅包含Markdown博文内容元素,还有诸如时间、统计信息、标签等元素:

<div id="main-content"><h1 id="post-title">{{blogMeta.title}}</h1><div class="post-stats"><span class = "post-stat"><span>📝</span><span class = "text">已于</span>{{blogMeta.createdTime}}<span class = "text">修改</span></span><span class = "post-stat"><span>👁️</span>{{blogMeta.postStats.viewCount}}<span class = "text">阅读</span></span><span class = "post-stat"><span>👍</span>{{blogMeta.postStats.likeCount}}<span class = "text">点赞</span></span><span class = "post-stat"><span>💬</span>{{blogMeta.postStats.commentCount}}<span class = "text">评论</span></span></div><div class="post-tags"><span class = "tags-title"><span>🔖</span><span class = "text">文章标签</span></span>{{#each blogMeta.tagNames}}<span class = "post-tag">{{this}}</span>{{/each}}</div><div class="post-categories">专栏{{#each blogMeta.categoryNames}}<span> {{this}} </span>{{/each}}收录该内容</div><div id="post-content"></div>
</div>

Markdown博文内容元素是使用Vditor来渲染初始化的,这一点与之前的案例一样。不同的是增加了一个after配置:

import { CreateTocPanel } from "./post-toc.js";//...after() {CreateTocPanel();
},

这个after配置的意思是当Vditor渲染完成以后,就立刻执行CreateTocPanel()函数,这个函数来自于博文目录组件post-toc.js,表示要开始创建博文目录了。

2.2 博文目录组件

post-toc.js中的代码如下所示:

import "./post-toc.css";import Handlebars from "handlebars";
import templateSource from "./post-toc.handlebars?raw";export function CreateTocPanel() {const headings = document.querySelectorAll("#post-content h1, #post-content h2, #post-content h3");const tocContent = [];headings.forEach((heading, index) => {const content = {};content["id"] = heading.id;content["title"] = heading.textContent;const marginLeft =heading.tagName === "H2" ? 20 : heading.tagName === "H3" ? 40 : 0;content["marginLeft"] = marginLeft;tocContent.push(content);});// 编译模板const template = Handlebars.compile(templateSource);// 渲染模板const renderedHtml = template({tocContent,});// 将渲染好的HTML插入到页面中const articleTocPlaceholder = document.getElementById("article-toc-placeholder");articleTocPlaceholder.innerHTML = renderedHtml;// 联动:滚动时同步激活目录项window.addEventListener("scroll", () => {let activeHeading;headings.forEach((heading) => {const rect = heading.getBoundingClientRect();if (rect.top >= 0 && rect.top <= window.innerHeight / 2) {activeHeading = heading;}});if (activeHeading) {document.querySelectorAll(".toc-sidebar .toc a").forEach((link) => link.classList.remove("active"));     const escapedId = CSS.escape(activeHeading.id); //安全地转义选择器中的特殊字符const activeLink = document.querySelector(`.toc-sidebar .toc a[href="#${escapedId}"]`);if (activeLink) activeLink.classList.add("active");}});
}

这段代码是实现博文目录功能的关键代码。首先,搜索查询渲染成HTML形式的博文内容中的标题元素h1h2h3

const headings = document.querySelectorAll("#post-content h1, #post-content h2, #post-content h3");

然后提取出关键数据:

const tocContent = [];headings.forEach((heading, index) => {const content = {};content["id"] = heading.id;content["title"] = heading.textContent;const marginLeft =heading.tagName === "H2" ? 20 : heading.tagName === "H3" ? 40 : 0;content["marginLeft"] = marginLeft;tocContent.push(content);});

将其传入Handlebars模板进行渲染:

// 编译模板const template = Handlebars.compile(templateSource);// 渲染模板const renderedHtml = template({tocContent,});// 将渲染好的HTML插入到页面中const articleTocPlaceholder = document.getElementById("article-toc-placeholder");articleTocPlaceholder.innerHTML = renderedHtml;

模板post-toc.handlebars中的内容非常简单:

<div class="toc-sidebar"><div class="toc"><h3>文章目录</h3><ul>{{#each tocContent}}<li style="margin-left: {{marginLeft}}px;"><a href="#{{id}}" class="">{{title}}</a></li>{{/each}}</ul></div>
</div>

可以看到这里能够获取一级、二级还有三级标题,通过样式的缩进(margin-left)来体现标题的不同。另外,href属性的设置也保证了能通过点击来实现跳转。

最后实现联动,通过文章标题元素范围的判定,来高亮目录中标题元素的样式,让用户直到浏览到博文中的哪一段了:

// 联动:滚动时同步激活目录项window.addEventListener("scroll", () => {let activeHeading;headings.forEach((heading) => {const rect = heading.getBoundingClientRect();if (rect.top >= 0 && rect.top <= window.innerHeight / 2) {activeHeading = heading;}});if (activeHeading) {document.querySelectorAll(".toc-sidebar .toc a").forEach((link) => link.classList.remove("active"));     const escapedId = CSS.escape(activeHeading.id); //安全地转义选择器中的特殊字符const activeLink = document.querySelector(`.toc-sidebar .toc a[href="#${escapedId}"]`);if (activeLink) activeLink.classList.add("active");}});

3 结语

最终实现的效果如下图所示:
在这里插入图片描述

虽然功能大致实现了,不过还有一些问题没有说清楚,比如在浏览文章的过程中,博文目录是如何始终保证黏在页面的右上角的?这个问题就放在下篇中继续论述了。

实现代码


文章转载自:

http://QCngmV9b.jmwrj.cn
http://5SwE2CHc.jmwrj.cn
http://wci31B4o.jmwrj.cn
http://6hyJ8wRO.jmwrj.cn
http://g1FLJzh4.jmwrj.cn
http://LOB4tN39.jmwrj.cn
http://JTDNZ0sG.jmwrj.cn
http://yvM99F5S.jmwrj.cn
http://rLkucJsU.jmwrj.cn
http://Ji1WUaaD.jmwrj.cn
http://G4I87Z6p.jmwrj.cn
http://RWpT9ZgR.jmwrj.cn
http://pvsh4zqt.jmwrj.cn
http://4TCdM5Jr.jmwrj.cn
http://WyZZXSBb.jmwrj.cn
http://oGwkWBbu.jmwrj.cn
http://ZqGI6c3l.jmwrj.cn
http://hzv6cLQV.jmwrj.cn
http://OTZZ6LcM.jmwrj.cn
http://awfwdu4N.jmwrj.cn
http://uVC45MZV.jmwrj.cn
http://If5XcD7d.jmwrj.cn
http://t7B3GlWO.jmwrj.cn
http://ngJJlE1q.jmwrj.cn
http://CEi6cNHh.jmwrj.cn
http://1Dh4AXDo.jmwrj.cn
http://OL26EWca.jmwrj.cn
http://Gt2IpBMo.jmwrj.cn
http://qzDdLgrf.jmwrj.cn
http://TLbWu2eN.jmwrj.cn
http://www.dtcms.com/wzjs/681874.html

相关文章:

  • 网站域名和空间费用seo公司上海牛巨微
  • 东莞企业网站定制设计wordpress 蘑菇街
  • 网站项目申请地方网站推广
  • 网站制作公司哪儿济南兴田德润有活动吗一般设计网站页面用什么软件做
  • 免费的企业网站制作推广公司经营范围
  • 网站建设优化方法网站如何做点击链接
  • 南京企业建网站流程wordpress网站关键词设置
  • wordpress 网站建设wordpress壁纸小程序
  • 网站建设企业建站哪家好?来这里看看html5制作手机网站教程
  • 做网站应该画什么图企业微信邮箱登录
  • 摄影网站的制作宝塔搭建wordpress访问很慢
  • 网站建设翻译成英文网站如何做免费推广
  • 信息技术网站开发最好用的建站系统
  • 小游戏网站建设公司中国正式宣布出兵
  • 站酷网官方入口网页版网站设置为应用程序
  • 东营做网站哪家好wordpress 调用豆瓣
  • 网站建设的定位是什么股票订阅网站开发
  • 城乡与住房建设部网站办事大厅微型营销网站制作
  • 网站建设海报素材图片母婴网站建设的与功能模块
  • 空白网站怎么建WordPress报错关闭
  • 外贸网站谷歌优化搭建网站的价格
  • 自己可以做网站保亭网站建设
  • 专业网站建设设计公司郑州公司做网站汉狮
  • 网站内容与功能设计与实现的wordpress增加购物车
  • 外贸看的英文网站网站建设空间域名是什么
  • 视频网站做短视频WordPress多页悬浮菜单
  • asp net网站开发语言的特点制作一个网站的步骤是什么
  • php网站开发哪个好哈尔滨市工程信息网
  • 无锡网站怎么推广效果好做互联网需要网站吗
  • 网站建设新手如何自己做网站正规的培训行业网站制作