Node.js不是JS應用、而是JS運行平臺css
看到Node.js這個名字,初學者可能會誤覺得這是一個Javascript應用,事實上,Node.js採用C++語言編寫而成,是一個Javascript的運行環境。爲何採用C++語言呢?據Node.js創始人Ryan Dahl回憶,他最初但願採用Ruby來寫Node.js,可是後來發現Ruby虛擬機的性能不能知足他的要求,後來他嘗試採用V8引擎,因此選擇了C++語言。既然不是Javascript應用,爲什麼叫.js呢?由於Node.js是一個Javascript的運行環境。提到Javascript,你們首先想到的是平常使用的瀏覽器,現代瀏覽器包含了各類組件,包括渲染引擎、Javascript引擎等,其中Javascript引擎負責解釋執行網頁中的Javascript代碼。做爲Web前端最重要的語言之一,Javascript一直是前端工程師的專利。不過,Node.js是一個後端的Javascript運行環境(支持的系統包括*nux、Windows),這意味着你能夠編寫系統級或者服務器端的Javascript代碼,交給Node.js來解釋執html
Node.js採用了Google Chrome瀏覽器的V8引擎,性能很好,同時還提供了不少系統級的API,如文件操做、網絡編程等。瀏覽器端的Javascript代碼在運行時會受到各類安全性的限制,對客戶系統的操做有限。相比之下,Node.js則是一個全面的後臺運行時,爲Javascript提供了其餘語言可以實現的許多功能。前端
Node.js採用事件驅動、異步編程,爲網絡服務而設計
事件驅動這個詞並不陌生,在某些傳統語言的網絡編程中,咱們會用到回調函數,好比當socket資源達到某種狀態時,註冊的回調函數就會執行。Node.js的設計思想中以事件驅動爲核心,它提供的絕大多數API都是基於事件的、異步的風格。以Net模塊爲例,其中的net.Socket對象就有如下事件:connect、data、end、timeout、drain、error、close等,使用Node.js的開發人員須要根據本身的業務邏輯註冊相應的回調函數。這些回調函數都是異步執行的,這意味着雖然在代碼結構中,這些函數看似是依次註冊的,可是它們並不依賴於自身出現的順序,而是等待相應的事件觸發。事件驅動、異步編程的設計(感興趣的讀者能夠查閱筆者的另外一篇文章《Node.js的異步編程風格》),重要的優點在於,充分利用了系統資源,執行代碼無須阻塞等待某種操做完成,有限的資源能夠用於其餘的任務。此類設計很是適合於後端的網絡服務編程,Node.js的目標也在於此。在服務器開發中,併發的請求處理是個大問題,阻塞式的函數會致使資源浪費和時間延遲。經過事件註冊、異步函數,開發人員能夠提升資源的利用率,性能也會改善。node
從Node.js提供的支持模塊中,咱們能夠看到包括文件操做在內的許多函數都是異步執行的,這和傳統語言存在區別,並且爲了方便服務器開發,Node.js的網絡模塊特別多,包括HTTP、DNS、NET、UDP、HTTPS、TLS等,開發人員能夠在此基礎上快速構建Web服務器。git
Node是一種新型的web服務器,他的核心理念是事件驅動編程,主要的特色:
1)Node是單線程的
2)它與平臺無關
Express是一個靈活的,簡潔的Nodejs Web程序框架,Express跟Connect有着緊密的聯繫,Connect是一個Node的插件庫,他能不一樣程度上處理web請求,在version 4.0以前,Express一直綁定Connect,在4.0中,Connect以及除了static以外的全部中間件都被去掉了,以便這些中間件能夠獨立升級.
1,hello worldweb
var http = require('http') http.createServer(function(req,res){ res.writeHead(200,{'Content-Type':'html/text'}) var path = req.url switch(path){ case '': res.writeHead(200,{'Content-Type':'html/text'}) } }).listen(3000) console.log("out.print successfully...")
2,路由
路由是指向客戶端提供他所發出的請求內容機制。express
var http = require('http') http.createServer(function(req,res){ var path = req.url.replace(/\/?(?:\?.*)?$/,'').toLowerCase()//優化url,去掉查詢字符串(?a=b),可選的反斜槓(/*),並變成小寫 switch(path){ res.writeHead(200,{'Content-Type':'text/plain'}) res.end('homepage') break case'/about': res.writeHead(200,{'Content-Type':'text/plain'}) res.end('about') break default: res.writeHead(200,{'Content-Type':'text/plain'}) res.end('Not found') break } }).listen(3000) console.log("out.print successfully...")
3,靜態資源服務
用node提供靜態資源只適用於初期的小項目,對於大的項目應該會想到使用Nginx或CDN之類的代理服務器來提供靜態資源。
對於node,咱們先要打開文件,讀取其中的內容,而後將這些內容發送給瀏覽器。npm
var http = require('http') var fs = require('fs')//FS(File System)文件系統操做函數 function serveStaticFile(res,path,contentType,responseCode){//輔助函數 if(!responseCode) responseCode = 200 fs.readFile(__dirname+path,function(err,data){//__dirname是一個全局變量,注意是2個下滑線"_"表示解析正在執行腳本所在的目錄,fs.readFile是一個讀取文件的異步方法 if(err){ res.writeHead(500,{'Content-Type':'text/plain'}) res.end('500 -Internal Error') }else{ res.writeHead(responseCode,{'Content-Type':contentType}) res.end(data) } }) } http.createServer(function(req,res){ var path = req.url.replace(/\/?(?:\?.*)?$/,'').toLowerCase()//優化url,去掉查詢字符串(?a=b),可選的反斜槓(/*),並變成小寫 switch(path){ case'': serveStaticFile(res,'/public/home.html','text/html') break case'/about': serveStaticFile(res,'/public/about.html','text/html') break case'/img': serveStaticFile(res,'/public/img/logo.jpg','image/jpeg') break default: serveStaticFile(res,'/public/404.html','text/html',404) break } }).listen(3000) console.log("out.print successfully...")
Express框架
1,腳手架:大多數的項目都須要必定數量的套路化代碼,爲了節約時間避免寫重複代碼,最通用的方法就是建立一個通用的項目骨架,每個寫新的項目只須要用這個骨架,或是這個模板。express就是借鑑了RoR,提供了一個生成腳手架的工具,從而能夠開始一個新的express項目。編程
2,安裝express
npm install --save express
--save選項是爲了保證會更新package.json,由於node_modules隨時均可以生成,因此咱們不會把這個目錄保存到咱們的代碼庫中,爲了這一點咱們能夠建立一個.gitignore
node 入口文件 projectName.jsjson
引入express框架 var express = require('express') var app = express()
設置監聽端口 var app = express() app.set('port',process.env.PORT||3000)
配置404 app.use(function(req,res){ res.type('text/plain') res.status(404) res.send('404 - Not Found') })
配置500 //定製500 app.use(function(err,req,res,next){ console.error(err.stack) res.type('text/plain') res.status(500) res.send('500 - Server Error') })
監聽端口而且在控制檯print app.listen(app.get('port'),function(){ console.log('http://localhost:'+app.get('port')+';') })
在404處理器以前加上路由:
app.get('/',function(req,res){ res.type('text/plain') res.send('mikeProject') }) app.get('/about',function(req,res){ res.type('text/plain') res.send('this is about mikes project') }) //定製404頁面 app.use(function(req,res,next){ res.type('text/plain') res.status(404) res.send('404 - Not Found') })
在express官方文檔中寫道app.verp,後面的http動詞就是方法(get/post),這裏的app.get是咱們添加路由的方法,這個方法有2個參數:一個路徑和一個函數
app.verb幫咱們作了不少的工做,默認忽略了大小寫或反斜槓而且進行匹配的時候不考慮查詢字符串。
res.type:設置響應頭Conten-Type
app.use:這是express添加中間件的一種方法
執行順序:express中,路由和中間件的添加順序很是的重要,注意執行的順序。
3,視圖和佈局
在這裏有一個新的視圖佈局,也就是視圖引擎叫handlebars,爲了支持handlebars,咱們必須使用相關的包:
npm install --save express3-handlebars var express = ('exrpess');//引入express框架 var app = express(); //設置handlerbars的視圖引擎 var handlebars = require('express3-handlebars').create({defaultLayout:'main'});//設置爲默認的視圖引擎 app.engine('handlebars',handlebars.engine); app.set('view engine','handlebars')
以上代碼對express進行了配置,將其做爲默認的視圖引擎,接下來建立視圖目錄:views
將每個公用的html代碼寫在母版頁裏面,下面是母模板的內容:
main.handlebars <!doctype html> <html> <head> <title>Mike's internet</title> </head> <body> {{{body}}} </body> </html>
{{{body}}}表達式表明每一個html表達式都會被這個body取代,defaultLayout:'main'
.handlebars和.hbs互用。注意{{{body}}}是三個大括號,而不是兩個。
設置視圖,更換路由:
app.get('/',function(req,res){ res.render('home') }) app.get('/about',function(req,res){ res.render('about') }) //定製404頁面 app.use(function(req,res,next){ res.status(404) res.render('404') }) //定製500 app.use(function(err,req,res,next){ console.error(err.stack) res.status(500) res.render('500') })
注意,視圖引擎默認會返回text/html的內容和200狀態
4,視圖和靜態文件
express靠中間件處理靜態文件和視圖。
static中間件能夠將一個或多個目錄指派爲包含靜態資源的目錄,其中的資源不通過中間任何的特殊處理,直接發送到客戶端(pic,css,js...),至關於給你要發的靜態文件建立一個路由.
app.use(express.static(_dirname+'/public'))
問題:
發現引用靜態資源引用不了,不知道什麼緣由.
答案:可能與電腦的配置有關係,64位的,電腦配置好一些的就能夠打印出來.
app.use(express.static(__dirname+'/public')) <body> <header><img src="/img/logo.jpg" alt="mike"></header> {{{body}}} </body>
5,視圖中的動態內容
視圖並不僅是一種傳遞靜態的html的複雜方式,視圖真正強大之處在於它能夠包含動態的信息.
請看下面的代碼:
首先定義你要顯示的動態數據: //定義要在視圖中動態顯示的數據 var mikes=["mikes11111111", "mikes22222222222", "mikes3333333333", "mikes444444444", "mikes55555555555"] //從新修改路由 app.get('/about',function(req,res){ //res.render('about') var mikeNumber = mikes[Math.floor(Math.random()*mikes.length)] res.render('about',{mike:mikeNumber}) }) //修改視圖 <blockquote>{{{mike}}}</blockquote>
顯示內容以下:
沒刷新一次,自動更改顯示的數量:
6,Node模塊
Node模塊和npm包是兩個相互關聯但又是彼此不一樣的概念。Node模塊就像他的名字同樣,提供一個模塊化和封裝的機制,Npm包則提供了一種存儲,版本化和應用的項目,可是不限於模塊的標準範式。好比,express做爲模塊引入:
var express = require('express');
require是一個用來引入模塊的函數。
建立一個包含模塊的目錄:
Lib
./lib/fortune.js
var moneyCookies= [//數組moneyCookies是被徹底隱藏起來的,由於沒有暴露在exports上面 "Do you want money?", "I have 5$,please keep it.", "you are welcome,you can have it." ] exports.getMoney = function(){//若是你想讓一個東西在模塊外可見,那就必須把它加到exports上面 var idm = Math.floor(Math.random()*moneyCookies.length); return moneyCookies[idm] } var fortune = require('./lib/fortune.js')//引入模塊 app.get('/about',function(req,res){ //res.render('about') // var mikeNumber = mikes[Math.floor(Math.random()*mikes.length)] // res.render('about',{mike:mikeNumber}) res.render('about',{ fortune: fortune.getMoney()}); }); git add -A git commit -m "commit for updating about module."
7,頁面測試
爲了把測試真正嵌入到頁面中,是爲了作一個頁面的時候,在瀏覽器中一加載頁面就能夠立刻發現全部的錯誤。這裏須要引入一個測試的框架 mocha
npm install --save-dev mocha
--save-dev instead of --save,是告訴npm把這個包放在開發依賴中,不要放在運行依賴中,這樣在部署依賴的時候能夠減小項目的依賴項,由於mocha要作bowser中運行,因此將放到public目錄中,建立子目錄:public/vendor
另外,測試還須要一個assert/expect函數,node中有這個函數,bowser中沒有,因此:
npm install --save-dev chai
而後把chai.js放到public/vendor裏面
爲了避免影響網站的速度並且還要看到測試的結果,得準備一個路由,放在因此路由以前:
node相關問題: 新版的express中已經不包含bodyparser解決辦法:1,npm install body-parser2,var bodyParser = require('body-parser')3,把app.use(express.bodyParser())替換成app.use(bodyParser.urlencoded({ extended: false }))4,在node中寫理由的時候,因爲post過來的數據須要格式化,若是發現entity undefined,就須要將app.use(bodyParser.urlencoded({ extended: true })) 該成true 就行了