整合营销服务商

电脑端+手机端+微信端=数据同步管理

免费咨询热线:

Node.js 学习笔记:构建 Web 服务

Node.js 学习笔记:构建 Web 服务

例3. 构建 Web 服务器

这部分示例将致力于用 Node.js 模拟一个类似于 Apache 的 Web 服务器,处理浏览器端的请求,将相关的页面响应给浏览器。首先,我们要在code目录下执行mkdir 03_webSever命令来创建用于存放这一组示例的目录。然后执行以下步骤:

  1. 在code/03_webSever目录下执行mkdir www命令,创建网站目录,然后在其中创建index.htm和login.htm两个 HTML 文件以及一个名为style.css的 CSS 文件:

1、index.htm:

<!DOCTYPE html>
<html lang="zh-cn">
<head>
<meta charset="utf-8" />
<link rel="stylesheet" type="text/css" href="style.css" />
<title>首页</title>
</head>
<body>
<h1>你好,nodejs!</h1>
<p> <a href="login.htm">请登录!</a> </p>
</body>
</html>

2、login.htm:

<!DOCTYPE html>
<html lang="zh-cn">
<head>
<meta charset="utf-8" />
<link rel="stylesheet" type="text/css" href="style.css" />
<title>登录页面</title>
</head>
<body>
<h1>你已经登录。。。</h1>
<p> <a href="index.htm">回首页!</a> </p>
</body>
</html>

3、style.css:

body { 
background: gray; 
}

2、在code/03_webSever目录下执行touch 03-webServer.js命令,创建脚本文件,并输入如下代码:

const http=require('http')
const fs=require('fs')
const server=http.createServer()

server.on('request', function(req, res) {
const webRoot='./www'
const url=req.url
if ( url==='/' ) {
url='/index.htm'
}

fs.readFile(webRoot+url, function(err, data) {
if ( err !==null ) {
console.error('错误信息:' + err.message)
return res.end('<h1>404 页面没找到!</h1>')
}
res.end(data)
})
})

server.listen(8080, function(){
console.log('请访问http://localhost:8080/,按Ctrl+C终止服务!')
})

3、保存所有文件后,在code/03_webSever目录下执行node 03-webServer.js命令,然后打开浏览器并访问http://localhost:8080/,就会看到如下页面:

示例4. 使用art-template模版引擎生成网页

这一部分本示例将以生成个人信息页面为例,演示在服务器端基于 Node.js 使用art-template模板引擎来生成网页。为此,我们需要在code目录下执行mkdir 04_templatingEngine命令来创建用于存放这一组示例的目录。

1. 单模版渲染

首先来示范一下如何使用art-template模版引擎的渲染单一模版文件,请跟着以下步骤来构建示例:

  1. 在code/04_templatingEngine目录下执行npm install art-template --save命令,安装将art-template包安装到当前示例项目中。
  2. 在code/04_templatingEngine目录下执行touch singleTpl.htm命令,创建一个模版文件,并在其中输入以下代码:
<!DOCTYPE html>
<html lang="zh-cn">
<head>
<meta charset="utf-8" />
<link rel="stylesheet" type="text/css" href="style.css" />
<title>{{ name }}的个人信息</title>
</head>
<body>
<h1>{{ name }}的个人信息</h1>
<table>
<tr><td>姓名:</td><td>{{ name }}</td></tr>
<tr><td>年龄:</td><td>{{ age }}</td></tr>
<tr><td>性别:</td><td>{{ sex }}</td></tr>
<tr>
<td>爱好:</td>
<td>{{ each items }} {{ $value }} {{ /each }}</td>
</tr>
</table>
</body>
</html>

3、在code/04_templatingEngine目录下执行touch 04-useTemplating_engine.js命令,创建一个脚本文件,具体如下:

const http=require('http')
const fs=require('fs')
const template=require('art-template')

class human {
constructor(name, age, sex, items=[])
{
this.name=name
this.age=age
this.sex=sex
this.items=items
}
}

const server=http.createServer()

