likes
comments
collection
share

Nodejs 第二十三章 Markdown 转 html

作者站长头像
站长
· 阅读数 26

什么是markdown语法

Markdown是一种轻量级标记语言,它允许人们使用易读易写的纯文本格式编写文档,然后转换成有效的XHTML(或者HTML)文档。由John Gruber和Aaron Swartz共同创建于2004年,Markdown的目的是实现「易读易写」,其语法灵感来源于电子邮件的格式化文本

markdown语法的必要性

  1. 易于学习和使用:Markdown的语法简单直观,常用的标记符号不超过十个,这使得即使是Markdown的初学者也能快速上手并使用它来进行日常的写作任务。
  2. 纯文本:Markdown文件是纯文本,因此可以使用任何文本编辑器打开和编辑,同时也易于版本控制。
  3. 转换灵活:Markdown文本可以通过工具轻松转换为HTML、PDF、EPUB等多种格式,便于发布与分享。
  • 上面三个特点是markdown语法的巨大优势,基本上涵盖了写文章最必要的几个要素

    • 包括说我目前写这篇文章,所采用的也是markdown语法。在通过Typora软件进行转化的时候,也非常的方便。

Nodejs 第二十三章 Markdown 转 html

markdown用途

  • Markdown的简洁与灵活使其成为程序员、作家和许多需要编写丰富文档的用户的首选语言。它特别适合写作和快速记录注释,也被广泛用于编写README文件、科学文档、博客、论坛和在线讨论
  • 我们看到的各种技术博客文档,基本上都是采用markdown语法进行的

使用到的第三方库

虽然市面上有很多已经集成好的markdown编辑器,但具备转化功能的免费软件其实还是较少的。为此,不妨我们自己来实现一次这个功能

第三方库概述

  • 我们先对即将使用到的三个第三方库进行几个简要的了解
  1. EJS (Embedded JavaScript Templating) :

    • 功能:一个JavaScript模板引擎,允许开发者将JavaScript变量和函数嵌入到HTML中,用于生成动态内容。
    • 优点:支持复杂的逻辑处理,如条件判断和循环,易于生成复杂的HTML结构。
  2. Marked:

    • 功能:一个Markdown解析器,用于将Markdown文本转换为HTML代码。
    • 优点:快速、轻量且可配置,支持标准Markdown和GitHub Flavored Markdown(GFM)。
  3. BrowserSync:

    • 功能:一个开发工具,可同时在多个设备和浏览器上同步页面改动,并实时刷新查看效果。
    • 优点:支持多设备实时预览,自动化页面刷新,提高响应式和跨设备测试的效率。

EJS语法

官网地址:EJS -- 嵌入式 JavaScript 模板引擎 | EJS 中文文档 (bootcss.com)

具体的使用方式从官网学习

Nodejs 第二十三章 Markdown 转 html

  • 数据绑定:EJS允许将服务器端的数据直接绑定到HTML标记中,从而在渲染页面时动态生成内容。
  • 代码重用:通过使用EJS的<% include %>标签,可以重用HTML片段,如头部、尾部或导航栏,从而简化代码维护。
  • 条件语句和循环:EJS支持JavaScript的控制语句,如ifelsewhilefor等,使得在模板中处理逻辑成为可能。

1.纯脚本标签

<% code %> 里面可以写任意的 js,用于流程控制,无任何输出

 <% alert('hello world') %> // 会执行弹框

2.输出经过 HTML 转义的内容

<%= value %> 可以是变量 <%= a ? b : c %> 也可以是表达式 <%= a + b %> 即变量如果包含 '<'、'>'、'&'等HTML字符,会被转义成字符实体,像< > & 因此用<%=,最好保证里面内容不要有HTML字符

 const text = '<p>猴赛雷</p>'
 <h2><%= text %></h2> 
 // 输出 &lt;p&gt;猴赛雷&lt;/p&gt; 插入 <h2> 标签中

3. 输出非转义的内容(原始内容)

