express渲染markdown
前言
好久没写博客了,哈哈,主要这一年将要忙于考研,所以更新的文章会比较少的。
我想可以写一个基于nodejs的express的框架系统,来渲染markdown写的文章,来记录自己对数据结构这门课程的学习,今天来尝试地做一下。
express的基本环境与Jade模板引擎
老朋友,还记得express吗?不记得没关系,来根据官网一步一步走。
安装Express
首先,你一定是由nodejs才点开这篇文章的对吧,打开你的编译器,然后:
通过 npm init
命令为你的应用创建一个 package.json
文件。 欲了解 package.json
是如何起作用的,请参考 Specifics of npm’s package.json handling.
$ npm init
此命令将要求你输入几个参数,例如此应用的名称和版本。 你可以直接按“回车”键接受大部分默认设置即可,下面这个除外:
entry point: (index.js)
键入 app.js
或者你所希望的名称,这是当前应用的入口文件。如果你希望采用默认的 index.js
文件名,只需按“回车”键即可。
接下来在 myapp
目录下安装 Express 并将其保存到依赖列表中。如下:
$ npm install express --save
如果只是临时安装 Express,不想将它添加到依赖列表中,可执行如下命令:
$ npm install express --no-save
Express Generator
这里我就不全自己写了,这里利用一个Express生成器,默认来生成Jade模板
通过应用生成器工具 express-generator
可以快速创建一个应用的骨架。
你可以通过 npx
(包含在 Node.js 8.2.0 及更高版本中)命令来运行 Express 应用程序生成器。
$ npx express-generator
然后安装所有依赖包:
npm install
启动应用
npm start
然后访问本机3000端口,就可以看到效果了 !
生成器自动生成的目录结构:
app.js
应用的初始化文件,包括引入应用程序的基础依赖项、设置视图即view的引擎目录以及模板、设置静态资源路径、配置通用的中间件、引入路由和一些错误处理中间件等。package.json
应用的配置文件,文件内包含程序的基础信息、启动脚本和依赖包等。bin/www
应用的启动文件,文件内包含引用要启动的应用、设置应用监听的端口和启动http服务等。public/**
应用的静态资源文件目录,该目录下的文件资源不需要经过文件映射就可以直接访问。routes/**
应用的路由文件,这些路由文件中设置的接口最终会以指定的HTTP请求方式暴露给用户,并在用户请求之后将结果返回。views
应用的视图文件,在app.js
中设置好视图引擎和模板之后,该目录即为应用视图的根目录,然后路由文件就会根据app.js
中的设置加载并渲染该目录下的视图文件。
Jade的基本语法
Express生成器默认生成的是Jade模板,jade是超高性能的node JavaScript模板引擎,有着非常强大的API和大量杰出的特性。它主要针对node的服务端。
下面的内容主要搬自这个网页——https://blog.youkuaiyun.com/no10086/article/details/89646189
随着前端业务的不断发展,页面交互逻辑的不断提高,让数据和界面实现分离渐渐被提了出来。JavaScript的MVC思想也流行了起来,在这种背景下,基于node.js的模板引擎也随之出现。
什么是模板引擎?
它用于解析动态数据和静态页面所生成的视图文件,将原本静态的数据变为动态,快速地实现页面交互;
目前使用较广的模板引擎有以下几种:Jade / Pug、EJS、Handlebars。
jade模板引擎
jade模板引擎相较于原来的html会显得更加简洁,它将标签原本的”<>”符号去掉,用括号代替,层级使用tab缩进来分,并且也支持js语法;
下面来简单记录一下Jade的基本语法:
1、jade对很多html操作进行了简化,如下:
html
head
style
body
div(class="content")
h1 正文
了解过html语句的,从结构上一定会发现,它将原本的双标签省略了,尖括号也不见了,而层级的划分则由缩进实现,默认的,jade会把几乎所有缩进后的字母变为标签(行内元素)。以下代码会变为:
<html>
<head>
<style></style>
</head>
<body>
<div class="content">
<h1>正文</h1>
</div>
</body>
</html>
也将用div(class=”content”)代表,简化了代码的书写;
2、“|”符号的作用
有时想让标签成为文字,那么“|”成为了绝好的工具:
div(class="content",id="content")
| center
可以看到,他将center作为文字原封不动的写入了html中,而不是作为一个标签渲染。
用它来转换js语句:
script
| var cli = document.getElementById("content");
| cli.onclick=function(){
| alert("aaa");
| }
他将会变为:
<script>
var cli = document.getElementById("content");
cli.onclick=function(){
alert("aaa");
}
</script>
3、识别js语句:
可以通过 script. 来识别js语法:
script.
var cli = document.getElementById("content");
cli.onclick=function(){
alert("aaa");
}
他也会变为:
<script>
var cli = document.getElementById("content");
cli.onclick=function(){
alert("aaa");
}
</script>
可以看到,相比于用 | 使用script. 更加方便快捷。
4、引入其他js文件:
想在jade的js标签中引入其他js文件?没错,它也支持。前提请确保他们在同一文件夹下:
script
include click.js
5、表达式
“-”允许直接写js语法,在变量调用时,通过 #{a+b} 或 div=a+b 进行:
html
head
body
-var a=10
-var b=20
div a+b为:#{a+b}
div=a+b
会得到:
<html>
<head></head>
<body>
<div>a+b为:30</div>
<div>30</div>
</body>
</html>
6、for循环:
“-“也可以用于循环语句的使用
html
head
body
-var arr=0;
-for(var i=5;i>arr;i--)
div=i
div the number = #{i}
得到:
<html>
<head></head>
<body>
<div>5</div>
<div>4</div>
<div>3</div>
<div>2</div>
<div>1</div>
<div>the number = 0</div>
</body>
</html>
除了-以外,还可以使用=或者!=来直接嵌入js代码:
使用 = ,代码中的特殊字符将会被转义:
p = 'This code is <escaped>!'
<p>This code is <escaped>!</p>
第三种方法是使用 != ,代码中的特殊字符不会被转义:
p = 'This code is <escaped>!'
<p>This code is <escaped>!</p>
7、case ,when
类似于switch case语句:
html
head
body
- var test = "汉子"
-var none = "无"
div The word is #{test}
case test
when "a": div the when is a
when "b": div the second is b
when "汉子": div the Third is 汉子
default: The words is #{none}
<html>
<head></head>
<body>
<div>The word is 汉子。</div>
<div>the Third is 汉子</div>
</body>
</html>
类似于switch case,只执行when中与case对应的代码块,在匹配后面用 : 来作为要执行的代码,后面跟上标签和对应语句
8、if else条件判断
html
head
body
-for(var i=12;i>0;i--)
-if(i%2==0)
div(style={background:'#eee',width:'100%',height:'20px',color: '#333'}) 偶数
-else
div(style={background:'#333',width:'100%',height:'20px',color: '#eee'}) 奇数
得到:
<html>
<head></head>
<body>
<div style="background:#eee;width:100%;height:20px;color:#333"> 偶数</div>
<div style="background:#333;width:100%;height:20px;color:#eee"> 奇数</div>
<div style="background:#eee;width:100%;height:20px;color:#333"> 偶数</div>
<div style="background:#333;width:100%;height:20px;color:#eee"> 奇数</div>
<div style="background:#eee;width:100%;height:20px;color:#333"> 偶数</div>
<div style="background:#333;width:100%;height:20px;color:#eee"> 奇数</div>
<div style="background:#eee;width:100%;height:20px;color:#333"> 偶数</div>
<div style="background:#333;width:100%;height:20px;color:#eee"> 奇数</div>
<div style="background:#eee;width:100%;height:20px;color:#333"> 偶数</div>
<div style="background:#333;width:100%;height:20px;color:#eee"> 奇数</div>
<div style="background:#eee;width:100%;height:20px;color:#333"> 偶数</div>
<div style="background:#333;width:100%;height:20px;color:#eee"> 奇数</div>
</body>
</html>
9、style的写法:
在对style样式进行修改时,与script相同,也可使用 . 对其进行编辑,之后对不同的标签在其后面加{},大括号里写css语句1,并使用 ; 隔开
html
head
meta(charset="utf-8")
title jade测试页面
style.
body{margin:0;padding:0}
div
{width: 100px;height: 100px;background: #ccc;text-align: center;line-height: 100px;margin: 10px auto}
div.last{clear:left}
body
-var a=0;
while a<12
if a%2==0 && a!=0
div.last=a++
else
div=a++
得到:
<html>
<head>
<meta charset="utf-8"/>
<title>jade测试页面</title>
<style>
body{margin:0;padding:0}
div
{width: 100px;height: 100px;background: #ccc;text-align: center;line-height: 100px;margin: 10px auto}
div.last{clear:left}
</style>
</head>
<body>
<div>0</div>
<div>1</div>
<div class="last">2</div>
<div>3</div>
<div class="last">4</div>
<div>5</div>
<div class="last">6</div>
<div>7</div>
<div class="last">8</div>
<div>9</div>
<div class="last">10</div>
<div>11</div>
</body>
</html>
10、Mixin
Mixin的作用是对模块的复用,当重复代码有不同内容时,起作用就来了:
- var num = 0;
mixin node
div The number in mixin node is #{num++}
+node()
+node()
+node()
div At last, the number in mixin node is #{num++}
得到:
<div>The number in mixin node is 0</div>
<div>The number in mixin node is 1</div>
<div>The number in mixin node is 2</div>
<div>At last, the number in mixin node is 3</div>
11、each可以对数组进行遍历
Jade 使用 each 对数组和对象遍历,用法与 JavaScript 大同小异。
// 遍历数组
ul
each val, index in ['zero', 'one', 'two']
li= index + ': ' + val
// 遍历对象
ul
each val, index in {1:'one',2:'two',3:'three'}
li= index + ': ' + val
生成的 HTML:
<!-- 遍历数组-->
<ul>
<li>0: zero</li>
<li>1: one</li>
<li>2: two</li>
</ul>
<!-- 遍历对象-->
<ul>
<li>1: one</li>
<li>2: two</li>
<li>3: three</li>
</ul>
12、继承
Jade 中使用 extends 来继承代码片段,与 include 本本分分地引用代码段不同,继承可以修改代码片段。
首先,在 layout 页面使用 block 标识符,设置一个可修改的代码片段,紧跟 block 标识符之后的是该代码片段的名字:
//- layout.jade
doctype html
html
head
block title
title Default title
body
block content
然后,在 index 页面继承 layout ,并可以根据代码片段的名字修改相关代码:
//- index.jade
extends ./layout.jade
block title
title Article Title
block content
h1 My Article
<!doctype html>
<html>
<head>
<title>Article Title</title>
</head>
<body>
<h1>My Article</h1>
</body>
</html>
上述这种继承方式,会抹除原来代码片段的部分,如果想要追加代码片段,可以使用 append 和 prepend 指令。 append 用于在原有代码片段之后追加, prepend 用于在原有代码片段之前追加。一个初始页面:
//- layout.jade
html
head
title Layout
body
block content
p Hello
使用 append :
extend layout
block append content
p World
生成的 HTML:
<html>
<head>
<script src="/vendor/jquery.js"></script>
<script src="/vendor/caustic.js"></script>
</head>
<body>
<p>Hello</p>
<p>World</p>
</body>
</html>
以上就是jade的基本语法了。
express渲染markdown以及文件读取
我的基本思路是这样的,将笔记写成markdown的形式,然后统一放在一个目录下,然后由node来读取该目录下的文件,再渲染到web页面上。
渲染markdown
先来尝试用express渲染markdown
我这里使用的是一个npm包,叫做marked ,这里有它的文档
首先使用命令来简单安装这个包(我这里用g来全局安装)
npm install -g marked
然后只要导入包再使用即可,运行Jade框架,然后在index.js中修改内容:
var express = require('express');
var router = express.Router();
var marked = require("marked");
/* GET home page. */
router.get('/', function(req, res, next) {
var html = marked('# Marked in Node.js\n\nRendered by **marked**.');
res.send(html);
//res.render('index', { title: 'Express' });
});
module.exports = router;
这样就成功地将markdown的字符串渲染到了网页上
结合Jade的话:
var express = require('express');
var router = express.Router();
var marked = require("marked");
/* GET home page. */
router.get('/', function(req, res, next) {
var note = '# Marked in Node.js\n\nRendered by **marked**.';
//渲染jade视图文件,传入相应参数
res.render('template', { note:note,
marked: marked });
});
module.exports = router;
对应的template.jade的内容是:
extends layout
block content
//- ‘!=’意为着后面的内容作为js代码执行,且特殊字符不转义
div!= marked(note)
这样就实现了markdown的渲染。
文件流
下面是node.js的文件流问题,最后要自动遍历某目录下的所有markdown文件
先尝试得到一个目录下的所有文件,这里你会需要nodejs的文档
我这里在public目录下创建一个docs目录,然后新建了一个test.md
检索代码index.js如下:
var express = require('express');
var router = express.Router();
var marked = require("marked");
var path = require('path');
var fs = require('fs');
//得到绝对路径
var dirTarget=path.resolve('./public/docs');
//读取dirTarget下的markdown文件,将文件名保存在数组下
function readMarkdownLst(dir){
var markdownList=[];
//同步读目录
var files = fs.readdirSync(dir);
files.forEach((item, index) => {
var fullPath = path.join(dir, item);
//同步得到其状态
const stat = fs.statSync(fullPath);
//若不是目录则加入数组
if (!stat.isDirectory()) {
markdownList.push(item);
}
});
return markdownList;
}
/* GET home page. */
router.get('/', function(req, res, next) {
// var note = '# Marked in Node.js\n\nRendered by **marked**.';
// //渲染jade视图文件,传入相应参数
// res.render('template', { note:note,
// marked: marked });
var content="";
readMarkdownLst(dirTarget).forEach(function(e){
content+=e;
})
res.send(content);
});
module.exports = router;
不出意外,运行成功后你的主机面就是你docs目录下的文件名。
下面,将检索到的文件名制成的数组利用jade循环制作成无序列表超链接,链接跳转到另一个js文件——content.js
修改后的index.js中的路由处理:
router.get('/', function(req, res, next) {
//将readMarkdownLst方法读取的文件数组返回出来,传给jade模板
res.render('index',{titleList:readMarkdownLst(dirTarget)});
});
在index.jade中:
extends layout
block content
ul
-for(var i=1;i<=titleList.length;i++)
li
a(href="content")= titleList[i-1]
这里要再写一个content.js:
var express = require('express');
var router = express.Router();
/* GET users listing. */
router.get('/', function(req, res, next) {
res.send('恭喜!');
});
module.exports = router;
而在程序入口文件app.js中要做相应的添加:
var contentRouter = require('./routes/content');
app.use('/content', contentRouter);
好了,下面可以多做几个docs下的文件,来看看动态效果是否成功:
文件流非常成功,那么后面的内容就是我们要将对应的选中的markdown文章的内容渲染在content这个页面下面。
实现选中文章顺利阅读功能
我们刚才实现了根据目录下的md文件自动生成无序列表超链接,那么用户选择连接时,我们要将选择的是哪一个文章发送给我们的content.js,故在index.jade中,超链接里写好get请求:
extends layout
block content
ul
-for(var i=1;i<=titleList.length;i++)
li
//- 超链接中用get方式请求,将文章名作为参数
a(href="content/?"+"article="+titleList[i-1])= titleList[i-1]
对应的content.js文件里进行接收参数,同时进行markdown文件的读写,还有markdown文件渲染到页面
var express = require('express');
var router = express.Router();
var marked = require("marked");
var path = require('path');
var fs = require('fs');
//得到绝对路径
var dirTarget=path.resolve('./public/docs');
//读取dirTarget下的markdown文件内容
function readMarkdown(dir,articleName,encode){
var articleContent;
//同步读目录
var dir=dir+"/"+articleName;
articleContent=fs.readFileSync(dir,encode);
return articleContent;
}
router.get('/', function(req, res, next) {
//得到前端发来的选中文章名
var article=req.query.article;
var articleContent=readMarkdown(dirTarget,article,"utf-8");
//渲染jade视图文件,传入相应参数
res.render('content', { title:"正在浏览"+article,note:articleContent,
marked: marked });
});
module.exports = router;
content.jade的内容如下:
extends layout
block content
//- ‘!=’意为着后面的内容作为js代码执行,且特殊字符不转义
div!= marked(note)
这样,我们就可以成功将docs目录下的md文件渲染到html页面上了!
基本的简单功能就实现了,后面就是按照自己的需求来增添一些样式和功能了!
大功告成!
我放在了公网上,有兴趣的可以访问看看——datastructure.top
商业转载 请联系作者获得授权,非商业转载 请标明出处,谢谢