server.on('request', function(req, res){
const url=req.url
let boy=null
if ( url==='/' ) {
boy=new human('凌杰', '37', '男', ['看书', '看电影','旅游'])
} else if ( url==='/wang' ) {
boy=new human('蔓儿', '25', '女', ['看书', '看电影','写作'])
}

if ( boy===null ) {
return res.end('<h1>404 页面没找到!</h1>')
}

fs.readFile('./singleTpl.htm', function(err, data){
if ( err !==null ) {
return res.end('<h1>404 没找到模版文件!</h1>')
}

const strHtml=template.render(data.toString(), {
name : boy.name,
age : boy.age,
sex : boy.sex,
items: boy.items
})

res.end(strHtml)
})
})

server.listen(8080, function(){
console.log('请访问http://localhost:8080/,按Ctrl+C终止服务!')
})

4、保存所有文件后,在code/04_templatingEngine目录下执行node 04-useTemplating_engine.js命令,然后打开浏览器并访问http://localhost:8080/wang,就会看到如下页面:

2. 多模版组合渲染

在同一 Web 应用中,所有的页面通常都由相同的头部和底部元素,所以为了减少代码的冗余,提高重用率,开发者们通常会考虑将重复的部分独立成一个单独的模版文件,然后用相互包含的方式组合成页面。下面就继续以art-template模板引擎为例来演示一下如何将多个模版组合渲染成单一的 HTML 页面,其具体步骤如下:

  1. 在code/04_templatingEngine目录下执行touch tpl1.art tpl2.art命令,创建两个模版文件,然后在这两个文件中分别输入以下代码:

1、tpl1.art :

<header>
<h1>查看个人信息</h1>
<br>
</header>

2、tpl2.art :

<footer>
<div>
<p>? 2016 owlman.org;本站系纯HTML5站点。</p>
</div>
</footer>

2、在code/04_templatingEngine目录下执行touch multiTpl.htm命令创建用于组合的 HTML 页面文件,并在其中输入以下代码:

<!DOCTYPE html>
<html lang="zh-cn">
<head>
<meta charset="utf-8" />
<link rel="stylesheet" type="text/css" href="style.css" />
<title>查看个人信息</title>
</head>
<body>
{{ include './tpl1.art' }}
<h2>{{ name }}的个人信息</h2>
<table>
<tr><td>姓名:</td><td>{{ name }}</td></tr>
<tr><td>年龄:</td><td>{{ age }}</td></tr>
<tr><td>性别:</td><td>{{ sex }}</td></tr>
<tr>
<td>爱好:</td>
<td>{{ each items }} {{ $value }} {{ /each }}</td>
</tr>
</table>
{{ include './tpl2.art' }}
</body>
</html>

3、在code/04_templatingEngine目录下执行

cp 04-useTemplating_engine.js 04-useTemplating_engine2.js命令,将之前的代码复制一份,并修改如下:

const http=require('http')
const fs=require('fs')
const template=require('art-template')

template.defaults.root=__dirname // 配置模版的查找根目录

class human {
constructor(name, age, sex, items=[])
{
this.name=name
this.age=age
this.sex=sex
this.items=items
}
}

const server=http.createServer()

server.on('request', function(req, res){
const url=req.url
let boy=null
if ( url==='/' ) {
boy=new human('凌杰', '37', '男', ['看书', '看电影','旅游'])
} else if ( url==='/wang' ) {
boy=new human('蔓儿', '25', '女', ['看书', '看电影','写作'])
}

if ( boy===null ) {
return res.end('<h1>404 页面没找到!</h1>')
}

fs.readFile('./multiTpl.htm', function(err, data){ // 修改了要读取的模版文件
if ( err !==null ) {
return res.end('<h1>404 没找到模版文件!</h1>')
}

const strHtml=template.render(data.toString(), {
name : boy.name,
age : boy.age,
sex : boy.sex,
items: boy.items
})

res.end(strHtml)
})
})

server.listen(8080, function(){
console.log('请访问http://localhost:8080/,按Ctrl+C终止服务!')
})

4、保存所有文件后,在code/04_templatingEngine目录下执行node 04-useTemplating_engine2.js命令,然后打开浏览器并访问http://localhost:8080,就会看到如下页面:

3. 多模版继承渲染

