一蜈漓、Nod.js基本概念
1.為什么學Node
-
企業(yè)需求:具有服務(wù)端開發(fā)需求
-
目的:打開服務(wù)端黑匣子
-
網(wǎng)站開發(fā)能力:服務(wù)端蹲堂,前端黍翎,運維部署
2.Node是什么?
-
Node.js不是語言腻要,不是庫复罐,也不是框架
-
是JS運行時環(huán)境(是一個平臺),可以解析和執(zhí)行JS代碼
3. Node.js 中的 JavaScript
- 沒有 BOM雄家、DOM
- EcmaScript 基本的 JavaScript 語言部分
- 在 Node 中為 JavaScript 提供了一些服務(wù)器級別的 API
- 文件操作的能力
- http 服務(wù)的能力
- 網(wǎng)絡(luò)服務(wù)構(gòu)建的能力
- 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的一些操作乱投,結(jié)果如下:
沒有任何輸出,但是我們在html中打開瀏覽器界面顷编,引用這個js文件在控制臺可以看見
注意:Node和瀏覽器中的 JavaScript 不一樣
2.Node的讀文件基本操作
注意:
1.
瀏覽器中的 JavaScript 是沒有文件操作的能力的戚炫,但是 Node 中的 JavaScript 具有文件操作的能力
2.
在 Node 中如果想要進行文件操作,就必須引入 fs 這個核心模塊; 在 fs 這個核心模塊中媳纬,就提供了所有的文件操作相關(guān)的 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)
結(jié)果如下:
注意:
<Buffer 68 65 6c 6c 6f 20 6e 6f 64 65 6a 73 0d 0a>
文件中存儲的其實都是二進制數(shù)據(jù) 0 1贮庞; 這里為什么看到的不是 0 和 1 呢峦筒?原因是二進制轉(zhuǎn)為 16 進制了;但是無論是二進制01還是16進制窗慎,人類都不認識物喷;所以我們可以通過 toString 方法把其轉(zhuǎn)為我們能認識的字符
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峦失,所以結(jié)果為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())
}
})
如果讀取失敗,結(jié)果如下圖所示:
3.Node的寫文件基本操作
a.寫文件的第一步也是使用require加載fs核心模塊
var fs = require('fs')
b.寫入文件
fs.writeFile('./data/你好.md', '大家好术吗,給大家介紹一下尉辑,我是Node.js', function (error) {
console.log('文件寫入成功')
})
結(jié)果會在控制臺輸出:文件寫入成功
同時在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文件夾
結(jié)果如下:
只是在控制臺輸出語句几迄,但是在data文件夾里沒有新建文件蔚龙,也沒有新建d文件夾。
因此乓旗,我們可以在這里加上 if 語句進行判斷
fs.writeFile('./data/你好.md', '大家好府蛇,給大家介紹一下,我是Node.js', function (error) {
if (error) {
console.log('寫入失敗')
} else {
console.log('寫入成功了')
}
})
三屿愚、Node之http服務(wù)
1.簡單的http服務(wù)
我們可以使用 Node 非常輕松的構(gòu)建一個 Web 服務(wù)器汇跨;在 Node 中專門提供了一個核心模塊:http;http 這個模塊的職責就是幫你創(chuàng)建編寫服務(wù)器的
a.加載 http 核心模塊
var http = require('http')
b.使用 http.createServer() 方法創(chuàng)建一個 Web 服務(wù)器妆距;返回一個 Server 實例
var server = http.createServer()
c.注冊 request 請求事件
當客戶端請求過來穷遂,就會自動觸發(fā)服務(wù)器的 request 請求事件,然后執(zhí)行第二個參數(shù):回調(diào)處理函數(shù)
server.on('request', function () {
console.log('收到客戶端的請求了')
})
d.綁定端口號娱据,啟動服務(wù)器
server.listen(3000, function () {
console.log('服務(wù)器啟動成功了蚪黑,可以通過 http://127.0.0.1:3000/ 來進行訪問')
})
運行該JS文件后,就可以打開瀏覽器
端口號范圍:0~65536
2.發(fā)送響應(yīng)
request 請求事件處理函數(shù)中剩,需要接收兩個參數(shù):
- Request 請求對象
- 請求對象可以用來獲取客戶端的一些請求信息忌穿,例如請求路徑
- Response 響應(yīng)對象
- 響應(yīng)對象可以用來給客戶端發(fā)送響應(yīng)消息
- response 對象有一個方法:write 可以用來給客戶端發(fā)送響應(yīng)數(shù)據(jù)
- write 可以使用多次,但是最后一定要使用 end 來結(jié)束響應(yīng)结啼,否則客戶端會一直等待
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('服務(wù)器啟動成功了,可以通過 http://127.0.0.1:3000/ 來進行訪問')
})
結(jié)果在瀏覽器輸出:
3.練習:根據(jù)不同的請求路徑返回不同數(shù)據(jù)
注意:
上面使用的 res.write方式比較麻煩郊愧,推薦使用更簡單的方式朴译,直接 end 的同時發(fā)送響應(yīng)數(shù)據(jù)
// res.write('hello')
// res.write(' world')
// res.end()
res.end('hello nodejs')
練習分析:根據(jù)不同的請求路徑發(fā)送不同的響應(yīng)結(jié)果
-
1. 獲取請求路徑
+
req.url 獲取到的是端口號之后的那一部分路徑
·+
url:同一資源定位符,一個url其實是要定一個資源
+
也就是說所有的 url 都是以 / 開頭的
-
2. 判斷路徑處理響應(yīng)
+
響應(yīng)內(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('服務(wù)器啟動成功,可以訪問了。坟乾。迹辐。')
})
4.端口號
ip 地址定位計算機
端口號定位具體的應(yīng)用程序
所有需要聯(lián)網(wǎng)通信的應(yī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('服務(wù)器啟動成功甚侣,可以訪問了。间学。殷费。')
})
5.Content-Type
- 服務(wù)器最好把每次響應(yīng)的數(shù)據(jù)是什么內(nèi)容類型都告訴客戶端,而且要正確的告訴
- 不同的資源對應(yīng)的 Content-Type 是不一樣低葫,具體參照:http://tool.oschina.net/commons
- 對于文本類型的數(shù)據(jù)详羡,最好都加上編碼,目的是為了防止中文解析亂碼問題
a.在瀏覽器中輸出漢字
在之前的學習中嘿悬,如果我們在res.end中寫入中文的話实柠,打開瀏覽器會出現(xiàn)亂碼的情況;
其實在服務(wù)端默認發(fā)送的數(shù)據(jù)善涨,其實是 utf8 編碼的內(nèi)容窒盐;但是瀏覽器不知道你是 utf8 編碼的內(nèi)容;瀏覽器在不知道服務(wù)器響應(yīng)內(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語句結(jié)果就正確了
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>')
結(jié)果會在瀏覽器中渲染成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)容
-
當瀏覽器收到服務(wù)器響應(yīng)內(nèi)容之后,就會根據(jù)你的 Content-Type 進行對應(yīng)的解析處理颂斜;圖片不需要指定編碼夫壁;一般只為字符數(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)絡(luò)服務(wù)構(gòu)建模塊
- 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ī)則斩萌,具體后面講缝裤,以及如何在項目中去使用這種編程方式,會通過后面的案例來處理