<%- 富文本数据 %> 通常用于输出富文本,即 HTML内容 上面说到<%=会转义HTML字符,那如果我们就是想输出一段HTML怎么办呢? <%-不会解析HTML标签,也不会将字符转义后输出。像下例,就会直接把 <p>我来啦</p> 插入标签中

 const content = '<p>标签</p>'
 <h2><%- content %></h2>

4. 引入其他模版

<%- include('***文件路径') %> 将相对于模板路径中的模板片段包含进来。 用<%- include指令而不是<% include,为的是避免对输出的 HTML 代码做转义处理。

 // 当前模版路径:./views/tmp.ejs
 // 引入模版路径:./views/user/show.ejs
 <ul>
   <% users.forEach(function(user){ %>
     <%- include('user/show', {user: user}); %>
   <% }); %>
 </ul>

5. 条件判断

 <% if (condition1) { %>
   ... 
 <% } %>
 ​
 <% if (condition1) { %>
   ... 
 <% } else if (condition2) { %>
   ... 
 <% } %>
 ​
 // 举例
 <% if (a && b) { %>
   <p>可以直接放 html 内容</p>
 <% } %>
 ​
 <% if (a && b) { %>
   <% console.log('也可以嵌套任意ejs模版语句') %>
 <% } %>

6. 循环

 //使用传统 for 循环遍历数组
 //这种方式适合于需要索引的场景,因为可以直接使用变量 i 获取当前循环的索引
 <% 
 // `target` 应该是一个数组或类数组对象。
 // 循环从0开始,直到数组的长度。
 for(var i = 0; i < target.length; i++){ 
 %>
   <%= i %> <%= target[i] %> <!-- 输出当前的索引 `i` 和对应的数组元素 `target[i]` -->
 <% } %>
 ​
 <% 
 // 使用for-in循环遍历对象 `jsArr` 的所有属性。
 // 通常 `jsArr` 应该是一个对象,其中的属性是脚本文件的路径。
 for(var i in jsArr) { 
 %>
   <script type="text/javascript" src="<%= jsArr[i] %>" ref="preload"></script> <!-- 动态插入脚本标签,其src属性是由对象 `jsArr` 的属性值决定的 -->
 <% } %>
 ​
 //推荐方式
 <% 
 // 使用for-of循环遍历数组 `cssArr`。
 // `cssArr` 应该是包含CSS文件链接的数组。
 // 这是一种简洁的方式来遍历数组的元素,不需要使用索引。
 for(var css of cssArr) { 
 %>
   <link rel="stylesheet" href="<%= css %>" /> <!-- 创建link标签,链接到CSS文件。属性href的值来自数组 `cssArr` 中的元素 -->
 <% } %>
 ​

初始化模板

  • 以下代码块为template.ejs,也就是后缀为ejs的文件

    • 初始化模板 被<%- XXX %>到时候会转换成html代码

    这里需要区分:

    • <%= %>:这个语法用于输出转义后的HTML内容。当我们的数据可能包含HTML标签或特殊字符时,使用 <%= %> 可以避免跨站脚本攻击(XSS),因为它会将字符如 <>& 等转义成HTML实体。
    • <%- %>:这个语法用于输出原始HTML内容。当我们确定嵌入的内容是安全的,或者需要包括HTML标签时使用。在包含另一个模板时,使用 <%- %> 可以确保模板的HTML标签不会被转义,从而正常地作为HTML元素被渲染。
 <!DOCTYPE html>
 <html lang="en">
 <head>
     <meta charset="UTF-8">
     <meta name="viewport" content="width=device-width, initial-scale=1.0">
     <title><%= title %></title>
     <link rel="stylesheet" href="./index.css">
 </head>
 <body>
     <%- content %>
 </body>
 </html>

使用场景

  • 动态网站开发,例如博客、电商平台,其中内容需要根据数据库的数据变化而变化。
  • 生成电子邮件模板,根据用户的行为发送定制化的通知。

marked

Marked 是一个快速且灵活的Markdown解析器,将Markdown文档转换为HTML是它的主要功能。它支持标准的Markdown语法,并提供了扩展功能,如GFM(GitHub Flavored Markdown)

