一、Nod.js基本概念
1.為什么學Node
-企業(yè)需求:具有服務端開發(fā)需求
-目的:打開服務端黑匣子
-網(wǎng)站開發(fā)能力:服務端,前端,運維部署
2.Node是什么?
-Node.js不是語言,不是庫,也不是框架
-是JS運行時環(huán)境(是一個平臺),可以解析和執(zhí)行JS代碼
3. Node.js 中的 JavaScript
- 沒有 BOM、DOM
- EcmaScript 基本的 JavaScript 語言部分
- 在 Node 中為 JavaScript 提供了一些服務器級別的 API
- 文件操作的能力
- http 服務的能力
- 網(wǎng)絡服務構建的能力
- EcmaScript
- 變量
- 方法
- 數(shù)據(jù)類型
- 內(nèi)置對象
- Array
- Object
- Date
- Math
二、基本Node操作
1.使用Node執(zhí)行js腳本文件
注意:node文件名不要用node.js命名,不要使用中文
例1
var foo = 'hello nodejs'
console.log(foo)
在終端或者cmd里中node + 文件名便可執(zhí)行文件

(我這個文件的名字叫做Hello_wrold.js)
可以看出,node和js基本語法一樣
例2
console.log(window)
console.log(document)
我們在node中執(zhí)行js的一些操作,結果如下:

沒有任何輸出,但是我們在html中打開瀏覽器界面,引用這個js文件在控制臺可以看見

注意:Node和瀏覽器中的 JavaScript 不一樣
2.Node的讀文件基本操作
注意:
1.瀏覽器中的 JavaScript 是沒有文件操作的能力的,但是 Node 中的 JavaScript 具有文件操作的能力
2.在 Node 中如果想要進行文件操作,就必須引入 fs 這個核心模塊; 在 fs 這個核心模塊中,就提供了所有的文件操作相關的 API
3.fs 是 file-system 的簡寫,就是文件系統(tǒng)的意思;例如:fs.readFile 就是用來讀取文件的
a.使用 require 方法加載 fs 核心模塊
var fs = require('fs')
b.讀取文件
注意:
1.第一個參數(shù)就是要讀取的文件路徑
2.第二個參數(shù)是一個回調(diào)函數(shù)
3.回調(diào)函數(shù)又有兩個參數(shù):error和data;如果讀取路徑成功,data返回數(shù)據(jù),error返回null;反之,如果讀取路徑失敗,data返回null,error返回錯誤對象
fs.readFile('./data/hello.txt', function (error, data) {
console.log(data)
})
(我創(chuàng)建了一個名為data的文件夾,里面保存了一個名為hello.txt的文件,內(nèi)容為:hello node)
結果如下:

注意:
<Buffer 68 65 6c 6c 6f 20 6e 6f 64 65 6a 73 0d 0a>
文件中存儲的其實都是二進制數(shù)據(jù) 0 1; 這里為什么看到的不是 0 和 1 呢?原因是二進制轉為 16 進制了;但是無論是二進制01還是16進制,人類都不認識;所以我們可以通過 toString 方法把其轉為我們能認識的字符
var fs = require('fs')
fs.readFile('./data/hello.txt', function (error, data) {
//console.log(data)
console.log(data.toString()) //hello node
})
在這里存在一個小問題:
如果我們將路徑寫錯了,看看上面寫的代碼會出現(xiàn)什么問題。
var fs = require('fs')
fs.readFile('./data/a.txt', function (error, data) {
console.log(data)
})

因為data里面是null,所以結果為undefined
在這里就可以通過判斷 error 來確認是否有錯誤發(fā)生
var fs = require('fs')
fs.readFile('./data/a.txt', function (error, data) {
//console.log(data)
//console.log(data.toString())
if (error) {
console.log('讀取文件失敗了')
} else {
console.log(data.toString())
}
})
如果讀取失敗,結果如下圖所示:

3.Node的寫文件基本操作
a.寫文件的第一步也是使用require加載fs核心模塊
var fs = require('fs')
b.寫入文件
fs.writeFile('./data/你好.md', '大家好,給大家介紹一下,我是Node.js', function (error) {
console.log('文件寫入成功')
})
結果會在控制臺輸出:文件寫入成功
同時在data文件下新建你好.md文件,并把內(nèi)容寫入