当然,如果重复的元素只有头部和尾部的话,有时候使用模版继承语法来渲染页面会是一个更好的选择,下面就来继续演示一下art-template模板引擎的继承语法来渲染 HTML 页面,其具体步骤如下:

  1. 在code/04_templatingEngine目录下执行touch baseTpl.art命令,创建父模版文件,然后在该文件中输入以下代码:
<!DOCTYPE html>
<html lang="zh-cn">
<head>
<meta charset="utf-8" />
<link rel="stylesheet" type="text/css" href="style.css" />
<title>{{ name }}的个人信息</title>
</head>
<body>
<header>
<h1>查看个人信息</h1>
<br>
</header>

{{ block 'message' }}
{{ /block }}

<footer>
<div>
<p>? 2016 owlman.org;本站系纯HTML5站点。</p>
</div>
</footer>
</body>
</html>

2、在code/04_templatingEngine目录下执行touch extendTpl.htm命令,创建子模版文件,然后在该文件中输入以下代码:

{{ extend 'baseTpl.art' }}

{{ block 'message' }}
<h1>{{ name }}的个人信息</h1>
<table>
<tr><td>姓名:</td><td>{{ name }}</td></tr>
<tr><td>年龄:</td><td>{{ age }}</td></tr>
<tr><td>性别:</td><td>{{ sex }}</td></tr>
<tr>
<td>爱好:</td>
<td>{{ each items }} {{ $value }} {{ /each }}</td>
</tr>
</table>
{{ /block }}

3、在code/04_templatingEngine目录下执行cp 04-useTemplating_engine.js 04-useTemplating_engine3.js命令,将之前的代码复制一份,并修改如下:

// 用Node.js生成动态页面
// 作者:owlman
// 时间:2019年07月12日

const http=require('http')
const fs=require('fs')
const template=require('art-template')

template.defaults.root=__dirname

class human {
constructor(name, age, sex, items=[])
{
this.name=name
this.age=age
this.sex=sex
this.items=items
}
}

const server=http.createServer()

server.on('request', function(req, res) {
const url=req.url
let boy=null
if (url==='/') {
boy=new human('凌杰', '37', '男', ['看书', '看电影','旅游'])
} else if (url==='/wang') {
boy=new human('蔓儿', '25', '女', ['看书', '看电影','写作'])
}

if (boy===null) {
return res.end('<h1>404 页面没找到!</h1>')
}

fs.readFile('./extendTpl.htm', function(err, data) {
if ( err !==null ) {
return res.end('<h1>404 没找到模版文件!</h1>')
}

const strHtml=template.render(data.toString(), {
name : boy.name,
age : boy.age,
sex : boy.sex,
items: boy.items
})

res.end(strHtml)
})
})

server.listen(8080, function(){
console.log('请访问http://localhost:8080/,按Ctrl+C终止服务!')
})

4、保存所有文件后,在code/04_templatingEngine目录下执行node 04-useTemplating_engine3.js命令,然后打开浏览器并访问http://localhost:8080,就会看到与之前相同的页面。

示例5. Web 表单处理

这一部分示例将致力于演示用 Node.js 处理 Web 表单,我们将会分别示范如何用get和post两种方法来处理表单的请求。首先,我们要在code目录下执行mkdir 05_webForm命令来创建用于存放这一组示例的目录。

1. get 方法

先用一个信息查询程序来演示一下如何处理使用get方法来发送请求的表单。首先,在code/05_webForm目录下执行mkdir get_form命令,并执行以下步骤:

在code/05_webForm/get_form目录下执行npm install art-template命令,将art-template安装到当前示例项目中。
在code/05_webForm/get_form目录下执行touch index.htm,创建一个模版文件,具体如下: <!DOCTYPE html>
<html lang="zh-cn">
<head>
<meta charset="UTF-8">
<title>个人信息查询</title>
</head>
<body>
<h1>个人信息查询</h1>
<form action="/query" method="GET">
<label for="message">请输入要查询的姓名:</label>
<input type="text" name="qname" />
<input type="submit" value="查询" />
</form>
<br />
{{ if name }}
<table>
<caption>{{ name }}的个人信息</caption>
<tr><td>姓名:</td><td>{{ name }}</td></tr>
<tr><td>年龄:</td><td>{{ age }}</td></tr>
<tr><td>性别:</td><td>{{ sex }}</td></tr>
<tr>
<td>爱好:</td>
<td>{{ each items }} {{ $value }} {{ /each }}</td>
</tr>
</table>
{{ else if query_error }}
<h2>没有找到相关信息!</h2>
{{ /if }}
</body>
</html>

