node.js 介紹
node.js初識
- node.js 平臺是基于 Chrome V8 JavaScript 引擎構建。
- 基于 node.js 可以開發(fā)控制臺程序(命令行程序、CLI程序)构哺、桌面應用程序(GUI)(借助 node-webkit熙揍、electron 等框架實現(xiàn))、Web 應用程序(網站)
node.js 有哪些特點纵东?
- 事件驅動(當事件被觸發(fā)時歼捏,執(zhí)行傳遞過去的回調函數(shù))
- 非阻塞 I/O 模型(當執(zhí)行I/O操作時,不會阻塞線程)
- 單線程
- 擁有世界最大的開源庫生態(tài)系統(tǒng) —— npm而涉。
事件循環(huán)(Event-Loop)
事件循環(huán)會監(jiān)視調用棧著瓶,以及回調隊列
如果調用棧中為空,這個時候啼县,就會將回調隊列中的第一個元素放到調用棧中調用
異步-非阻塞
js是單線程的 異步的功能是由webapi或者nodeapi提供的2脑!## 事件循環(huán)(Event-Loop)
事件循環(huán)會監(jiān)視調用棧季眷,以及回調隊列
如果調用棧中為空余蟹,這個時候,就會將回調隊列中的第一個元素放到調用棧中調用
異步-非阻塞
js是單線程的 異步的功能是由webapi或者nodeapi提供的W庸巍威酒!
進程和線程
- 每一個 正在運行 的應用程序都稱之為進程。
- 每一個應用程序運行都至少有一個進程
- 進程是用來給應用程序提供一個運行的環(huán)境
- 進程是操作系統(tǒng)為應用程序分配資源的一個單位
- 用來執(zhí)行應用程序中的代碼
- 在一個進程內部话告,可以有很多的線程
- 在一個線程內部兼搏,同時只可以干一件事
- 而且傳統(tǒng)的開發(fā)方式大部分都是 I/O 阻塞的
- 所以需要多線程來更好的利用硬件資源
Node 中將所有的阻塞操作交給了內部實現(xiàn)的線程池
Node 本身主線程主要就是不斷的往返調度
REPL環(huán)境
- REPL 全稱: Read-Eval-Print-Loop(交互式解釋器)
- R 讀取 - 讀取用戶輸入,解析輸入了Javascript 數(shù)據(jù)結構并存儲在內存中沙郭。
- E 執(zhí)行 - 執(zhí)行輸入的數(shù)據(jù)結構
- P 打印 - 輸出結果
- L 循環(huán) - 循環(huán)操作以上步驟直到用戶兩次按下 ctrl-c佛呻、.exit或者process.exit() 按鈕退出。
- 在REPL中編寫程序 (類似于瀏覽器開發(fā)人員工具中的控制臺功能)
- 直接在控制臺輸入
node
命令進入 REPL 環(huán)境
- 按兩次 Control + C 退出REPL界面 或者 輸入
.exit
退出 REPL 界面
- 按住 control 鍵不要放開, 然后按兩下 c 鍵
node.js中JavaScript 文件名命名規(guī)則
- 不要用中文
- 不要包含空格
- 不要出現(xiàn)node關鍵字
- 建議以 '-' 分割單詞
node.js命令
process.stdout.write
這個方法可以用來輸出內容病线,而且不會自動換行
如果要換行可以加上\n
fs模塊--文件的讀寫
var fs = require("fs"); // 引用fs模塊
// 寫
fs.writeFile("文件路徑", "要寫的內容", "編碼格式", function(err){
if(err){
throw err;
}
console.log("寫入成功")
})
// 讀
fs.readFile("文件路徑", function(err, data){
//data是一個Buffer 字節(jié)數(shù)組
//獲取字符串吓著,需要自己toString
})
// 讀
fs.readFile("文件路徑", "utf-8", function(err, data){
//data是一個字符串
})
// 創(chuàng)建一個目錄
fs.mkdir('./test-mkdir',function(err){
if(err){
console.log("創(chuàng)建文件夾出錯");
console.log(err)
}else{
console.log("創(chuàng)建成功")
}
})
path模塊相關
__dirname -- 獲取當前文件所在目錄的完整路徑(偽全局)
__filename -- 獲取當前文件的完整路徑 (偽全局)
path.join() -- 用來拼接路徑
path.join(__dirname,"css","index.css");
http模塊
直接上代碼
//1. 引入http模塊
var http = require("http");
// 2. 創(chuàng)建服務實例
var server = http.createServer();
// 3. 注冊請求事件(每當有請求來的時候,就會觸發(fā)該事件)
server.on("request",function(request,response){
// 設置響應頭
response.writeHead(200,"OK",{
"Content-Type":"text/html;charset=utf-8"
})
//給瀏覽器端返回數(shù)據(jù)(要給響應體中添加內容)
response.write("");
//在所有的響應信息添加完畢之后送挑,需要結束響應
response.end();
})
//4. 開啟服務實例的監(jiān)聽功能
server.listen(端口號, function(){
//監(jiān)聽開啟成功后會執(zhí)行的函數(shù)
console.log("服務啟動成功")
})
request對象
- url 瀏覽器請求的地址(包含兩部分 路徑?參數(shù))
- method 瀏覽器發(fā)送該請求的方式 GET POST
- headers 請求頭中所有的信息(對象)
- rawHeaders 請求頭中所有的信息(數(shù)組)
- httpVersion 請求頭中包含的協(xié)議的版本
response對象
response.setHeader("鍵", "值") 往請求頭中新增信息
-
response.writeHead(狀態(tài)碼, 狀態(tài)描述信息, {往請求頭中新增信息的鍵值對})
- setHeader和writeHead都可以用來設置響應頭
- writeHead是直接將內容寫入響應頭中绑莺,而setHeader是在end的時候才寫入
- 如果同時使用setHeader和writeHead設置了響應信息,那么會合并惕耕,如果有相同的內容則以writeHead的為主
response.write(data, "編碼格式", callback) 給響應體中追加內容
response.end(data) 通知服務器 所有的響應內容已經發(fā)送完畢 如果傳入了data纺裁,就相當于是先調用response.write將數(shù)據(jù)追加之后再response.end結束響應 每次響應都需要調用這個end方法
response.statusCode -- 設置狀態(tài)碼
response.statusMessage -- 設置狀態(tài)碼對應的狀態(tài)信息
mime (第三方模塊)
在頁面請求的時候,不同的資源是有不同的content-type的值的司澎,如果通過response.url一一匹配設置太麻煩了欺缘,可以通過 mime 模塊設置不同類型資源的Content-Type
npm install mime
- 在代碼中直接引用
var mime = require('mime')
server.on("request",function(request,response){
if(request.url.startsWith("/static")){
response.writeHead(200,"OK",{
"Content-Type":mime.getType("文件路徑")
})
}
})
獲取post請求發(fā)送的數(shù)據(jù)
如果post傳過來的數(shù)據(jù)時大量的話,那么data事件可能會觸發(fā)多次挤安,所以可聲明一個空數(shù)組谚殊,用來存放每次觸發(fā)data事件的時候傳過來的一段數(shù)據(jù),end事件是當數(shù)據(jù)全部傳送完畢的時候會調用的函數(shù)蛤铜,這個時候再把數(shù)組中每一次傳過來的數(shù)據(jù)拼接起來嫩絮,就得到了完整的數(shù)據(jù)丛肢。
server.on("request", function(req, res){
var chunkArr = []; // 用來保存post請求傳過來的數(shù)據(jù)
req.on("data", function(chunk){
chunkArr.push(chunk); // 將此次數(shù)據(jù)存放起來
})
req.on("end", function(){
// 拼接全部數(shù)據(jù)
var result = Buffer.concat(chunkArr);
})
})
獲取get請求發(fā)送的數(shù)據(jù)
get請求的數(shù)據(jù)是在URL地址欄里的,所以可利用 url模塊 或者 querystring 模塊獲取
- url模塊
var url = require("url");
var urlObj = url.parse(request.url,true); // 返回的是個對象剿干,包含了url里面的信息
urlObj.query // query這個屬性返回的是參數(shù)(也即是get方式的數(shù)據(jù))對象
underscore和arttemplate --node.js模板--第三方模塊
// underscore
var _ = require("underscore");
var render = _.template("模板字符串");
var resultHtml = render("數(shù)據(jù)");
response.end(resultHtml); // 發(fā)送給客戶端
// arttemplate
// 直接自己讀文件
var resultHtml = template("模板字符串",數(shù)據(jù));
// 根據(jù)模板字符串生成渲染函數(shù)
var render = template.compile("模板字符串");
var resultHtml = render(數(shù)據(jù));
node.js模塊化
NOde采用的模塊化結構是按照CommonJS規(guī)范
模塊與文件是意義對應關系蜂怎,即加載一個模塊,實際上就是加載該模塊對應的模塊文件
-
模塊特點
- 所有代碼都運行在模塊作用域置尔,不會污染全局作用域派敷。
- 模塊可以多次加載,但是只會在第一次加載時運行一次撰洗,然后運行結果就被
緩存
了,以后再加載腐芍,就直接讀取緩存結果差导。要想讓模塊再次運行,必須清除緩存猪勇。 - 模塊加載的順序设褐,按照其在代碼中出現(xiàn)的順序。
-
模塊分類
文件模塊 -- 就是我們自己寫的功能模塊文件
核心模塊 -- Node 平臺自帶的一套基本的功能模塊泣刹,也有人稱之為 Node 平臺的 API
第三方模塊 -- 社區(qū)或第三方個人開發(fā)好的功能模塊助析,可以直接拿回來用
1. 定義模塊
將代碼寫在一個js文件中,這個js文件就是一個模塊了
module.exports是一個對象椅您,這個對象會在模塊被創(chuàng)建出來的時候外冀,同時被創(chuàng)建
// module.exports.成員 = 內容;
var say = function(){};
module.exports.say = say;
// 也可以直接給module.exports賦值
module.exports = {
say : say
}
// 注意: 雖然exports和module.exports指向的是同一個對象的的引用地址,但是模塊暴露給外接的屬性或方法掀泳,只能直接賦值給module.exports,因為模塊最終的返回值是 module.exports
2. 引用模塊
-
核心模塊
- require("核心模塊名稱")
-
文件模塊
require("文件的路徑雪隧,必須以/ ./ ../開頭")
文件路徑中的.js路徑可以省略,如果省略 .js 則node會查找 以該文件為名字 以 .js .node .json 為后綴的文件來加載员舵,如果沒有脑沿,就報錯了
-
目錄模塊
require("目錄路徑,必須以/ ./ ../開頭")
當以目錄路徑為參數(shù)的時候node.js會查找該目錄下的 package.json 文件马僻,會加載其 main 屬性所對應的文件
如果沒有 package.json 或者package.json中沒有main屬性 則去加載目錄中 index.js index.node
-
node_modules中的模塊
require("模塊名稱");
(這么寫是不帶./ / ../)
如果這么寫庄拇,先回去核心模塊中查找,如果找不到,則去當前目錄的父路徑中的node_modules文件夾中查找對應的模塊,如果還是找不到韭邓,就會一直沿著父路徑往上找措近,直到找到硬盤根目錄
如何查看要查找的路徑(module.paths)
3. 刪除模塊緩存
let absolutepath = path.join(__dirname, `../data/test.json`);
delete require.cache[absolutepath];
require(absolutepath);
express
express是什么
express是一個簡單高效用來開發(fā)web應用的框架(基于node.js)
怎么用它
- 安裝
- npm install express -S
- 引用并使用
//1. 引入
var express = require("express");
//2. 創(chuàng)建實例
var app = express();
//4. 注冊路由
app.use(express.static("static"))
//3. 啟動服務監(jiān)聽
app.listen(8888,function(){
console.log("http://localhost:8888")
})
express.static(root,[options])
root 參數(shù)指的是靜態(tài)資源文件所在的根目錄,寫絕對路徑仍秤。
options 對象是可選的(http://www.expressjs.com.cn/4x/api.html)
express 中的中間件
中間件(Middleware) 是一個函數(shù)熄诡,它可以訪問請求對象(request object (req)), 響應對象(response object (res)), 和 web 應用中處于請求-響應循環(huán)流程中的中間件,一般被命名為 next 的變量诗力。
-
常規(guī)中間件(應用級中間件)的回調函數(shù)凰浮,一般有3個參數(shù)
- req, res, next
- 其中next()是一個函數(shù)我抠,通過這個函數(shù)把執(zhí)行流程交給下一個中間件
-
可以掛載中間件的部分方法
- app.use()
- app.get()、app.post()袜茧、app.put()菜拓、app.delete()、...等各種請求方法
- 注意:在掛載中間件時 next() 方法的重要性笛厦,不調用該方法無法執(zhí)行到下一個中間件
-
中間件的應用場景
- 利用中間件實現(xiàn)記錄日志功能
- 利用中間件模擬body-parser功能
路由注冊
- app.method
// 只接受指定方式的請求
app.get("/index",function(req,res){})
app.post()
- app.use
// 可以接受任意方式的請求
// 請求路徑pathname纳鼎,必須以路由路徑開頭
app.use("/index",function(req,res){})
// index.html index/s/a 都可以
- app.all
// 可以接受任意方式的請求
// 請求路徑pathname必須和路由路徑完全一樣
app.all("/index",function(req,res){})
// index/s/a 不可以
res對象新增功能
-
res.send()
- send可以將對象 字符串 數(shù)組 數(shù)字 等等數(shù)據(jù)返回給瀏覽器
- 一次請求中只能用一次
- 他里面自動給響應報文頭中添加了一些內容 Content-type
-
res.sendFile()
- 可以用來向瀏覽器發(fā)送文件內容
res.json
res.jsonp
res.status(狀態(tài)碼).end(""); -- 設置狀態(tài)碼
res.redirect('http://google.com'); -- 重定向
req對象獲取GET方式或POST方式的請求數(shù)據(jù)
req.query 直接就可以獲取到get請求的參數(shù)對象
-
req.body 可以獲得POST提交方式的請求數(shù)據(jù)內容,但是需要配置
- 如果form表單中的enctype屬性未設置或者設置為application/x-www-form-urlencoded裳凸,那么req.body就可以通過body-parser解析之后獲取到請求數(shù)據(jù)
- 但是如果設置為 multipart/form-data這樣的格式 贱鄙,那么req.body是拿不到內容的!姨谷!
-
req.body的配置
- 下載包 npm install body-parser -S
- 引包 var parser = require("body-parser");
- 將其掛載到app上 app.use(parser.urlencoded({extended: false}));
- 注意: extend屬性如果設置為true, 則express最終將數(shù)據(jù)轉換成對象的時候使用的就是qs模塊逗宁,如果是false則使用的是querystring模塊!
- 注意: 掛載use這局代碼一定寫在app.use(router)之上梦湘,先讓parser處理POST請求的數(shù)據(jù)瞎颗,然后再讓路由去處理相關數(shù)據(jù)的邏輯代碼
// GET /shoes?order=desc&shoe[color]=blue&shoe[type]=converse
req.query.order
// => "desc
var app = require('express')();
var bodyParser = require('body-parser');
var multer = require('multer');
app.use(bodyParser.json()); // for parsing application/json
app.use(bodyParser.urlencoded({ extended: true })); // for parsing application/x-www-form-urlencoded
app.use(multer()); // for parsing multipart/form-data
app.post('/', function (req, res) {
console.log(req.body);
res.json(req.body);
})
路由對象
// router.js文件中
var express = require("express")
var router = express.Router();
router.get()
router.use()
// app.js文件中
var app = express();
app.use(router);
nodemon 自動刷新應用
1、全局下載 nodemon
npm install -g nodemon
2捌议、 修改 package.json的 scripts
{
"name": "myapp",
"version": "0.0.0",
"private": true,
"scripts": {
"start": "nodemon ./bin/www" // here here
},
"dependencies": {
"cookie-parser": "~1.4.3",
"debug": "~2.6.9",
"express": "~4.16.0",
"http-errors": "~1.6.2",
"morgan": "~1.9.0",
"pug": "2.0.0-beta11"
}
}
模板引擎(集成在express中如何使用)
var express = require("express");
var app = express();
// 1. 指定的為指定的后綴的模板文件哼拔,指定相應的模板引擎
app.engine("html", require("express-art-template"));
//2. 將模板的后綴通過set方法進行設置
app.set('view engine', 'html');
//3. 設置模板文件存放的目錄
app.set("views", "./views")
app.get("/index", function(req, res){
//res.render("不帶后綴的模板文件的名稱(這個文件會去設置好的模板文件的存放目錄去查找)", {數(shù)據(jù)}, function(err, html){
//err是異常信息
//html 模板最終的渲染結果,最終生成的html代碼
// })
res.render("index", {list: [{name: "呵呵"}, {name: "呵呵"}, {name: "哈哈"}]}, function(err, html){
res.send(html);
})
})
彈框下載
- node.js原生實現(xiàn)
res.setHeader("Content-Type", "application/octet-stream")
res.setHeader("Content-Disposition", "attachment; filename=xxx.txt")
//要返回的文件內容瓣颅,需要自己手動進行讀取倦逐,通過res.write獲取res.end返回給瀏覽器
fs.readFile(path.join(__dirname, "xxx.js"),"utf-8", function(err, data){
res.end(data);
})
- express實現(xiàn)
res.download("文件路徑");
使用node.js發(fā)送請求
-
流程介紹
- 引入http模塊
- 給http注冊request事件
- 設置請求參數(shù)
- 獲取響應信息(和用node.js原生代碼,拿POST方式提交數(shù)據(jù)一樣)
- 注冊 發(fā)生錯誤的事件
- 結束請求
代碼示例
var http = require("http");
var req = http.request({
host:"www.qiushibaike.com",
hostname:"www.qiushibaike.com",
method:"GET",
port:443,
path:"/",
headers:{
"Connection":" keep-alive",
"Pragma":" no-cache",
"Cache-Control":" no-cache",
// 必須要寫E丁僻孝!
"User-Agent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/59.0.3071.115 Safari/537.36",
//注意:gzip如果在請求頭中出現(xiàn)了,那么服務器端在響應請求的時候守谓,會將所有的數(shù)據(jù)進行壓縮穿铆,以提高傳輸效率
//如果是瀏覽器在請求,那么瀏覽器拿到壓縮后的數(shù)據(jù)之后斋荞,會自行解壓荞雏,然后進行展示
//nodejs中請求到數(shù)據(jù)之后,并不能自行解壓平酿,所以我們不在請求頭加這個內容凤优!
}
},function(response){
var buffer = []; // 用來存放返回信息
response.on("data",function(chunk){
buffer.push(chunk);
});
response.on("end",function(){
buffer = Buffer.concat(buffer);
buffer.toString("utf-8")
})
})
req.on("error", function(err){
throw err;
})
// end事件不要忘記注冊!
req.end();
cheerio 處理服務器返回來的HTML代碼
-
咋弄
- 下載包 npm install cheerio -S
- 引包 var cheerio = require("cheerio")
- 加載返回回來HTML數(shù)據(jù) var $ = cheerio.load("html代碼")
- 然后就和jQuery一樣來操作里面的元素就闊以了蜈彼!
代碼示例
var cheerio = require("cheerio");
// 通過http筑辨,拿到的HTML格式的數(shù)據(jù)經過buffer拼接,轉碼幸逆,發(fā)送給cheerio
var $ = cheerio.load(Buffer.concat(buffer).toString("utf-8"));
// 用來存放自己想要的東西
var qiubaArr = [];
$(".article").each(function(index, ele){
// 拿到所有author
var author = $(ele).find(".author h2").text()
var content = $(ele).find(".content>span").text();
// 把所有數(shù)據(jù)存起來棍辕,然后
qiubaArr.push({
author,
content
})
})
CRUD
使用 MongoDB 官方提供的 mongodb 驅動包操作 MongoDB 數(shù)據(jù)庫
安裝:
npm install mongodb --save
CRUD:
參考文檔:
- https://www.npmjs.com/package/mongodb#connecting-to-mongodb
- http://mongodb.github.io/node-mongodb-native/2.2/
var mongodb = require('mongodb')
// 連接路徑URL
var url = 'mongodb://localhost:27017/itcast'
var MongoClient = mongodb.MongoClient
// ================== 插入數(shù)據(jù) ==================
// 1. 連接數(shù)據(jù)庫(打開冰箱門)
// MongoClient.connect(url, function (err, db) {
// if (err) {
// throw new Error('連接失敗')
// }
// // 2. 把大象放到冰箱
// db.collection('heros').insert({ name: '張飛', gender: '男', age: 23 }, function (err, result) {
// if (err) {
// throw new Error('插入數(shù)據(jù)失敗')
// }
// console.log(result)
// // 3. 關上冰箱門
// db.close()
// })
// })
// ================== /插入數(shù)據(jù) ==================
// ================== 查詢數(shù)據(jù) ==================
// MongoClient.connect(url, function (err, db) {
// if (err) {
// throw new Error('連接失敗')
// }
// // 查詢所有
// // db.collection('heros').find({}).toArray(function (err, docs) {
// // if (err) {
// // throw new Error('查詢數(shù)據(jù)失敗')
// // }
// // console.log(docs)
// // })
// // 按條件查詢
// db.collection('heros').find({name: '張飛'}).toArray(function (err, docs) {
// if (err) {
// throw new Error('查詢數(shù)據(jù)失敗')
// }
// console.log(docs)
// })
// })
// ================== /查詢數(shù)據(jù) ==================
// ================== 更新數(shù)據(jù) ==================
// MongoClient.connect(url, function (err, db) {
// if (err) {
// throw new Error('連接失敗')
// }
// db.collection('heros').updateOne({name: '張飛'}, {
// $set: {
// age: 20
// }
// }, function (err, result) {
// if (err) {
// throw new Error('更新失敗')
// }
// console.log(result)
// })
// })
// ================== /更新數(shù)據(jù) ==================
// ================== 刪除數(shù)據(jù) ==================
// MongoClient.connect(url, function (err, db) {
// if (err) {
// throw new Error('連接失敗')
// }
// db.collection('heros').deleteOne({name: '張飛'}, function (err, result) {
// if (err) {
// throw new Error('刪除失敗')
// }
// console.log(result)
// })
// })
// ================== /刪除數(shù)據(jù) ==================
在node做本地服務器的項目中暮现,做到文件下載
- 流的方式
router.post('/:cubeId/download', function(req, res, next) {
let fileName = 'file';
// console.log(res)
// console.log(req.body);
// console.log(res.body.params);
let currFilePath = path.join(__dirname,`../data/file.txt`);
let f = fs.createReadStream(currFilePath);
res.writeHead(200, {
'Content-Type': 'application/force-download',
'Content-Disposition': `attachment; filename=file.txt`
});
f.pipe(res);
})