天天看点

初学Node.js之Markdown建站

 平时做项目中的Web应用都是用Asp.Net或者Java,但有时候需要快速做一些小Web的时候,感觉用Asp.Net或者Java都有点重。一开始想学学PHP,但实在不喜欢PHP的语法,所只用PHP写了两个简单的Demo之后就写不下去了。了解到最近Node.js有点热,前景也不错,而且JavaScript平时也都在用,所以决定学习下Node.js。

  准备入门,习惯做两件事,一件是在Google上搜索相关的入门教程,参考等;二是去官方网站找找Guide或者Manual。最后花了一两天浏览了些资料,这两个够了:

<a href="http://www.nodebeginner.org/index-zh-cn.html" target="_blank">Node.js入门&gt;&gt;一本全面的Node.js教程</a>

<a href="http://javascript.ruanyifeng.com/nodejs/basic.html" target="_blank">Node.js概述(by 阮一峰)</a>

  当然还有其它一些资料,比如博客、论坛之类。不过官方网站上只找到API文档,适合参考不适合入门。所有这些资料主要是浏览一遍,认识下Node.js,然后还是要在实践中学习。

  官方下载,安装都很顺利。不过虽然安装时修改了PATH,但是安装好之后命令行不能直接运行“node”,想起Windows有个老毛病——经常安装程序修改了PATH之后不会生效,所以自己又去系统环境变量设置里重新配置了一下PATH,node命令就可用了。

  “Helloworld”还是很简单的,把各种教程上的示例都试了下,然后准备写个小Web。Web Helloworld也很简单,不过要想写功能完整的Web,还是得花些精神,所以还是找框架吧。Google出来Express评价不错,也比较主流,所以决定用Express。仍然是Google+官方文档,这两个资料都不错:

<a href="http://expressjs.com/guide.html" target="_blank">官方的 Express Guide</a>

<a href="http://expressjs.jser.us/guide.html" target="_blank">中文翻译的 Express 新手指南</a>

  简单实践之后准备构思自己的第一个Node.js Web应用。因为懒得写HTML,最近又正好经常写Markdown的文档,所以准备用Markdown来建个站。Markdown解析当然想自己写,所以仍然是老办法,Google。Node.js的Markdown包找到两个,都是Github上:

<a href="https://github.com/evilstreak/markdown-js" target="_blank">https://github.com/evilstreak/markdown-js</a>

<a href="https://github.com/evilstreak/markdown-js" target="_blank">https://github.com/andris9/node-markdown</a>

  也不知道哪个好,所以随便选一个,就选了第一个。当然在建站之前还是需要实验下的,基本满足要求,能支持标准的Markdown语法,但有一些小BUG。因为这次目的是熟悉Node.js和Express,所以不纠结这个问题了,以后有空再找更好的Markdown解析包,实在找不到自己写个也不是很麻烦的事情。

  一切准备就绪,开始建站,这里记录下建站的详细过程,一步步说明,入门级的。老实说我比较懒,不想自己去准备Express的结构,所以用了传说中最好的Node.js IDE - JetBrain WebStrom。WebStorm新建项目时有“Node.js Express App”模板

<a href="http://s3.51cto.com/wyfs02/M01/22/B6/wKiom1MkEJ_iVx_iAAE84auSzqQ395.jpg" target="_blank"></a>

  然后在Node.js Express App的选项中,选择使用EJS模板引擎和LESS样式引擎。虽然很多人推荐Jade模板引擎,但是这是个新语法,而且也是我不喜欢的那种。还是EJS更贴近ASP和JSP的语法,所以选择它了。至于LESS,写起来肯定要比直接写CSS轻松得多,这个早就有经验了。

  Express应用信息和依赖包在package.json中配置。先得把markdown加进来,所以修改WebStorm生成的package.json:

1

2

3

4

5

6

7

8

9

10

11

12

13

14