3、在code/05_webForm/get_form目录下执行touch app.js,创建一个脚本文件,具体如下: const http=require('http')

const fs=require('fs')
const url=require('url')
const template=require('art-template')

class human {
constructor(name, age, sex, items=[])
{
this.name=name
this.age=age
this.sex=sex
this.items=items
}
}

const db=[
new human('凌杰', '37', '男', ['看书', '看电影','旅游']),
new human('蔓儿', '25', '女', ['看书', '看电影','写作']),
new human('张语', '32', '女', ['看书', '旅游','绘画'])
]

const server=http.createServer(function(req, res){
const query=url.parse(req.url, true)
let obj=null
let query_error=false
if ( query.pathname==='/' ) {
query_error=false
}
else if (query.pathname==='/query') {
for(let i=0; i < db.length; ++i) {
if (db[i].name==query.query["qname"]) {
obj=db[i]
}
}
if ( obj===null ) {
query_error=true
}
} else {
return res.end('<h1>404 页面没找到!</h1>')
}

fs.readFile('./index.htm', function(err, data){
if ( err !==null ) {
return res.end('<h1>404 没找到模版文件!</h1>')
}

let strHtml=null
if ( obj !==null ) {
strHtml=template.render(data.toString(), {
name : obj.name,
age : obj.age,
sex : obj.sex,
items: obj.items,
query_error: query_error
})
} else {
strHtml=template.render(data.toString(), {
name : false,
query_error: query_error
})
}
res.end(strHtml)
})
})

server.listen(8080, function() {
console.log('请访问http://localhost:8080/,按Ctrl+C终止服务!')
})

4、保存所有文件后,在code/05_webForm/get_form目录下执行node app.js命令,结果如下:

2. post 方法

先来演示如何处理使用post方法来发送请求的表单。首先,在code/05_webForm目录下执行mkdir post_form命令,并执行以下步骤:

  1. 在code/05_webForm/get_form目录下执行npm install art-template命令,将art-template安装到当前示例项目中。
  2. 在code/05_webForm/post_form目录下执行touch index.htm,创建一个模版文件,具体如下:
 <!DOCTYPE html>
<html lang="zh-cn">
<head>
<meta charset="UTF-8">
<title>个人信息管理</title>
</head>
<body>
<h1>个人信息管理</h1>
<table>
<caption>个人数据表</caption>
<tr><th>姓名</th><th>年龄</th><th>性别</th><th>爱好</th></tr>
{{ each db }}
<tr>
<td>{{ $value.name }} </td>
<td>{{ $value.age }} </td>
<td>{{ $value.sex }} </td>
<td>{{ each $value.items }} {{ $value }} {{ /each }}</td>
</tr>
{{ /each }}
</table>

<form action="/add" method="POST">
<table>
<caption>录入新人员</caption>
<tr><td>姓名:</td><td><input type="text" name="uname" /></td></tr>
<tr><td>年龄:</td><td><input type="text" name="age"></td></tr>
<tr><td>性别:</td><td><input type="text" name="sex"></td></tr>
<tr><td>爱好:</td><td><input type="text" name="items"></td></tr>
</table>
<input type="submit" value="添加" />
</form>
</body>
</html>

3、在code/05_webForm/post_form目录下执行touch app.js,创建一个脚本文件,具体如下:

const http=require('http')
const fs=require('fs')
const url=require('url')
const querystring=require('querystring')
const template=require('art-template')

class human {
constructor(name, age, sex, items=[])
{
this.name=name
this.age=age
this.sex=sex
this.items=items
}
}

const db=[
new human('凌杰', '37', '男', ['看书', '看电影','旅游']),
new human('蔓儿', '25', '女', ['看书', '看电影','写作']),
new human('张语', '32', '女', ['看书', '旅游','绘画'])
]

