天天看點

初學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,如需轉載請自行聯系原作者