<code>{</code>

<code>    </code><code>"name"</code><code>: </code><code>"application-name"</code><code>,</code>

<code>    </code><code>"version"</code><code>: </code><code>"0.0.1"</code><code>,</code>

<code>    </code><code>"private"</code><code>: </code><code>true</code><code>,</code>

<code>    </code><code>"scripts"</code><code>: {</code>

<code>        </code><code>"start"</code><code>: </code><code>"node app.js"</code>

<code>    </code><code>},</code>

<code>    </code><code>"dependencies"</code><code>: {</code>

<code>        </code><code>"express"</code><code>: </code><code>"3.5.0"</code><code>,</code>

<code>        </code><code>"ejs"</code><code>: </code><code>"*"</code><code>,</code>

<code>        </code><code>"less-middleware"</code><code>: </code><code>"*"</code><code>,</code>

<code>        </code><code>"markdown"</code><code>: </code><code>"*"</code>

<code>    </code><code>}</code>

<code>}</code>

  最后那句 "markdown": "*" 就是新加的内容。package.json是JSON格式,需要使用双引号把属性名和字符串值包起来。虽然Node.js规范推荐大家在编程时使用单引号表示字符串,但这里,单引号是不行的。

  Express App的入口是app.js,这个也在package.json中配置:<code>"start"</code><code>: </code><code>"node app.js"</code>。WebStorm生成的app.js中是通过3000端口监听服务,因为我的机器上没有安装Web服务器,80端口空着,所以我把端口号改成了80。然后,看app.js的源码,在启动HTTP服务之前配置了两行URL Route。考虑到以后的应用中URL路由配置可能会很长,再加上我有点洁癖,所以我决定尝试把URL路由放在一个独立的脚本中配置,于是在根目录下创建了一个app-routes.js,专们用来配置路由:

<code>var</code> <code>routes = require(</code><code>'./routes'</code><code>);</code>

<code>var</code> <code>user = require(</code><code>'./routes/user'</code><code>);</code>

<code>exports.route = </code><code>function</code> <code>(app) {</code>

<code>    </code><code>app.get(</code><code>'/'</code><code>, routes.index);</code>

<code>    </code><code>app.get(</code><code>'/users'</code><code>, user.list);</code>

<code>};</code>

  然后在app.js中引入app-routes,并调用导出的route方法,顺便把上面没用的require都删了:

<code>// app.get('/', routes.index);</code>

<code>// app.get('/users', user.list);</code>

<code>require(</code><code>'./app-routes'</code><code>).route(app);</code>

  运行了下,效果不错,路由没问题。剩下的就是解析Markdown的问题了。Markdown文件我准备专门建个目录来保存,所有文件都用其它编辑器(比如Sublime Text)写好放在里面,然后通过在url中直接输入“/文件名”的方式来打开解析后的HTML页面。

  然后在routes目录下添加了一个markdown.js,这个脚本根据URL参数,在md目录下选择对应的文件,解析后,通过views目录下新添加的md.ejs模板显示成HTML,所以目录结构就像这个

<a href="http://s3.51cto.com/wyfs02/M01/22/B7/wKioL1MkEHiBxrYwAADqw3rRoqk097.jpg" target="_blank"></a>

  接着添加路由,将访问请求交给markdown.js来处理

<code>var</code> <code>markdown = require(</code><code>'./routes/markdown'</code><code>);</code>

<code>    </code><code>app.get(</code><code>'/(:md)?'</code><code>, markdown.show);</code>

  还有md.ejs模板,需要在&lt;body&gt;标准中显示由Markdown文件转换而来的HTML,所以使用&lt;%- %&gt;标记。

<code>&lt;!DOCTYPE html&gt;</code>

<code>&lt;</code><code>html</code><code>&gt;</code>

<code>&lt;</code><code>head</code><code>&gt;</code>

<code>    </code><code>&lt;</code><code>meta</code> <code>http-equiv</code><code>=</code><code>"content-type"</code> <code>content</code><code>=</code><code>"text/html; charset=utf-8"</code><code>/&gt;</code>

<code>    </code><code>&lt;</code><code>title</code><code>&gt;&lt;%= title %&gt;&lt;/</code><code>title</code><code>&gt;</code>