const server=http.createServer(function(req, res){
const query=url.parse(req.url, true)
if ( query.pathname==='/' ) {
fs.readFile('./index.htm', function(err, data) {
if ( err !==null ) {
return res.end('<h1>404 没找到模版文件!</h1>')
}

const strHtml=template.render(data.toString(), {
"db": db
})

res.end(strHtml)
})
}
else if ( query.pathname==='/add' ) {
req.on('data', function(chunk) {
const obj=querystring.parse(chunk.toString())
db.push(new human(
obj['uname'],
obj['age'],
obj['sex'],
obj['items'].split(','),
))
})

res.writeHead(302, {
'location': `/`
})

res.end()
} else {
return res.end('<h1>404 页面没找到!</h1>')
}
})

server.listen(8080, function(){
console.log('请访问http://localhost:8080/,按Ctrl+C终止服务!')
})

4、保存所有文件后,在code/05_webForm/post_form目录下执行node app.js命令,结果如下:

一篇基础部分,介绍了Node-RED的功能,应用场景、安装、配置及基本使用。那今天将继续深入高级用法,还有初步实现原理。这样的话,就可以对Node-RED更深入的掌握,可以更加灵活应用起来。如果默认的功能如果不能满足需求,我会介绍扩展方法,大家熟悉了就可以定制自己的流式处理程序了。我已经把它移植到了边缘计算网关当中了,充当规则引擎。你们也充分发挥想象力,尽情发挥它的威力。

  1. 节点说明:

我们挑选一些重点的节点做下说明。

  • 输入节点:flow的数据来源,入口。通过各种网络协议,手工注入,串口等方式注入数据。
  • 处理节点:某些专门用来在处理数据,比如HTML解析,数据转换,拼接等等。在节点的javascript block中完成。此类节点就是处理节点。
  • 输出节点:按照某种协议,规范,进行数据输出。比如可以是websocket, TCP, UDP,HTTP response。
  • 社交节点:用来email收发,twitter等社交工具连接。比如可以从输入节点接收数据,通过email节点,就可以发送到163邮箱当中。
  • 其它几点:比如下列表格中列出的高级节点,硬件节点等等,都是功能的封装。

节点类型

节点名称

节点说明







输入

Inject

注入节点,向flow中注入payload

Catch

捕获节点异常

Status

从其它节点发送状态消息

MQTT

MQTT节点消息输入节点

HTTP

HTTP 请求输入节点

Web socket

Web socket协议输入节点

TCP

TCP协议输入节点

UDP

UDP协议输出节点

Serial

串口数据输入节点

处理


Template

按照预制的模板,设置message属性,或者数据转换

Delay

增加延时

Function

用户直接编写javascript处理函数。

Change

可以对payload进行搜索替换

社交

email

发送邮件

twitter

发送twitter消息

存储

file

用来将信息保存到文件当中。

高级

exec

可以设置调用执行的命令,或者脚本

Raspberry Pi

rpi gpio

控制树莓派GPIO端口。输入或者输出两种

rpi mouse

控制树莓派鼠标控制。

rpi keyboard

树莓派键盘控制。

在使用的时候,上面的Node应该可以满足的大部分应用了。

2.自定义节点

Node-Red除了系统内置的节点,还提供了自定义节点的功能,是它的一大特点。具体文档参考:https://nodered.org/docs/creating-nodes/first-node.html

场景:当系统默认组件功能不足,用户需要个性化功能,外观设置的时候,就会用到。

可以把npm module封装为node.每个node包含两部分:

  • .js 定义了server端的行为。
  • .html定义了界面中编辑器的外观及帮助。

该模块可以通过npm安装到Node-Red当中。

JS文件中需要完成:

  1. 创建节点,注册节点类型。

注册节点类型

2.定义如何接收消息

定义如何接收消息

3.定义如何发送消息


定义如何发送消息

还有如何关闭节点,在节点日志处理,国际化等操作。官方文档中做了非常详细的说明。


3.在线Node及Flow