核心功能

  • 高性能:Marked 的解析速度非常快,适合处理大量文档。
  • 灵活性:支持自定义渲染器,允许开发者根据需要修改生成的HTML。
  • 兼容性:支持多种Markdown语法,包括GFM和原始Markdown。

使用场景

  • 在线文档和帮助系统,允许快速渲染Markdown为用户友好的HTML页面。
  • 博客平台,用户写作的Markdown文章可以即时转换并显示。

Nodejs 第二十三章 Markdown 转 html

使用方式

  • 将md 转换成html
 const marked = require('marked')
 marked.parse(readme.toString()) //调用parse即可,其中readme就是要转化的markdown内容

browserSync

BrowserSync 是一个用于前端开发的工具,它可以同步多个设备和浏览器中的动作,例如滚动、点击和刷新

核心功能

  • 实时重载:当文件被修改时,BrowserSync能自动刷新所有已连接的设备和浏览器,显示最新的内容。
  • 设备同步:在多个设备上测试时,可以同步我们在一个设备上的操作到其他所有设备,如滚动、表单输入等。
  • 易于集成:可以与大多数构建工具和预处理器集成,如Gulp、Webpack等。

使用场景

  • 响应式网站开发,同时在多个设备和屏幕尺寸上预览网站,确保跨设备兼容性。
  • 快速测试和调试,改善开发流程的效率,尤其是在涉及多页面和复杂交互的大型项目中

使用方式

  • 创建browser 并且开启一个服务 设置根目录和 index.html 文件

    • 能够快速启动服务,运行我们的HTML文件,且支持热更新等功能
 const browserSync = require('browser-sync')
 const openBrowser =  () => {
     const browser = browserSync.create()
     browser.init({//初始化browser 
         server: {
             baseDir: './',//根目录
             index: 'index.html',//入口文件
         }
     })
     return browser
 }    
  • html代码有了 但是没有通用的markdown的通用css。这个就跳过好了(因为太长了),仁者见仁智者见智,根据自己的喜好设置自己喜欢的样式

markdown转HTML案例

前置基础框架建设

  • 首先安装这三个库:

    • npm i ejs marked browser-sync
  • 然后初始化package.json文件:npm init -y

  • 创建模板文件template.ejs

  • 创建必要的index.js入口文件,markdown的index.css样式文件

Nodejs 第二十三章 Markdown 转 html

建立模板

  • 把我们在前面EJS语法中的初始化模板搬过来进行使用就OK了

    • 其中title和content都是占位符,到时候会有内容将其替换

Nodejs 第二十三章 Markdown 转 html

实现转化效果

  • 通过ejs,我们就能实现将markdown转化为HTML的效果,接下来就让我们来梳理下流程吧!

    • 首先我们需要获取到markdown的内容,这是第一步

    • 有了基础的内容,我们就要把它往我们的模板身上套了。还记得我们前面的初始化模板吗?

      一共有两个占位符,一个是title标题,一个是content内容

  • 但要怎么套呢?

    • 通过ejs的renderFile方法就能够做到这点,其中有三个参数

      1. 参数1:传递我们的模板
      2. 参数2:传递我们提前在模板中写好的占位符
      3. 参数3:回调函数返回的结果,分别是err(错误)和str(结果)
    • 那这样就好办了呀,我们将template.ejs套进参数1模板里面,参数2设置好title和content,参数3拿到结果写入要将markdown转为HTML的文件就结束了

  • 这里有几个点,在参数2中,title是字符串好说,但content就是markdown的内容了,在获取的时候,我们要先通过fs模块的读取文件拿到内容,然后通过marked第三方库将其转化为HTML的展示形态

    • 最后在参数3的回调函数判断一下是否有发送错误,有则返回错误,没有就使用fs模块的writeFileSync写入功能,创建出新的index.html展示结果
 const ejs = require('ejs'); // 导入ejs库,用于渲染模板
 const fs = require('node:fs'); // 导入fs模块,用于文件系统操作
 const marked = require('marked'); // 导入marked库,用于将Markdown转换为HTML
 const browserSync = require('browser-sync'); // 导入browser-sync库,用于实时预览和同步浏览器
 ​
 ​
 const init = () => {
   const md = fs.readFileSync('./test.md', 'utf-8'); // 读取test.md文件的内容
 ​
   // 参数1:渲染对象,参数2:填充占位符内容,参数3:回调函数返回内容处理
   ejs.renderFile('./template.ejs', {
     // 拿到markdown文档的内容,进行转化为html
     content: marked.parse(md),
     title: " Nodejs 第二十三章 Markdown 转 html"
   }, (err, str) => {
     // 有报错返回报错,没有则正常写入内容到一个新的HTML文件中,这也是我们要做到的将markdown转为HTML
     if (err) return err
     fs.writeFileSync('index.html', str)
   });
 }
 ​
 init()