<code>    </code><code>&lt;</code><code>link</code> <code>rel</code><code>=</code><code>'stylesheet'</code> <code>href</code><code>=</code><code>'/stylesheets/style.css'</code><code>/&gt;</code>

<code>&lt;/</code><code>head</code><code>&gt;</code>

<code>&lt;</code><code>body</code><code>&gt;</code>

<code>&lt;%- content %&gt;</code>

<code>&lt;/</code><code>body</code><code>&gt;</code>

<code>&lt;/</code><code>html</code><code>&gt;</code>

  markdown.js中的处理过程是获取路由参数,再根据参数在md目录下去找对应的文件,如果没找到直接返回404错误。如果找到了就将这个文件的内容读出来,解析成HTML,传递给md.ejs输出到浏览器。同时考虑如果URL不带参数,则说明是访问首页,markdown.js应该直接去解析md/index.md。

15

16

17

18

19

20

21

22

23

24

25

26

27

28

<code>var</code> <code>fs = require(</code><code>"fs"</code><code>);</code>

<code>var</code> <code>path = require(</code><code>"path"</code><code>);</code>

<code>var</code> <code>markdown = require(</code><code>"markdown"</code><code>).markdown;</code>

<code>exports.show = </code><code>function</code> <code>(req, res) {</code>

<code>    </code><code>// 获取参数并将参数转换成</code>

<code>    </code><code>var</code> <code>mdName = req.params.md || </code><code>"index"</code><code>;</code>

<code>    </code><code>if</code> <code>(!mdName.match(/\.md$/i)) {</code>

<code>        </code><code>mdName += </code><code>".md"</code><code>;</code>

<code>    </code><code>// 根据当前目录和相对目录找到参数对应的md文件</code>

<code>    </code><code>var</code> <code>filename = path.resolve(path.join(__dirname, </code><code>"../md"</code><code>, mdName));</code>

<code>    </code><code>// render函数定义符合readFile和callback标准</code>

<code>    </code><code>// 如果md文件读取错误或者没有内容,向浏览器返回404</code>

<code>    </code><code>// 有内容则调用md.ejs渲染HTML页面</code>

<code>    </code><code>var</code> <code>render = </code><code>function</code> <code>(err, md) {</code>

<code>        </code><code>if</code> <code>(err || !md) {</code>

<code>            </code><code>res.status(404).send(</code><code>"Error"</code><code>);</code>

<code>            </code><code>return</code><code>;</code>

<code>        </code><code>}</code>

<code>        </code><code>var</code> <code>html = markdown.toHTML(md);</code>

<code>        </code><code>res.render(</code><code>"md"</code><code>, {</code>

<code>            </code><code>title: </code><code>"Markdown Content"</code><code>,</code>

<code>            </code><code>content: html</code>

<code>        </code><code>});</code>

<code>    </code><code>};</code>

<code>    </code><code>// 从md文件读取内容,并将读到的内容交能render函数处理</code>

<code>    </code><code>fs.readFile(filename, { encoding: </code><code>"utf8"</code> <code>}, render);</code>

  完工,使用node app.js启动服务,再在浏览器中访问,一切正常,只是——页面太丑了,因为没加样式。修改public/stylesheets/style.less,加入样式

29

30

31

32

33

34

35

36

37

38

39

40

41

42

43

44

45

46

47

48

49

50

51

52

53

54

55

56

57

58

59

60

61

62

63

64

65

66

67

68

69

70

71

72

73

74

75

76

77

78

79

<code>body {</code>

<code>    </code><code>margin</code><code>: </code><code>24px</code><code>;</code>

<code>    </code><code>font</code><code>: </code><code>14px</code> <code>"微软雅黑"</code><code>, </code><code>"Lucida Grande"</code><code>, </code><code>Helvetica</code><code>, </code><code>Arial</code><code>, </code><code>sans-serif</code><code>;</code>

<code>.h {</code>

<code>    </code><code>font-weight</code><code>: </code><code>normal</code><code>;</code>

<code>h</code><code>1</code> <code>{</code>

<code>    </code><code>.h();</code>

<code>    </code><code>font-size</code><code>: </code><code>32px</code><code>;</code>

<code>h</code><code>2</code> <code>{</code>

<code>    </code><code>font-size</code><code>: </code><code>24px</code><code>;</code>

<code>h</code><code>3</code> <code>{</code>

<code>    </code><code>font-size</code><code>: </code><code>18px</code><code>;</code>

<code>.quote {</code>

<code>    </code><code>background-color</code><code>: </code><code>#e8e8e8</code><code>;</code>

<code>    </code><code>border-left</code><code>: </code><code>5px</code> <code>solid</code> <code>#cacaca</code><code>;</code>

<code>    </code><code>padding</code><code>: </code><code>3px</code> <code>0</code> <code>3px</code> <code>24px</code><code>;</code>

<code>    </code><code>border-radius: </code><code>3px</code><code>;</code>

<code>    </code><code>margin</code><code>: </code><code>0</code><code>;</code>

<code>    </code><code>p {</code>

<code>        </code><code>margin</code><code>: </code><code>1px</code> <code>0</code><code>;</code>

<code>code</code> <code>{</code>

<code>    </code><code>.quote();</code>

<code>    </code><code>font-family</code><code>: Consolas, Monaco, </code><code>"Lucida Console"</code><code>, </code><code>monospace</code><code>;</code>

<code>    </code><code>padding</code><code>: </code><code>0</code> <code>5px</code><code>;</code>

<code>    </code><code>margin-left</code><code>: </code><code>2px</code><code>;</code>

<code>    </code><code>margin-right</code><code>: </code><code>2px</code><code>;</code>

<code>    </code><code>border</code><code>: </code><code>1px</code> <code>solid</code> <code>#cacaca</code><code>;</code>

<code>pre</code> <code>{</code>

<code>    </code><code>border-left-width</code><code>: </code><code>32px</code><code>;</code>

<code>    </code><code>padding-left</code><code>: </code><code>12px</code><code>;</code>

<code>    </code><code>code</code> <code>{</code>

<code>        </code><code>border</code><code>: </code><code>0</code><code>;</code>

<code>        </code><code>padding</code><code>: </code><code>0</code><code>;</code>

<code>        </code><code>margin</code><code>: </code><code>0</code><code>;</code>

<code>blockquote {</code>

<code>        </code><code>background-color</code><code>: </code><code>#f8f8f8</code><code>;</code>

<code>a {</code>

<code>    </code><code>color</code><code>: </code><code>#666</code><code>;</code>

<code>    </code><code>text-decoration</code><code>: </code><code>underline</code><code>;</code>

<code>    </code><code>&amp;:visited {</code>

<code>        </code><code>color</code><code>: </code><code>#999</code><code>;</code>

<code>    </code><code>&amp;:hover {</code>

<code>        </code><code>color</code><code>: </code><code>#000</code><code>;</code>

<code>ul, ol {</code>

<code>    </code><code>padding</code><code>: </code><code>0</code><code>;</code>

<code>    </code><code>li {</code>

<code>        </code><code>margin-left</code><code>: </code><code>1.2em</code><code>;</code>

<code>ul li {</code>

<code>    </code><code>list-style-type</code><code>: </code><code>square</code><code>;</code>

<code>hr {</code>

<code>    </code><code>height</code><code>: </code><code>1px</code><code>;</code>

<code>    </code><code>border</code><code>: </code><code>0</code><code>;</code>

<code>    </code><code>background-color</code><code>: </code><code>#999</code><code>;</code>

  这回才是真正的完工了,看看效果:

<a href="http://s3.51cto.com/wyfs02/M00/22/B7/wKioL1MkEHnSGqJvAAL_f5pnYvk875.jpg" target="_blank"></a>

本文转自边城__ 51CTO博客,原文链接:http://blog.51cto.com/jamesfancy/1377256,如需转载请自行联系原作者