Node-Red除了提供可视化编辑器以外,自己动手创建flow。还有一个强大的在线社区,上面已经提供大量资源。包括Node与Flow。我们在使用Node-Red的过程中,首先搜索一下是不是已经有现成的Node, Flow可以用。打开:https://flows.nodered.org 就可以访问到在线资源。比如,在工业里常用的modbus协议,我们看看如何支持:

搜索Node节点


安装modbus节点

点击进去就可以访问到该node的详细信息。告诉用户如何安装,如何使用。安装完成后,重启Node-Red就可以使用了。

4.综合例子

综合例子中我们综合MQTT输入,email节点。来看复杂一些的flow创建及执行过程。在我们日常的使用过程中,大部分就是这种场景。

MQTT处理流程,Node-RED处理MQTT消息体,进行预警

  • Device通过MQTT协议上报数据。
  • 发送到MQTT Broker。
  • 在Node-Red输入节点中,进行MQTT消息订阅,当有订阅消息收到时,会接收消息并打包到message中,经过Function节点进行处理。当超过某一阀值时,会流转到email节点。通过email节点发送通知邮件。

这就是一个典型的物联网+Node-RED应用。Node-RED充当了物联网平台的规则引擎。这是放在云端。当然把它放在硬件网关中,充当边缘计算引擎也是可以的。大家可以动手亲自试一下。要是有问题,可以留言或者自私。

今天的介绍就到这里。下一期,我将带大家深入Node-RED内部工作机制,以及源代码分析。可以帮助大家透彻了解它。 其实这个软件,大家可以充分发挥想象力,它能干的事情非常多。日常工作,可以定制为各种商业化软件。

过一个阶段测试,终于找到两个 markdown 转 html 并实现代码高亮的工具,并以最简代码(几十行)实现了炫酷的展示效果。步骤很少,也很简单,零基础也应该一看就会。往下看吧......,需要安装有Node环境哦!

1 缘起

因为markdown 文档编辑器具有语法简单、文件简洁、标准化、使用广泛等特点,所以一直习惯采用 markdown 编辑器写文章,但仍有一些网站不支持 markdown 编辑器,想尝试将 markdown 转成 html 再发布,采用 Javascript 来实现,尤其想达到 CSDN 博客、简书这样的展示效果。

图1 CSDN 博客 markdown 文章及代码块高亮效果

图2 简书 markdown 文章及代码高亮效果

2 插件选择及应用模式

2.1 插件选择

2.1.1 markdown 转 html 插件

比较常用的 M2H 插件有:

  • Markdown-it 中文:https://markdown-it.docschina.org/ github:https://github.com/markdown-it/markdown-it
  • Marked 官网:https://marked.js.org/ github:https://github.com/markedjs/marked

从网上获得的 markdown-it 和 marked 的比较,主要表现在:在性能、使用简单程度、正则解析(中文支持)方面,marked 比较好;在扩展性上,markdown-it 表现则更好一些。 由于 markdown 应用比较成熟,而且一般使用也不会太复杂,所以选择性能好、中文支持好的 marked,至于 marked 的扩展性,也尝试了一下,满足常见的应用是没问题的。

2.1.2 代码块高亮模块

代码块高亮插件有:

  • heiglightjs 官网:https://highlightjs.org/ github:https://github.com/highlightjs/highlight.js
  • prismjs 官网:https://prismjs.com/ github:https://github.com/PrismJS/prism

对 heiglightjs 和 prismjs 分别做了简单测试,发现 prismjs 使用非常简单,易于上手,且代码精简,效率高,而 heiglightjs 相对比较复杂,使用起来不习惯,也许是各人喜好不同吧。所以选择使用 prismjs 插件。

2.2 插件应用模式

M2H 一般有两种应用模式:一种是前端渲染,如在 vue 中使用;另一种是在后端生成 html 文件,在浏览器展示,如在 node 中使用。 此处我选择在 node 中生产静态 html 文件的应用模式,用户可以在电脑端当作 markdown 转 node 的工具使用; 至于前端渲染,vue 页面打包时总会包含一个 chunk-vendors.js 文件,会引起页面加载变慢,有很多办法压缩该文件,但这已经超出了 M2H 的范畴了,在此不再赘述。