Nodejs 第二十三章 Markdown 转 html

  • 通过打开生成的效果图,能看到是成功了的

Nodejs 第二十三章 Markdown 转 html

热更新效果

  • 利用第三方库browserSync起一个服务

    • 在server选项中,我们设置了根目录,然后依据根目录的相对位置,设置了默认启动文件index.html
 const browserSync = require('browser-sync')
 const server =  () => {
     const browser = browserSync.create()
     browser.init({
         server: {
             baseDir: './',
             index: 'index.html',
         }
     })
     return browser
 }
  • 然后在内容写入到index.html的时候调用这个server函数去起服务器

Nodejs 第二十三章 Markdown 转 html

  • 能够看到已经正常启动服务器了,但此时还没有热更新的功能,我们修改了markdown文档的内容,浏览器并没有跟着一起改变

    • 我们通过fs模块的WatchFile进行监听markdown文档是否发生了变化,如果变化就重新调用服务。
    • 但这个有一个问题,那就是当我们重新调用服务的时候,它会在原有基础上再开一个服务
 fs.watchFile('test.md',(curr,prev) => {
   if(curr.mtime !== prev.mtime) {
     init()
   }
 })
  • 但我们不希望连续开多个服务,而是重新刷新一下,那应该怎么做?

    • 首先我们在编写server函数的时候,得到browserSync的创建方法时,不应该进行固定,而是声明成全局变量
 //原有写法
 const browser = browserSync.create();
 ​
 //修改后的写法
 let browser;//提前声明全局变量
 ​
 browser = browserSync.create();
  • 在init方法函数中,传入一个callback回调函数用来处理重新刷新
 const init = (callback) => {
   const md = fs.readFileSync('./test.md', 'utf-8'); // 读取test.md文件的内容
 ​
   // 参数1:渲染对象,参数2:填充占位符内容,参数3:回调函数返回内容处理
   ejs.renderFile('./template.ejs', {
     // 拿到markdown文档的内容,进行转化为html
     content: marked.parse(md),
     title: " Nodejs 第二十三章 Markdown 转 html"
   }, (err, str) => {
     // 有报错返回报错,没有则正常写入内容到一个新的HTML文件中,这也是我们要做到的将markdown转为HTML
     if (err) return err
     fs.writeFileSync('index.html', str)
     callback && callback()
   });
 }
 ​
 fs.watchFile('test.md',(curr,prev) => {
   if(curr.mtime !== prev.mtime) {
     init(() =>{
       browser.reload()
     })
   }
 })
 init(() => {
   server()
 })
  • 然后再重新启动服务的时候,修改markdown文件,浏览器就会自己进行刷新,同时图标也显示了内容的更新状态

Nodejs 第二十三章 Markdown 转 html

  • 这样热更新的解决方法是很方便的,建立起全局变量。这样我们browserSync.create()browser.reload()指向的其实是同一个服务。那后者一旦reload重新加载了,相当于重新加载了一开始的服务,因为此时他们是同一个体

彩蛋

  • 这个BrowserSync我不知道大家还记不记得最前面介绍的时候,它的优点是什么?

    • 支持多设备实时预览,自动化页面刷新
    • 具体体现出来就是我们电脑滑动页面的时候,手机端也会跟着自动滑动到相对应的内容上。反之也是一样的
转载自:https://juejin.cn/post/7359510120248262683
评论
请登录