注意:writeFile有三個參數(shù)
1.第一個參數(shù):文件路徑2.第二個參數(shù):文件內(nèi)容3.第三個參數(shù):回調(diào)函數(shù)?;卣{(diào)函數(shù)在這里只有一個參數(shù)error;若文件寫入成功,error 是 null;若文件寫入失敗,error 就是錯誤對象注意:
在這里存在一個小問題:
如果我們將路徑寫錯了,看看上面寫的代碼會出現(xiàn)什么問題。
fs.writeFile('./d/你好.md', '大家好,給大家介紹一下,我是Node.js', function (error) {
console.log('文件寫入成功')
})
我把data文件夾改成了d文件夾
結果如下:

只是在控制臺輸出語句,但是在data文件夾里沒有新建文件,也沒有新建d文件夾。
因此,我們可以在這里加上 if 語句進行判斷
fs.writeFile('./data/你好.md', '大家好,給大家介紹一下,我是Node.js', function (error) {
if (error) {
console.log('寫入失敗')
} else {
console.log('寫入成功了')
}
})
三、Node之http服務
1.簡單的http服務
我們可以使用 Node 非常輕松的構建一個 Web 服務器;在 Node 中專門提供了一個核心模塊:http;http 這個模塊的職責就是幫你創(chuàng)建編寫服務器的
a.加載 http 核心模塊
var http = require('http')
b.使用 http.createServer() 方法創(chuàng)建一個 Web 服務器;返回一個 Server 實例
var server = http.createServer()
c.注冊 request 請求事件
當客戶端請求過來,就會自動觸發(fā)服務器的 request 請求事件,然后執(zhí)行第二個參數(shù):回調(diào)處理函數(shù)
server.on('request', function () {
console.log('收到客戶端的請求了')
})
d.綁定端口號,啟動服務器
server.listen(3000, function () {
console.log('服務器啟動成功了,可以通過 http://127.0.0.1:3000/ 來進行訪問')
})
運行該JS文件后,就可以打開瀏覽器
端口號范圍:0~65536
2.發(fā)送響應
request 請求事件處理函數(shù),需要接收兩個參數(shù):
- Request 請求對象
- 請求對象可以用來獲取客戶端的一些請求信息,例如請求路徑
- Response 響應對象
- 響應對象可以用來給客戶端發(fā)送響應消息
- response 對象有一個方法:write 可以用來給客戶端發(fā)送響應數(shù)據(jù)
- write 可以使用多次,但是最后一定要使用 end 來結束響應,否則客戶端會一直等待
var http = require('http')
var server = http.createServer()
server.on('request', function (request, response) {
console.log('收到客戶端的請求了,請求路徑是:' + request.url)
response.write('hello')
response.write(' nodejs')
response.end()
})
server.listen(3000, function () {
console.log('服務器啟動成功了,可以通過 http://127.0.0.1:3000/ 來進行訪問')
})
結果在瀏覽器輸出:

3.練習:根據(jù)不同的請求路徑返回不同數(shù)據(jù)
注意:
上面使用的 res.write方式比較麻煩,推薦使用更簡單的方式,直接 end 的同時發(fā)送響應數(shù)據(jù)
// res.write('hello')
// res.write(' world')
// res.end()
res.end('hello nodejs')
練習分析:根據(jù)不同的請求路徑發(fā)送不同的響應結果
- 1. 獲取請求路徑
+req.url 獲取到的是端口號之后的那一部分路徑
·+ url:同一資源定位符,一個url其實是要定一個資源
+也就是說所有的 url 都是以 / 開頭的
- 2. 判斷路徑處理響應
+響應內(nèi)容只能是二進制數(shù)據(jù)或者字符串、數(shù)字、對象、數(shù)組、布爾值
var http = require('http')
var server = http.createServer()
server.on('request', function (req, res) {
console.log('收到請求了,請求路徑是:' + req.url)
console.log('請求我的客戶端的地址是:', req.socket.remoteAddress, req.socket.remotePort)
var url = req.url
if (url === '/') {
res.end('index page')
} else if (url === '/login') {
res.end('login page')
} else {
res.end('404 Not Found.')
}
})
server.listen(3000, function () {
console.log('服務器啟動成功,可以訪問了。。。')
})
4.端口號
ip 地址定位計算機
端口號定位具體的應用程序
所有需要聯(lián)網(wǎng)通信的應用程序都會占用一個端口號
在cmd中輸入
ipconfig
可以查看本機的端口號
在同一局域網(wǎng)的計算機可以訪問自己的計算機
req.socket.remoteAddress:當前請求我的計算機客戶端的地址(ip地址和端口號)
req.socket.remotePort:當前請求我的計算機客戶端的端口號
根據(jù)下列代碼可以看見訪問自己計算機的ip地址和端口號
var http = require('http')
var server = http.createServer()
server.on('request', function (req, res) {
console.log('收到請求了,請求路徑是:' + req.url)
console.log('請求我的客戶端的地址是:', req.socket.remoteAddress, req.socket.remotePort)
res.end('hello nodejs')
})
server.listen(5000, function () {
console.log('服務器啟動成功,可以訪問了。。。')
})
5.Content-Type
- 服務器最好把每次響應的數(shù)據(jù)是什么內(nèi)容類型都告訴客戶端,而且要正確的告訴
- 不同的資源對應的 Content-Type 是不一樣,具體參照:http://tool.oschina.net/commons
- 對于文本類型的數(shù)據(jù),最好都加上編碼,目的是為了防止中文解析亂碼問題
a.在瀏覽器中輸出漢字
在之前的學習中,如果我們在res.end中寫入中文的話,打開瀏覽器會出現(xiàn)亂碼的情況;
其實在服務端默認發(fā)送的數(shù)據(jù),其實是 utf8 編碼的內(nèi)容;但是瀏覽器不知道你是 utf8 編碼的內(nèi)容;瀏覽器在不知道服務器響應內(nèi)容的編碼的情況下會按照當前操作系統(tǒng)的默認編碼去解析;中文操作系統(tǒng)默認是 gbk
解決辦法: 就是正確的告訴瀏覽器我給你發(fā)送的內(nèi)容是什么編碼的;在 http 協(xié)議中,Content-Type 就是用來告知對方我給你發(fā)送的數(shù)據(jù)內(nèi)容是什么類型
res.setHeader('Content-Type', 'text/plain; charset=utf-8')
再輸出res.end語句結果就正確了
text/plain 就是普通文本
b.在瀏覽器中輸出html標簽
如果你發(fā)送的是 html 格式的字符串,則也要告訴瀏覽器我給你發(fā)送是 text/html 格式的內(nèi)容
res.setHeader('Content-Type', 'text/html; charset=utf-8')
res.end('<p>hello html <a href="">點我</a></p>')
結果會在瀏覽器中渲染成html標簽
利用文件的方法,將html文件渲染成頁面
fs.readFile('./resource/index.html', function (err, data) {
if (err) {
res.setHeader('Content-Type', 'text/plain; charset=utf-8')
res.end('文件讀取失敗,請稍后重試!')
} else {
res.setHeader('Content-Type', 'text/html; charset=utf-8')
res.end(data)
}
})
c.在瀏覽器中輸出圖片
-發(fā)送的并不是文件,本質(zhì)上來講發(fā)送是文件的內(nèi)容
-當瀏覽器收到服務器響應內(nèi)容之后,就會根據(jù)你的 Content-Type 進行對應的解析處理;圖片不需要指定編碼;一般只為字符數(shù)據(jù)才指定編碼
fs.readFile('./resource/ab2.jpg', function (err, data) {
if (err) {
res.setHeader('Content-Type', 'text/plain; charset=utf-8')
res.end('文件讀取失敗,請稍后重試!')
} else {
res.setHeader('Content-Type', 'image/jpeg')
res.end(data)
}
})
四、Node中的js-模塊系統(tǒng)
在 Node 中沒有全局作用域的概念;在 Node 中,只能通過 require 方法來加載執(zhí)行多個 JavaScript 腳本文件
1.Node.js中的核心模塊
- 核心模塊是由 Node 提供的一個個的具名的模塊,它們都有自己特殊的名稱標識,例如
- fs 文件操作模塊
- http 網(wǎng)絡服務構建模塊
- os 操作系統(tǒng)信息模塊
- path 路徑處理模塊 - 所有核心模塊在使用的時候都必須手動的先使用
require方法來加載,然后才可以使用,例如:var fs = require('fs')
// 用來獲取機器信息的
var os = require('os')
// 用來操作路徑的
var path = require('path')
// 獲取當前機器的 CPU 信息
console.log(os.cpus())
// memory 內(nèi)存
console.log(os.totalmem())
// 獲取一個路徑中的擴展名部分
// extname extension name
console.log(path.extname('c:/a/b/c/d/hello.txt'))
2.Node.js中的模塊系統(tǒng)
- 在 Node 中沒有全局作用域的概念
- 在 Node 中,只能通過 require 方法來加載執(zhí)行多個 JavaScript 腳本文件
- require 加載只能是執(zhí)行其中的代碼,文件與文件之間由于是模塊作用域,所以不會有污染的問題
- 模塊完全是封閉的
- 外部無法訪問內(nèi)部
- 內(nèi)部也無法訪問外部
- 模塊作用域固然帶來了一些好處,可以加載執(zhí)行多個文件,可以完全避免變量命名沖突污染的問題
- 但是某些情況下,模塊與模塊是需要進行通信的
- 在每個模塊中,都提供了一個對象:
exports - 該對象默認是一個空對象
- 你要做的就是把需要被外部訪問使用的成員手動的掛載到
exports接口對象中 - 然后誰來
require這個模塊,誰就可以得到模塊內(nèi)部的exports接口對象 - 還有其它的一些規(guī)則,具體后面講,以及如何在項目中去使用這種編程方式,會通過后面的案例來處理