3 计划实现的功能

  • 读取指定 mardown 文件(test.md),生成目标 html 文件(test.html)——测试 marked 插件解析及渲染功能
  • html 文件根据代码块对应的语言进行高亮显示——测试 prismjs 插件的 language 语言包功能
  • html 文件实现显示语言名称、“复制”按钮、行号——测试 prismjs 插件的 Plugins 功能
  • 自定义代码块的显示样式修改——测试拓展功能
  • 最终实现的界面如下图:

图3 最终实现的markdown 转化及代码块渲染效果

4 编码实现

4.1 准备工作

4.1.1 创建项目

创建目录:marked-prismjs,使用 vsCode 打开该目录。

4.1.2 安装插件

在 vsCode 里的终端输入相关命令进行插件安装 1)安装 marked 插件

npm install marked --save

2)安装 prismjs

npm install prismjs --save

4.1.3 下载 prismjs 的 css 和 js 文件

访问官网下载页:https://prismjs.com/download.html

  • 按下图选择 themes,我比较喜欢“Tomorrow Night” 这个主题,您可以根据自己的喜好选择

图4 themes 选项

  • Languages 选项,默认即可,如下图,其中包含 CSS、Javascript 语言

图5 编程语言包选项

  • Plugins 选项,可以根据功能需要选择,此处选择4个选项,依次为:行号、显示语言名称、工具条、复制到剪贴板的按钮,如下图:

图6 插件选项

  • 分别点击下图的“DOWNLOAD JS”和“DOWNLOAD CSS”按钮下载 prism.js 和 prism.css 到项目根目录下。

4.1.4 编写 markdown 测试文件

  • 编写 markdown 测试文件:test.md,内容如下:

# 这是H1标签

## 下面是 javascript 代码:

```javascript let a=1; console.log(a); ```

4.2 编写实现转换功能的 js 文件

创建 m2h.js 文件,内容为:

const { marked }=require('marked') // 引入 marked 模块
const prism=require('prismjs') // 引入 prism 模块
const fs=require('fs') // 引入文件处理模块
const mdContent=fs.readFileSync('test.md', 'utf-8') // 读取 markdown 文件内容
// 下面是 marked 扩展功能,当节点(token)类型是代码(code)时,自定义渲染节点功能,及使用自定义的 renderer 函数来代替默认 renderer 函数,达到改写当前节点生成的 html 代码的样式
marked.use({
    extensions: [{
        name: 'code',   
        renderer(token) {
            if (token.type==='code') {
                let codeHtml=`<pre class="language- round"><code class="language-`+token.lang+` line-numbers">`
                codeHtml+=Prism.highlight(token.text, Prism.languages.javascript, 'javascript')
                codeHtml+=`</code></pre>`
                return codeHtml
            }
        }
    }]
})
// 定义 html 文件头部代码
var htmlContent=`
<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>Document</title>
    <link rel="stylesheet" href="prism.css">
    <script src="prism.js"></script>
</head>
<style>
  .round {
    box-shadow: 10px 10px 5px #888888;
    border-radius: 10px;
  }
</style>
<body>
`
htmlContent +=marked.parse(mdContent) // 叠加上 marked 生成的 html 代码
htmlContent+=`</body> // 叠加底部代码(补齐)
</html>`
fs.writeFileSync('test.html', htmlContent) // 将最终的 html 内容写入到文件 test.html

测试运行:

node m2h.js

每运行一次,就会重新生成新的 test.html 文件。

在 vsCode 里鼠标右键点击 test.html 文件,选择“Open with Live Server”,

图7 Open with LiveServer

会在浏览器里打开 test.html 页面,test.md 已经成功生成了 test.html,实现了代码块高亮、语言显示及拷贝按钮及行号。得到的最终结果如下图:

图8 效果展示

达到了当初计划实现的功能需求。

5 总结

以上通过简单的几十行代码就实现了如此炫酷的功能展示,体现了插件的强大功能,同时通过自定义扩展节点渲染函数,也展现了插件的灵活性和可扩展性。此次测试仅仅是个引子,后续会继续进行研究和测试,感兴趣请收藏、关注,方便共同学习、进步。