一、Node快速體驗
1、 Node介紹
(1) Node.js是什么
-
Node
是一個基于Chrome V8
引擎的JavaScript
運行環境。-
Node
不是一種獨立的語言、 -
Node
不是JavaScript
框架, -
Node
是一個除了瀏覽器之外的、可以讓JavaScript
運行的環境
-
(2) Node 能做什么
- Web 服務器(重點)
- 命令行工具
- 網絡爬蟲:是一種按照一定的規則,自動地抓取網站信息的程序
- 桌面應用程序開發
(3) 一些資源
① 文檔
Node.js 官方文檔
Node.js 中文文檔(非官方)
② 書籍
深入淺出 Node.js
Node.js 權威指南
Node.js 實戰
Node.js實戰(第2季)
③ github資源
Node.js 包教不包會
ECMAScript 6 入門
七天學會 NodeJS
④ 社區
2. Node起步
(1)安裝Node
對于已經裝過的,重新安裝就會升級
確認 Node 環境是否安裝成功
打開命令行,輸入 node --version
或者 node -v
(2)REPL環境(Read Eval Print Loop:交互式解釋器:了解)
在命令行工具界面輸入node
按回車 就進入到了REPL
環境
- read
- eval
- loop
類似于瀏覽器中的 Console控制臺 ,可以做一些代碼測試。
按ctrl + 兩次c 退出REPL環境
但是, 我們寫代碼肯定不是在控制臺中寫,而是寫在一個單獨的.js文件中.
(3)HelloWorld
- 新建一個
hello.js
并寫入以下示例代碼
var message = 'Hello World!'
console.log(message)
- 打開命令行并定位到
hello.js
文件所屬目錄 - 在命令行中輸入
node hello.js
回車執行
node hello.js
(4)Node中的js
node中的js和瀏覽器中的js不同:
- 瀏覽器中的js
- ECMAScript
- DOM
- BOM
- node中的js
- ECMAScript
- 自己的API
- 沒有DOM和BOM
二、Node中的模塊和包管理器npm
1、Node中的模塊體驗
(1)文件讀寫
文件讀取:
// 1、引用fs核心模塊
var fs = require('fs');
// 2、讀取文件
// fs.readFile中有兩個參數:
// path: 要讀取文件的路徑
// callback:讀取文件后面的回調函數
// err: 讀取文件異常的異常信息
// 如果文件讀取出來err為null
// 如果文件讀取失敗err為失敗信息
// data: 文件中的內容
// 如果文件讀取成功,那么將來data會是一個十六進制的Buffer數組
fs.readFile('./00.txt', function(err, data){
// 輸出文件中的內容
console.log(err);
console.log(data.toString());
});
文件寫入:
注意:
在寫入時會覆蓋已有的內容
如果寫入的文件不存在,會自動創建
// 1、引用fs核心模塊
var fs = require('fs');
// 2、向文件中寫入內容
// 這個方法是用來覆蓋原文件中的內容,
// fs.wirteFile(file, data, callback)
// file:要寫入內容的文件
// data:要寫入的數據(可以為utf-8)
// callback:寫入后的回調函數
// err: 寫入失敗后面的異常信息
fs.writeFile('./0000.txt', '天若有情天易老,人間正道是滄桑', function(err){
if (err) {
console.log(err.message);
} else {
console.log('寫入成功');
}
});
文件追加寫:
// 核心模塊:fs
// fs.readFile
// fs.writeFile
// 思想:先讀,然后追加,然后再寫
var fs =require("fs");
// 1、讀取
fs.readFile("./00.txt", function(err, data){
if(err) {
console.log(err.message);
} else {
// 2、追加
var str = "小豬配奇身上紋,掌聲送給社會人";
str = data + str;
fs.writeFile("./00.txt", str, function(err) {
if(err) {
console.log(err.message);
} else {
console.log("寫入成功");
}
});
}
});
(2)HTTP 服務
// node把處理web服務器相關的功能 封裝到了 http模塊中
// 1、導入http模塊
var http = require('http');
// 2、使用http這個模塊中的createServer()創建一個服務器實例對象
var server = http.createServer();
// 3、給服務器對象注冊一個request事件,當瀏覽器有請求發送過來時,服務器會接收,并且執行后面的回調函數
// 請求處理函數function(形參1,形參2){}
// 形參1:request請求對象 獲取到當前請求的路徑,方法等信息
// 形參2:response響應對象 發送響應數據
server.on('request', function(request, response) {
console.log('有瀏覽器連接上來了!');
// 向客戶端頁面返回字符串
response.write("hello node");
// 結束響應
response.end();
});
// 4、綁定端口號,啟動web服務器
server.listen(12345, function() {
console.log(' 請訪問http://localhost:12345');
});
注意:
- 注冊request處理函數 有兩個形參 request和response 不要搞混
- 在寫完服務器代碼后 保存代碼 ->在cmd中
node 文件名
然后回車執行- 每次修改代碼后 要重新在cmd中
node 文件名
執行這個文件- !! 端口號盡量寫的大一些 大于3000 否則容易和已經運行的軟件所占用的接口相沖突!
2、小案例
(1)通過http模塊寫一個自己的服務器
需求:將來有請求鏈接上來以后,服務器需求響應一個hello world的內容回瀏覽器
var http = require("http");
var server = http.createServer();
// 如果需要響應在requrest事件后面的回調函數中有兩個參數:
// request
// response
// 參數:
// request:瀏覽器發送到服務器的請求
// response:服務器響應回瀏覽器的數據
// 方法:
// write:向響應報文中寫入一段要發送瀏覽器的數據,不會將數據立即響應回瀏覽器
// end:將數據響應回瀏覽器
// setHeader:設置響應頭
server.on('request', function(request, response){
// 響應數據回瀏覽器要借助response
// response.write('hello');
// response.write(' world');
// response.end('hello world');
// 設置響應報文的響應頭
response.setHeader("content-type", "text/html;charset=utf-8");
response.end('<doctype><html><head></head><body></body></html>');
});
//下一行listen方法中第二個參數可以指定自己的IP,也可以不寫,不寫時在瀏覽器訪問localhost:3000即可
server.listen(3000, "192.168.81.1", function(){
console.log('服務器開啟成功');
});
(2)通過服務器響應一個完整的html頁面回瀏覽器
js文件:
var http = require("http");
// 引用fs模塊
var fs = require('fs');
var server = http.createServer();
server.on('request', function(request, response){
// response.setHeader("content-type","text/html;charset=utf-8");
// response.end('');
// 響應一個完整頁面回瀏覽器的思路
// 1、創建一個完整的html頁面
// 2、將這個頁面中內容讀取出來
fs.readFile('./views/index.html', function(err, data){
if(err) {
return console.log(err.message);
}
// 3、將內容通過response.end響應回瀏覽器
response.end(data);
});
});
server.listen(3000, "192.168.81.1", function(){
console.log('服務器開啟成功');
});
html文件:./views/index.html
<body>
<h1>你好,世界!</h1>
</body>
(3)在上面基礎上顯示一張圖片
js文件:
var http = require("http");
var fs = require('fs');
var server = http.createServer();
// request:所有瀏覽器發送到服務器的請求數據
// 屬性:
// url
server.on('request', function (request, response) {
// 如果將來圖片的請求過來服務器,服務器是沒有作任何處理
// 處理一個圖片和頁面的請求:
// 約定:
// 將來請求根目錄時,是請求頁面
// 將來請求views/0.jpg時,是請求圖片
// 問題:如何得到請求的路徑
// request.url
// 判斷:如果請求的 / ,響應頁面回去
// 如果請求的 /views/0.jpg響應圖片回去
if (request.url === '/') {
fs.readFile('./views/index.html', function (err, data) {
if (err) {
return console.log(err.message);
}
response.end(data);
});
} else if (request.url === '/views/0.jpg') {
fs.readFile("./views/0.jpg", function(err, data){
if (err) {
return console.log(err.message);
}
response.end(data);
});
}
});
server.listen(3000, "192.168.81.1", function () {
console.log('服務器開啟成功');
});
html文件:./views/index.html
<body>
<h1>你好,世界!</h1>
<!-- 因為這個路徑壓根就沒有在服務器中被解析過,是瀏覽器來解析,因此使用絕對路徑 -->
<img src="/views/0.jpg" id="img">
</body>
(4)在此基礎上響應一個js文件
var http = require("http");
var fs = require('fs');
var server = http.createServer();
server.on('request', function (request, response) {
// 處理根目錄的請求
if (request.url === '/') {
fs.readFile('./views/index.html', function (err, data) {
if (err) {
return console.log(err.message);
}
response.end(data);
});
// 處理了圖片的請求
} else if (request.url === '/views/0.jpg') {
fs.readFile("./views/0.jpg", function(err, data){
if (err) {
return console.log(err.message);
}
response.end(data);
});
// 處理jquery
} else if (request.url === '/views/jquery.min.js') {
fs.readFile("./views/jquery.min.js", function(err, data){
if (err) {
return console.log(err.message);
}
response.end(data);
});
}
});
server.listen(3000, "192.168.81.1", function () {
console.log('服務器開啟成功');
});
html:
<body>
<h1>你好,世界!</h1>
<img src="/views/0.jpg" id="img">
<input type="button" value="點我隱藏" id="btn">
</body>
<script src="/views/jquery.min.js"></script>
<script>
$("#btn").click(function(){
$("#img").hide(1000);
});
</script>
(5)在此基礎上統一處理靜態文件
js文件:
var http = require("http");
var fs = require('fs');
var server = http.createServer();
server.on('request', function (request, response) {
if (request.url === '/') {
fs.readFile('./views/index.html', function (err, data) {
if (err) {
return console.log(err.message);
}
response.end(data);
});
}
// 統一處理靜態資源:
// 判斷請求路徑中是否有攜帶views
else if (request.url.indexOf("/views") != -1) {
// 直接將對應的文件讀取出來
// 先將絕對路徑轉為相對路徑
var url = '.' + request.url;
fs.readFile(url, function(err, data){
if(err) {
return console.log(err.message);
}
response.end(data);
});
}
});
server.listen(3000, "192.168.81.1", function () {
console.log('服務器開啟成功');
});
html:
<head>
<link rel="stylesheet" href="/views/index.css">
</head>
<body>
<h1>你好,世界!</h1>
<img src="/views/0.jpg" id="img">
<input type="button" value="點我隱藏" id="btn">
</body>
<script src="/views/jquery.min.js"></script>
<script>
$("#btn").click(function(){
$("#img").hide(1000);
});
</script>
(6) 完成仿apache的目錄瀏覽功能
步驟:
- 服務器
- 1、創建一個服務器
- 2、如果將來瀏覽器請求靜態頁面,我們就將一個靜態文件返回到瀏覽器
- 4、服務器接收到瀏覽器的請求,將當前目錄下的所有的路徑讀取出來,并且返回給瀏覽器的異步對象
- 瀏覽器
- 3、通過瀏覽器發送一個異步請求到服務器,去請求當前服務器的目錄數據(getPath)
- 5、瀏覽器接收數據,并且將數據渲染到頁面上
js文件:
// 創建一個服務器
// 1、引用http模塊
var http = require('http');
var fs = require('fs');
// 2、創建一個服務器對象
var server = http.createServer();
// 3、設置request事件
server.on('request', function (req, res) {
// 3.1、請求請求參數
var url = req.url;
// 3.2、判斷是否請求的是首頁 /
if (url === '/') {
// 將首頁的靜態文件響應回瀏覽器
// 先通過fs模塊將內容讀取出來
fs.readFile('./views/index.html', function(err, data){
if(err) {
return console.log(err.message);
}
res.end(data);
});
}
// 3.3、判斷是否請求的是靜態文件
else if (url.indexOf('/views') != -1) {
fs.readFile('.' + url, function(err, data){
if(err) {
return console.log(err.message);
}
res.end(data);
});
}
// 3.4、判斷請求的是否是getPath
else if (url === '/getPath') {
//將當前目錄下的所有的路徑讀取出來,并且返回給瀏覽器的異步對象
fs.readdir('./', function(err, files){
if(err) {
return console.log(err.message);
}
// 將files數組轉為json格式的字符串
var str = JSON.stringify(files);
// console.log(str);
// 響應回瀏覽器
res.end(str);
});
}
});
// 4、開啟監聽
server.listen(3000, '192.168.81.1', function () {
console.log('開啟成功');
});
但是此方法在打開網頁后會出現小bug,即文件和目錄分不清,圖標都會顯示文件夾的樣子;文件大小和修改時間顯示不正確,若要修復bug這需要后面的知識。
html:
<style>
h1 {
border-bottom: 1px solid #c0c0c0;
margin-bottom: 10px;
padding-bottom: 10px;
white-space: nowrap;
}
table {
border-collapse: collapse;
}
th {
cursor: pointer;
}
td.detailsColumn {
-webkit-padding-start: 2em;
text-align: end;
white-space: nowrap;
}
a.icon {
-webkit-padding-start: 1.5em;
text-decoration: none;
}
a.icon:hover {
text-decoration: underline;
}
a.file {
background: url("data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABAAAAAQCAIAAACQkWg2AAAABnRSTlMAAAAAAABupgeRAAABHUlEQVR42o2RMW7DIBiF3498iHRJD5JKHurL+CRVBp+i2T16tTynF2gO0KSb5ZrBBl4HHDBuK/WXACH4eO9/CAAAbdvijzLGNE1TVZXfZuHg6XCAQESAZXbOKaXO57eiKG6ft9PrKQIkCQqFoIiQFBGlFIB5nvM8t9aOX2Nd18oDzjnPgCDpn/BH4zh2XZdlWVmWiUK4IgCBoFMUz9eP6zRN75cLgEQhcmTQIbl72O0f9865qLAAsURAAgKBJKEtgLXWvyjLuFsThCSstb8rBCaAQhDYWgIZ7myM+TUBjDHrHlZcbMYYk34cN0YSLcgS+wL0fe9TXDMbY33fR2AYBvyQ8L0Gk8MwREBrTfKe4TpTzwhArXWi8HI84h/1DfwI5mhxJamFAAAAAElFTkSuQmCC ") left top no-repeat;
}
a.dir {
background: url("data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABAAAAAQCAYAAAAf8/9hAAAAGXRFWHRTb2Z0d2FyZQBBZG9iZSBJbWFnZVJlYWR5ccllPAAAAd5JREFUeNqMU79rFUEQ/vbuodFEEkzAImBpkUabFP4ldpaJhZXYm/RiZWsv/hkWFglBUyTIgyAIIfgIRjHv3r39MePM7N3LcbxAFvZ2b2bn22/mm3XMjF+HL3YW7q28YSIw8mBKoBihhhgCsoORot9d3/ywg3YowMXwNde/PzGnk2vn6PitrT+/PGeNaecg4+qNY3D43vy16A5wDDd4Aqg/ngmrjl/GoN0U5V1QquHQG3q+TPDVhVwyBffcmQGJmSVfyZk7R3SngI4JKfwDJ2+05zIg8gbiereTZRHhJ5KCMOwDFLjhoBTn2g0ghagfKeIYJDPFyibJVBtTREwq60SpYvh5++PpwatHsxSm9QRLSQpEVSd7/TYJUb49TX7gztpjjEffnoVw66+Ytovs14Yp7HaKmUXeX9rKUoMoLNW3srqI5fWn8JejrVkK0QcrkFLOgS39yoKUQe292WJ1guUHG8K2o8K00oO1BTvXoW4yasclUTgZYJY9aFNfAThX5CZRmczAV52oAPoupHhWRIUUAOoyUIlYVaAa/VbLbyiZUiyFbjQFNwiZQSGl4IDy9sO5Wrty0QLKhdZPxmgGcDo8ejn+c/6eiK9poz15Kw7Dr/vN/z6W7q++091/AQYA5mZ8GYJ9K0AAAAAASUVORK5CYII= ") left top no-repeat;
}
a.up {
background: url("data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABAAAAAQCAYAAAAf8/9hAAAAGXRFWHRTb2Z0d2FyZQBBZG9iZSBJbWFnZVJlYWR5ccllPAAAAmlJREFUeNpsU0toU0EUPfPysx/tTxuDH9SCWhUDooIbd7oRUUTMouqi2iIoCO6lceHWhegy4EJFinWjrlQUpVm0IIoFpVDEIthm0dpikpf3ZuZ6Z94nrXhhMjM3c8895977BBHB2PznK8WPtDgyWH5q77cPH8PpdXuhpQT4ifR9u5sfJb1bmw6VivahATDrxcRZ2njfoaMv+2j7mLDn93MPiNRMvGbL18L9IpF8h9/TN+EYkMffSiOXJ5+hkD+PdqcLpICWHOHc2CC+LEyA/K+cKQMnlQHJX8wqYG3MAJy88Wa4OLDvEqAEOpJd0LxHIMdHBziowSwVlF8D6QaicK01krw/JynwcKoEwZczewroTvZirlKJs5CqQ5CG8pb57FnJUA0LYCXMX5fibd+p8LWDDemcPZbzQyjvH+Ki1TlIciElA7ghwLKV4kRZstt2sANWRjYTAGzuP2hXZFpJ/GsxgGJ0ox1aoFWsDXyyxqCs26+ydmagFN/rRjymJ1898bzGzmQE0HCZpmk5A0RFIv8Pn0WYPsiu6t/Rsj6PauVTwffTSzGAGZhUG2F06hEc9ibS7OPMNp6ErYFlKavo7MkhmTqCxZ/jwzGA9Hx82H2BZSw1NTN9Gx8ycHkajU/7M+jInsDC7DiaEmo1bNl1AMr9ASFgqVu9MCTIzoGUimXVAnnaN0PdBBDCCYbEtMk6wkpQwIG0sn0PQIUF4GsTwLSIFKNqF6DVrQq+IWVrQDxAYQC/1SsYOI4pOxKZrfifiUSbDUisif7XlpGIPufXd/uvdvZm760M0no1FZcnrzUdjw7au3vu/BVgAFLXeuTxhTXVAAAAAElFTkSuQmCC ") left top no-repeat;
}
html[dir=rtl] a {
background-position-x: right;
}
#listingParsingErrorBox {
border: 1px solid black;
background: #fae691;
padding: 10px;
display: none;
}
</style>
<body>
<div id="listingParsingErrorBox" i18n-values=".innerHTML:listingParsingErrorBoxText">糟糕!Google Chrome無法解讀服務器所發送的數據。請<a >報告錯誤</a>,并附上<a href="LOCATION">原始列表</a>。</div>
<span id="parentDirText" style="display:none" i18n-content="parentDirText">[上級目錄]</span>
<h1 id="header" i18n-content="header">C:\dev\ 的索引</h1>
<table>
<thead>
<tr class="header" id="theader">
<th i18n-content="headerName" onclick="javascript:sortTable(0);">名稱</th>
<th class="detailsColumn" i18n-content="headerSize" onclick="javascript:sortTable(1);">大小</th>
<th class="detailsColumn" i18n-content="headerDateModified" onclick="javascript:sortTable(2);">修改日期</th>
</tr>
</thead>
<tbody id="tbody">
<tr>
<td data-value=".."><a class="icon up" href="#">[上級目錄]</a></td>
<td class="detailsColumn" data-value="0"></td>
<td class="detailsColumn" data-value="0"></td>
</tr>
</tbody>
</table>
</body>
<script src="/views/jquery.min.js"></script>
<!-- 3、通過瀏覽器發送一個異步請求到服務器,去請求當前服務器的目錄數據 -->
<script>
$.ajax({
url: '/getPath',
type: 'GET',
dataType: 'json',
success: function(data){
// console.log(data);
// 通過js動態生成html代碼
var str = '';
for(var i = 0; i < data.length; i ++) {
str += "<tr>";
str += '<td data-value="播放器/"><a class="icon dir" href="#">'+ data[i] +'</a></td>';
str += '<td class="detailsColumn" data-value="0"></td>';
str += '<td class="detailsColumn" data-value="1489037313">2018/8/11 下午1:28:33</td>';
str += '</tr>';
}
// 將data動態渲染到頁面上
$('#tbody').append(str);
}
})
</script>
(7)使用fs核心模塊來判斷路徑是否為文件&文件夾
var fs = require('fs');
var str = './01.js';
// 可以使用fs核心模塊中的方法來進行判斷
// fs.stat用來判斷路徑的狀態
// path:要判斷的路徑
// callback:判斷之后的回調函數
// err:異常信息
// stats:狀態對象(obj)
// size:大小
// 如果是文件有大小
// 如果是目錄,沒有大小
// mtime:內容被修改的時間
fs.stat(str, function(err, stats){
if(err) {
return console.log(err.message);
}
// console.log(stats);
if(stats.isFile()) {
console.log(str + '是文件');
console.log(stats.size);
} else {
console.log(str + '是目錄');
}
});
(8)仿apach的目錄瀏覽功能-服務器渲染
var http = require('http');
var fs = require('fs');
//下載使用npm下載art-template 第三方包,用來渲染數據到html結構中
var template = require('art-template');
// 創建一個服務器
var server = http.createServer();
// 設置請求事件
server.on('request', function (req, res) {
// 如果讀取根目錄,并數據 + html結構響應回去
var url = req.url;
// 判斷
if (url === '/') {
// 得到html結構
// readFile讀取出來 的內容是十六進制的buffer數組
fs.readFile('./views/index.html', function (err, data) {
if (err) {
return console.log(err.message);
}
// res.end(data);
// 得到數據
fs.readdir('./', function (err1, files) {
if (err1) {
return console.log(err1.message);
}
// 區分文件和文件夾
var fileArr = [];
var dirArr = [];
var count = 0;
for (var i = 0; i < files.length; i++) {
(function (i) {
fs.stat(files[i], function (err2, stat) {
count ++;
if (err2) {
return console.log(err2.message);
}
if (stat.isFile()) {
fileArr.push({
name: files[i],
type: 'file',
size: stat.size,
time: stat.mtime
});
} else {
dirArr.push({
name: files[i],
type: 'dir',
size: stat.size,
time: stat.mtime
});
}
if (count === files.length) {
var newArr = dirArr.concat(fileArr);
// newArr 就是我們要的數據對象
// 將數據與結構結合
// 數據: newArr [{name: ,type:'',size:'',time:''}]
// 結構:data
// 問題:如何裝飾數據與結構結合:
// 找第三方包:art-template可以用來幫助我們完成這個操作
var htmlStr = template.render(data.toString(), {
newArr: newArr
});
res.end(htmlStr);
}
});
})(i);
}
})
});
}
})
// 開啟服務器
server.listen(3000, function () {
console.log('running');
});
3、 Node中的模塊系統
(1)什么是模塊?
- 隨著項目的推進、邏輯的復雜,不可能把所有js代碼都寫在一個文件中
- 一個js文件就是一個具有獨立功能的模塊
- Node中按照模塊去劃分功能,有一套模塊加載機制:
- module.exports導出模塊成員
- require()導入模塊成員
(2)導入模塊(require方法)
作用:加載并執行模塊中的代碼!!!!
-
使用:
require()
方法的()中可以寫:// 1. node自帶的模塊,如fs、http require('fs'); // 2. 通過路徑引入的自己的js文件 require('./foo.js');
注意事項
①
require()
加載模塊是同步加載!!② 引入自己的js文件時,路徑中的./或者../不能省略
③ 如果省略,
require()
會把它當成是一個node自帶的模塊報錯信息不要害怕!!!一定要去看!!!
④ 模塊后綴
.js
可以省略不寫
(3)導出模塊
模塊中定義的變量是局部的,
那如何在A模塊使用B模塊中定義好的變量呢?
這個問題, 就涉及到了模塊加載機制
導出模塊中的變量
- 每個模塊中都有一個
module
對象 -
module
對象中有一個exports
對象 - 在CommonJS 中規定:一個模塊返回數據可以通過module.exports和rexports兩個關鍵字來返回
- 模塊最終返回的僅僅只是 modules.exports
- exports 僅僅只是 modules.exports的一個引用
- 我們可以把需要導出的成員都掛載到
module.exports
對象上
驗證module.exports與exports的關系:
1)console.log(module.exports === exports);
2)exports.a = 123;
3)exports = fucntion() {}
4)exports = modules.exports = fucntion() {}
導入模塊foo.js
var a = 10;
var b = 20;
function add(){};
module.exports.a = a;
module.exports.b = b;
module.exports.add = add;
// 可以理解為在模塊的末尾有這樣一句代碼: return module.exports;
// return module.exports;
導入模塊main.js
// 1 導入模塊
var foo = require('./foo.js');
// 2 使用模塊中的成員
console.log(foo.add);
console.log(foo.a);
(4)模塊加載機制
-
require關鍵字:
- 可以幫助一個模塊加載另一個模塊
-
優先從緩存中加載
- Node 加載模塊時,如果這個模塊已經被加載過了,則會直接緩存起來,將來再次引用時不會再次加加載這個模塊(即:如果一個模塊被加載兩次,則模塊中的代碼只會被執行一次)
- 加載模塊本質上是加載模塊中的modules.exports值
-
核心模塊
- 先去緩存中看下是否存在,如果有,直接拿來使用
- 如果沒有,則加載
-
自定義模塊
- 以 './' 或者 '../' 或者 'c:/xxx' 類似于這樣的標識路徑作為加載名
-
第三方模塊:包
- 先在當前文件的模塊所屬目錄去找 node_modules目錄
- 如果找到,則去該目錄中找 moment 目錄
- 如果找到 moment 目錄, 則找該目錄中的 package.json文件
- 如果找到 package.json 文件,則找該文件中的 main屬性
- 如果找到main 屬性,則拿到該屬性對應的文件
- 如果找到 moment 目錄之后,
- 沒有package.json
- 或者有 package.json 沒有 main 屬性
- 或者有 main 屬性,但是指向的路徑不存在
- 則 node 會默認去看一下 moment 目錄中有沒有 index.js ,index.node, index.json 文件
- 如果找不到index 或者 找不到 moment 或者找不到 node_modules
- 則進入上一級目錄找 node_moudles 查找(規則同上)
- 如果上一級還找不到,繼續向上,一直到當前文件所屬磁盤的根目錄
- 如果到磁盤概目錄還沒有找到,直接報錯
核心模塊: 由 Node 本身提供,通過唯一的模塊標識名進行加載 核心模塊本質上也是文件模塊,它已經被編譯到可執行文件中了 第三方模塊 用戶自定義模塊
-
模塊的兼容處理
- 有些模塊即能在瀏覽器端使用,又能在服務器端使用,是因為它們作了兼容處理(如:moment)
(5)模塊分類及第三方模塊(包)的使用
- 核心模塊
- 由
Node
本身提供,例如fs
、http
等
- 由
- 自定義(用戶)模塊
- 自己寫的.js文件,按照路徑來加載,注意
./
或者../
不能省略
- 自己寫的.js文件,按照路徑來加載,注意
- 第三方模塊
- 在npm網站找包(模塊)
- 打開cmd, 使用命令
npm install 包名
安裝包 - 在需要使用的位置, 通過
require('第三方包名')
加載包 - 看文檔調用 API
4、art-template 第三方包
(1)使用art-template
// 1、遍歷對象
// var template = require('art-template');
// // 有html結構
// var str = '<ul><li><%=name%></li><li>{{age}}</li></ul>';
// // 也有數據
// var obj = {
// name: '張三',
// age: 18
// };
// // 結合
// // render可以結合html結構和數據
// // str:結構
// // obj:數據
// var htmlStr = template.render(str, obj);
// console.log(htmlStr);
// 2、遍歷數組
var template = require('art-template');
// 準備結構
var str = '<ul>{{each arr}}<li>{{$index}}----{{$value.name}}----{{$value.age}}</li>{{/each}}</ul>';
// 數據
var arr = [
{name: '張三', age: 18},
{name: '李四', age: 30}
]
// 結合
var htmlStr = template.render(str, {
arr: arr
});
console.log(htmlStr);
//需要結合
// art-template的使用步驟:
// 1、下載
// 2、在html結構中書寫art-template的語法
// 輸出:
// {{name}}
// 條件:
// {{if 條件}} ... {{else if 條件}}... {{else}}
// 循環
// {{each 數據}} {{$index}} {{$value}} {{/each}}
// 3、通過nodejs代碼將結構與數據結合起來
(2)使用url核心模塊來將參數轉為對象
// url核心模塊可以幫助我們將路徑后面的參數得到
// url: http://192.168.11.1:8080/abc/index.html?name=abc&age=18
// 引用
var url = require('url');
// pase路徑
var strUrl = 'http://192.168.11.1:8080/abc/index.html?name=abc&age=18';
var urlObj = url.parse(strUrl, true);
console.log(urlObj);
5、npm包管理器
(1) npm 是什么
npm 全稱
Node Package Manager
,它的誕生是為了解決 Node 中第三方包共享的問題。 和瀏覽器一樣,由于都是 JavaScript,所以前端開發也使用 npm 作為第三方包管理工具。 例如大名鼎鼎的 jQuery、Bootstrap 等都可以通過 npm 來安裝。 所以官方把 npm 定義為JavaScript Package Manager
。yarn也是包管理器
(2)npm 命令行工具
-
只要你安裝了 node 就已經安裝了 npm,可以在cmd中進行npm的指令操作
// 檢驗npm是否安裝成功 npm --version
-
常用命令
// 在項目中初始化一個 package.json 文件,凡是使用 npm 來管理的項目都會有這么一個文件 // 只敲一次就可以! npm init // 跳過向導,快速生成 package.json 文件,簡寫是npm init -y npm init --yes // 一次性安裝 dependencies 中所有的依賴項,簡寫是 npm i npm install // 安裝指定的包,可以簡寫為 npm i 包名 // npm 5 以前只下載,不保存依賴信息,如果需要保存,則需要加上 --save 選項 // npm 5 以后就可以省略 --save 選項了 npm install 包名 // 一次性安裝多個指定包 npm install 包名 包名 包名 ... // 安裝指定版本的包!!! npm install jquery@版本號 // 如果不指定版本號,默認安裝最新穩定版本 // 卸載指定的包 npm uninstall 包名 // 查看使用幫助 npm help // 查看某個命令的使用幫助 // 例如我忘記了 uninstall 命令的簡寫了,這個時候,可以輸入 npm uninstall --help 來查看使用幫助 npm 命令 --help
(3)package.json
我們的項目會放到云端的倉庫中,例如 github ,第三方包沒有上傳的意義,我們只需要把我們的源碼放到云端倉庫,
node_modules
目錄中存儲的就是第三方包(不用擔心丟失問題),如果沒有package.json
文件則你就找不回來了。建議每一個項目都要有一個
package.json
文件(包描述文件,就像產品的說明書一樣),給人踏實的感覺最重要的就是保存這個項目的第三方依賴信息(因為我們不需要提交第三方包到我們的云端倉庫,只需要提交我們自己的代碼),有了這個文件中的依賴信息結合npm install
命令我們就可以放心了。-
這個文件可以通過
npm init
的方式來自動初始化出來。npm init
對于咱們目前來講,最有用的是那個
dependencies
選項,可以用來幫我們保存第三方包的依賴信息。如果你的
node_modules
刪除了也不用擔心,我們只需要:npm install
就會自動把package.json
中的dependencies
中所有的依賴項都下載回來。-
建議每個項目的根目錄下都有一個
package.json
文件- 不同的項目有不同依賴,各自保存各自的
-
執行 npm install 包名的的時候可以加上--save
--save
這個選項,目的是用來保存依賴項信息
- npm 5 以前不會自動保存依賴信息到 package.json 文件中,必須手動加
--save
選項才可以 - npm 5 以后不需要加
--save
選項了,因為它會自動保存依賴項
- npm 5 以前不會自動保存依賴信息到 package.json 文件中,必須手動加
(4) package-lock.json
- npm 5 以前是不會有
package-lock.json
這個文件的。 - 當你安裝包的時候,npm 都會生成或者更新
package-lock.json
這個文件。 - npm 5 以后的版本安裝包不需要加
--save
參數,它會自動保存依賴信息 - 當你安裝包的時候,會自動創建或者是更新
package-lock.json
這個文件 -
package-lock.json
這個文件會保存node_modules
中所有包的信息(版本、下載地址)- 這樣的話重新
npm install
的時候速度就可以提升
- 這樣的話重新
三、ECMAScript 6
一般情況下不建議在window環境下面使用ES6
1、 嚴格模式
'use strict'
- 如果開啟了嚴格模式,變量不能直接使用,必須先申明,申明變量時一定要用var / let
- 更多介紹:http://www.ruanyifeng.com/blog/2013/01/javascript_strict_mode.html
2、 申明一個變量(let)
-
let 申明的變量不存在變量提升
//console.log(a); // undefined //var a = 123; //console.log(a); // 123 //等同于 var a; console.log(a); a = 123; console.log(a); console.log(a); let a = 123; console.log(a);
-
let 申明的變量不允許重復聲明
var a = 123; var a = 456; console.log(a); let a = 123; let a = 456; console.log(a);
-
let 申明的變量存在塊級作用域
- 可以用來解決閉包問題
// 塊級作用域:來自于后臺 // 特點:凡是大括號包裹的部分都是一個單獨的作用域,我們把這個作用域叫做塊級作用域, 塊級作用域之間不會相互影響 if (true) { let a = 123; console.log(a); } // if (true) { // console.log(a); // } console.log(a);
3、 申明一個常量 (const)
-
contst申明的常量:一旦設置無法修改
- 可以改變對象中的屬性
-
contst申明的常量:一旦設置不可重復聲明
// const a = 123; // a = 456; // console.log(a); // 值類型(簡單類型) // 簡單類型的常量一旦設置無法修改 // 引用類型(復雜類型) // 一旦賦值只要不是改變引用還是可以修改的 const obj = { name: "張三" } // 修改引用類型的引用地址 // obj = {};// 如果修改引用地址會報錯 obj.name = "李四";// 修改的是地址中對應對象的屬性: console.log(obj);
4、 字符串的一些擴展方法的使用
includes() :返回布爾值,表示是否找到了參數字符串
startsWith() :返回布爾值,表示是否找到了參數字符串
endsWith() :返回布爾值,表示參數字符串是否在源字符串的尾部
-
repeat():返回一個新字符串,表示將原字符串重復n次
var s = "hello world"; s.startsWith('hello') //true s.startsWith('world', 6) //true ,表示從第6個開始后面的字符是 world s.endWith('hello', 5) //true ,表示前5個字符是hello 'x'.repeat(2) // “xx” 'hello'.repeat(2) // “hellohello” 'ivan'.repeat(0) // “”
5、 模板字符串
使用“`”來定義模板字符串
在模板字符串中可以保持變量的結構,可以識別換行
-
在模板字符串中可以直接使用js代碼格式:${ code }
var obj = { name: '張三', age: 18 } // var a = '<ul><li>' + obj.name + '</li><li>' + obj.age + '</li></ul>'; // console.log(a); // 模板字符串: // 1.0 拼接字符:輸出變量 // var a = `<ul><li> ${obj.name} </li><li> ${obj.age} </li></ul>`; // console.log(a); // 2.0 識別換行 var a = `<ul> <li>${obj.name}</li> <li>${obj.age}</li> </ul>`; console.log(a);
6、 解構賦值
(1) 字符串的解構賦值
- var [a,b,c] = 'xyz';
const [a, b, c, d, e] = 'hello';
a // "h"
b // "e"
c // "l"
d // "l"
e // "o"
(2)對象解構
- var {name, age} = {name: 'zs', age: 18}
- 給對象屬性設置別名
- var {name: myName, age} = {name: 'zs', age: 18}
- 屬性設置默認值(如果對旬中有這個屬性,并且值不為undefined,那么這個屬性的值為對象中
- var {name='ls', age} = {name: 'zs', age: 18}的值,如果不存在或者是值為undefined,那么值為:默認值)
var obj = {
name: '張三',
age: 18
}
var {name, age} = obj;
name // 張三
age // 18
- 無順序問題
(3) 數組的解構
- var [x,y,z] = [1,2,3];
var arr = ['a', 'b', 'c']
var [x, y, z] = arr;
x // 'a'
y // 'b'
z // 'c'
- 有順序問題
- 被賦值的變量和數組中的元素個數可以不相等
(4)其它特點
- 可以指定默認值
- 解構出來的元素不一定要與前面的對象一一對應
- 可以給解構出來的元素設置別名
7、 箭頭函數的推演
-
作用:在nodejs中我們大量使用了回調函數,每次回調函數都用匿名函數來實現,ES6中為了能夠讓我們書寫匿名函數速度加快,就提出了箭頭函數,用來簡化匿名函數
寫法1: arr.sort(function(x, y){return x - y ;}); 寫法2:arr.sort((x, y) => {return x - y ;}); 寫法3:arr.sort((x, y) => x - y);
-
箭頭函數的其它寫法
如果參數只有一個,可以將()省略 // arr.map(c => c + 1); 如果沒有參數,則一定能要寫上() // ()=> console.log(‘a’) 如果多于一個參數,每個參數之間用逗號分隔 (x, y) => { ... } 如果方法體只有一句代碼,可以省略{} 和分號,如果有返回可以省略return 如果方法體多于一句代碼,則不能省略{} ,每句代碼使用 分號分隔
-
箭頭函數的一些特點
-
箭頭函數沒有自己的this,函數體內部寫的this,指向的是外層代碼塊的this
//匿名函數 var obj = { name: '張三', age: 18, sayHi: function(){ console.log(`大家好,我叫${this.name},今年${this.age}歲了`); } } obj.sayHi(); // 箭頭函數 this.name = '李四'; this.age = 20; // window var obj = { name: '張三', age: 18, sayHi: () => {// => goes to console.log(`大家好,我叫${this.name},今年${this.age}歲了`); } } obj.sayHi();
-
箭頭函數內部的this是定義時所在的對象,而不是使用時所在的對象并且不會改變
// 匿名函數 var obj = { name: '張三', age: 18, sayHi: function(){ console.log(`大家好,我叫${this.name},今年${this.age}歲了`); } } var newObj = { name: '李四', age: 40 } // 可以通過apply和call動態改變this的指向 obj.sayHi.apply(newObj); // 箭頭函數 this.name = '李四'; this.age = 30; var obj = { name: '張三', age: 18, sayHi: ()=>{ console.log(`大家好,我叫${this.name},今年${this.age}歲了`); } } var newObj = { name: '范冰冰', age: 40 } // 函數在定義時this指向的是obj上一層中的this name = '王五' // obj.sayHi(); // 可以通過apply和call動態改變this的指向 // 調用時使用newObj來調用,并沒有改變this的指向 obj.sayHi.apply(newObj);
-
箭頭箭頭函數不能用作構造函數
// 普通構造函數 var GirlFirend = function(name, age){ this.name = name; this.age = age; } var g1 = new GirlFirend('張三', 30); console.log(g1.name, g1.age); var GirlFirend = (name, age) => { this.name = name; this.age = age; } var g2 = new GirlFirend('李四', 39); console.log(g2.name, g2.age);
-
箭頭函數內部不存在arguments,箭頭函數體中使用的arguments其實指向的是外層函數的arguments
// 什么是arguments:函數在使用時的實參 // // 普通函數 // var fn = function () { // // 將所有的傳入的實參打印出來 // console.log('-----fn-------'); // console.log(arguments); // console.log('-----fn-------'); // return function () { // console.log('-----fn1-------'); // console.log(arguments); // console.log('-----fn1-------'); // } // } // var fn1 = fn(1, 2, 3, 4, 5, 6, 7, 8, 9, 0); // fn1(1, 2, 3, 4, 5); // // fn 1,2,3,4,5,6,7,8,9,0 // // fn1 1,2,3,4,5 // 箭頭函數 var fn = function () { // 將所有的傳入的實參打印出來 console.log('-----fn-------'); console.log(arguments); console.log('-----fn-------'); return () => { console.log('-----fn1-------'); console.log(arguments); console.log('-----fn1-------'); } } var fn1 = fn(1,2,3,4,5,6,7,8,9,0); fn1(1,2,3,4,5);
-
8、 對象中屬性的簡寫
//es5中對象: {add:add, substrict:substrict}
//es6中對象: {add, substrict} 注意這種寫法的屬性名稱和值變量是同一個名稱才可以簡寫,否則要想es5那樣的寫法,例如: {addFun:add}
var name = '李四';
var age = 20;
// ES6之前
var obj = {
name: name,
age: age
}
//ES6之后
var obj = {
name,
age
}
console.log(obj.name, obj.age);
9、 對象中方法的簡寫
//es5中對象: {add:function(){}, substrict:function(){}}
//es6中對象: {add(){}, substrict(){}}
//ES6之前
var obj = {
name,
age,
sayHi: function(){
console.log(`大家好,我叫${this.name}, 今年${this.age}歲了`);
}
}
// ES6之后
var obj = {
name,
age,
sayHi(){
console.log(`大家好,我叫${this.name}, 今年${this.age}歲了`);
}
}
obj.sayHi();
10、promise
含義:承諾(答應去做,但是還沒有做)
來由:ES6之前是沒有promise的,這個玩意是由前端社區提出,在ES6中成為一個標準
作用:它是用來簡化回調函數的多層嵌套
-
使用:
1)要使用promise必須先根據promise構造函數創建一個函數對象 在promise的構造函數中有兩個參數:resolve reject 這兩個參數是兩個函數 這兩個參數是與promise中的狀態對應: pending(進行中) fulfilled(已成功) rejected(已失敗) 狀態的切換有兩種方式: pending---->fulfilled 操作成功 --->會執行resolve中的方法 pending---->rejected 操作失敗 --->會執行reject中的方法 在使用promise的時候可以在后面接無數個then方法,then方法可以傳遞參數 參數一般分為三種類型: 1)undefined 2)普通數據:字符串,數字,boolean,數組,對象 3)promise對象 2)使用then方法執行promise 3)在promise中提供一個統一處理錯誤的方法:catch
-
創建一個promise 對象
var p = new Promise(function(resolve, reject){ if () { // 如果成功執行reject } else{ // 如果失敗執行resolve } });
- resolve: 成功后執行
- reject: 失敗后執行
執行promise的代碼:使用p.then()方法執行
- then(resolve, reject ) 方法可以執行promise的代碼
- 特點:可以無限調用then方法
- 統一處理捕獲異常的方法: catch()
-
then方法返回值:
- 每個then方法執行完成以后,會將返回值交給下一個then方法,如果沒有返回值,其實返回的是undefined
- 沒有返回值
- 如果沒有返回值,返回值為undefined
- 返回值為:數字,字符串,對象,數組....
- 下面的then方法可以直接通過resolve方法的參數接收到
- 返回值為一個新的promise對象:
- 將來下面緊跟的then方法其實是執行上面返回的promise對象的then方法
-
// 需求:依次將三個文件中的內容讀取出來并且輸出: 0 ~ 1 ~ 2
var fs = require('fs');
// 讀取0文件
fs.readFile('./file/0.txt', function (err0, data0) {
if (err0) {
return console.log(err0.message);
}
console.log(data0.toString());
// 讀取1文件
fs.readFile('./file/1.txt', function (err1, data1) {
if (err1) {
return console.log(err1.message);
}
console.log(data1.toString());
// 讀取2文件
fs.readFile('./file/2.txt', function (err2, data2) {
if (err2) {
return console.log(err2.message);
}
console.log(data2.toString());
});
});
});
// 回調地獄
使用promise解決回調地獄
// var p = new Promise(function(reject, resolve){
// if () {
// // 如果成功執行reject
// } else{
// // 如果失敗執行resolve
// }
// });
// // 需求:依次將三個文件中的內容讀取出來并且輸出: 0 ~ 1 ~ 2
// 創建一個promise對象讀取0文件
var fs = require('fs');
// 一旦Promise被創建:
// promise的狀態為: pending 等待
// promise執行以后,狀態會發生改變
// pending -- fulfilled 說明執行成功 執行resolve方法
// pending -- rejected 說明執行失敗 執行reject
// 一旦對應的狀態確定,就無法改變
var p0 = new Promise(function (resolve, reject) {
fs.readFile('./file/0.txt', function (err, data) {
if (err) {
reject(err);
} else {
resolve(data);
}
});
});
// 讀取1文件
var p1 = new Promise(function (resolve, reject) {
fs.readFile('./file/1.txt', function (err, data) {
if (err) {
reject(err);
} else {
resolve(data);
}
});
});
// 調用:than
p0
.then(data => {
console.log(data.toString());
// 不返回值任何內容:返回值 為undefined
})
.then((res) => {
console.log(res); // undeinfed
console.log('第二個then方法');
// 返回值為非promise對象
return 'abc';
})
.then((str) => {
console.log(str);// abc
console.log('第三個then方法');
// 返回值為promise對象
return p1;
})
.then((data) => {
console.log(data.toString());
console.log('第四個then方法');
});
四、Express
- 作者:https://github.com/tj
- Github:https://github.com/expressjs/express
- 官網:http://expressjs.com/
- 中文翻譯:http://www.expressjs.com.cn/
- awesome-express: https://github.com/wabg/awesome-express
1、起步
安裝:
npm install --save express
hello world:
// 引入express第三方包
var express = require('express')
// 創建一個express對象(express用來搭建web服務:相當于創建一了個服務器對象)
var app = express()
// 將來瀏覽器發送get請求,請求網站的根目錄,會執行后面的回調函數
// 回調函數有兩個參數:
// req:請求對象
// res:響應對象
app.get('/', function (req, res) {
res.send('Hello World!')
})
// 開啟監聽:監聽3000商品,
// 當開啟成功以后會執行后面的回調函數
app.listen(3000, function () {
console.log('Example app listening on port 3000!')
})
基本路由:
app.get('/', function (req, res) {
res.send('Hello World!')
})
app.post('/', function (req, res) {
res.send('Got a POST request')
})
app.put('/user', function (req, res) {
res.send('Got a PUT request at /user')
})
app.delete('/user', function (req, res) {
res.send('Got a DELETE request at /user')
})
2、Express 中外置路由使用
router.js 文件代碼如下:
// 1. 加載外置路由 express 模塊
var express = require('express')
// 2. 調用 express.Router() 方法,得到一個路由容器實例
var router = express.Router()
// 3. 為 router 添加不同的路由
router.get('/', function (req, res) {
res.send('hello express')
})
router.get('/add', function (req, res) {
res.send('add')
})
// 4. 將 router 路由容器導出(路由對象暴露給外界)
module.exports = router
在 app.js 文件中:
// 使用express搭建服務
var express = require('express')
// 1. 加載路由模塊
var router = require('./router')
var app = express()
// 2. 將路由模塊導出的路由容器 router 通過 app.use() 方法掛載到 app 實例上
// 這樣的話 app 實例程序就擁有了 router 的路由了
app.use(router)
// 把所有的路由添加到一個新的文件中(外置路由)
// 開啟監聽
app.listen(3000, function () {
console.log('running...')
})
3、res.render方法
- 使用:
- 1.0 下載一個第三方包express-art-template(art-template)
- 2.0 將這個第三方包注冊到express中:
- app.engine('html', require('express-art-template'));
- 3.0 可以直接使用render方法了
- 注意點:
- 1.0 render方法渲染的靜態文件默認會去views文件夾下去找,所以存放靜態文件的文件夾,一定要取名叫做views
- 2.0 默認情況下render渲染的靜態文件后綴為art,但是在注冊art-template時可以修改為html
- 注意點:
- res中雖然已經有了render方法但是不能夠直接使用,它需要配合一些第三方包來進行使用(art-template的來使用)
index.js
// res.render方法用來渲染html文件的
var express = require('express');
var app = express();
// 注冊 art-template
app.engine('html', require('express-art-template'));
// 注冊路由
app.get('/', (req,res) => {
// 將靜態頁面響應回瀏覽器
// 特點:它在渲染的時候會去一個固定的路徑下面去找對應的art文件
res.render('index.html', {
name: '張三',
age: 18
});
})
app.listen(3000, () => {
console.log('running');
});
index.html
<body>
<ul>
<li>{{name}}</li>
<li>{{age}}</li>
</ul>
</body>
4、在 Express 中處理靜態資源
- 默認情況下express沒有幫助我們處理靜態文件,如果要處理靜態文件,我們需要在express中進行設置
- 設置:
- 訪問是不帶路徑
- app.use(express.static('public'))
- public 是靜態文件存放的位置
- app.use(express.static('public'))
- 按照原路徑來訪問
- app.use('/imgs', express.static('public'))
- 在訪問時必須帶上/imgs才能訪問到圖片
- 訪問的路徑自己來設置
- app.use('/abc', express.static('public'))
- 訪問是不帶路徑
app.use(express.static('public'))
app.use(express.static('files'))
app.use('/public', express.static('public'))
app.use('/aaa', express.static('public'))
app.use('/static', express.static(path.join(__dirname, 'public')))
index.js
// 引入express第三方包
var express = require('express');
// 創建一個express對象(express用來搭建web服務:相當于創建一了個服務器對象)
var app = express();
// 設置靜態文件的路徑
// app.use(express.static('imgs')); // 設置完成以后,將來可以訪問imgs中的靜態文件
// app.use('/imgs', express.static('imgs'));
app.use('/abc', express.static('imgs'));
// 注冊模板引擎
app.engine('html', require('express-art-template'));
// 渲染靜態文件
app.get('/', function (req, res) {
res.render('index.html');
});
app.listen(3000, function () {
console.log('running');
});
index.html
<body>
<img src="/abc/4.jpg" alt="">
</body>
5、express 的中間件
-
什么是中間件:
- 在express已有的結構上再加入一些自己的代碼
-
參數:
- req:請求報文對象
- res:響應報文對象
- next:函數,如果調用這個函數才會繼續的執行后續的流程代碼
-
應用中間件
- app.use()
- app.use(function(){})
- 無論發送任何請求都會執行的中間件
- app.use('/path', function(){})
- 只要在請求path路由時才會執行的中間件(無論GET/POST)
- app.use(function(){})
- app.method()
- app.get()
- 在get請求時會執行的中間件
- app.post()
- 在post請求時會執行的中間件
- app.get()
var express = require('express'); var app = express(); // 1、app.use 中間件 // 特點:由于在設置的時候沒有加上任何參數,所以在這里任意請求過來都會執行這個中間件 app.use(function(req, res, next){ console.log('我是app.use'); // 調用next執行后續的邏輯代碼 next(); }); // 2、app.use 的變形 // 特點:只要在請求/add的時候才會執行的中間件(不管是GET/POST) app.use('/add', function(req, res, next){ console.log('我是app.use /add'); next(); }); // 3、app.METHOD = GET / POST // (1)app.get 在express中其實路由就是一種中間件 app.get('/add', function(req, res, next){ console.log('我是中間件app.get /add'); next(); }) // (2)app.post app.post('/add', function(req, res, next){ console.log('我是中間件app.post /add'); next(); }); app.get('/', function(req, res){ console.log('根目錄'); }); // 設置的路由 app.get('/add', function(req, res){ console.log('add'); }); app.post('/add', function(req, res){ console.log('add'); }); app.get('/del', function(req, res){ console.log('del'); }); app.listen(3000, function() { console.log('running'); });
- app.use()
-
路由級中間件
- 它的用法與應用級中間件完全一樣,唯一的區別是
- 應用級中間件的中間件是掛載在app對象上
- 路由級中間件的中間件是掛載在router對象上的
app.js
// 開戶服務器 var express = require('express'); var router = require('./router'); var app = express(); //使用外置路由 app.use(router); app.listen(3000, () => { console.log('running'); });
router.js
var express = require('express'); var router = express.Router(); router.use(function(req, res, next){ console.log('router.use'); next(); }); router.use('/add', function(req, res, next){ console.log('router.use /add'); next(); }); router.get('/add', function(req, res, next){ console.log('router.get /add'); next(); }); router.get('/', function(req, res){ console.log('根目錄'); }); router.get('/add', function(req,res){ console.log('get/add'); }); // 暴露到外界 module.exports = router;
- 它的用法與應用級中間件完全一樣,唯一的區別是
-
錯誤處理中間件
- 可以用來處理網站發生的所有錯誤
var express = require('express'); var fs = require('fs'); var app = express(); app.get('/', function(req, res){ console.log('根目錄'); }); app.get('/add', function(req, res){ // 會報錯 res.render('add.html'); }); // 統一錯誤處理的中間件 app.use((err, req, res, next) => { // console.log("err:" + err.message); fs.readFile('./404.html', (err, data) => { res.end(data); }); }); app.listen(3000, function() { console.log('running'); });
-
內置中間件(靜態資源處理)
- express.static(root,option)
五、使用nodejs來操作數據庫
1、連接數據庫
// 使用nodejs來鏈接mysql
// 1、引包
var mySql = require('mysql');
// 2、設置連接參數
var connection = mySql.createConnection({
host: 'localhost',
user: 'root',
password: '123456',
database: 'class_one'
});
// 3、開啟連接
connection.connect();
// 4、執行sql語句
var strSql = 'SELECT * FROM person';
connection.query(strSql, (err, result, fields) => {
if (err) {
return console.log(err.message);
}
console.log(result);
// [ RowDataPacket { id: 1, name: '張三', age: 18, gender: '男' },
// RowDataPacket { id: 3, name: '王五', age: 22, gender: '男' } ]
console.log(fields);
// 存儲的是表中字段的信息
});
2、增刪改查
// 引包
var mysql = require('mysql');
// 建立連接
var connection = mysql.createConnection({
host: 'localhost',
user: 'root',
password: '123456',
database: 'class_one'
});
// 開啟連接
connection.connect();
// 執行sql說到語句
// 查詢
// result: 就是數據表中的數據
// fields: 數據表中的字段的屬性說明
var strSql = 'SELECT * FROM person';
// 新增
// result: OkPacket對象,這是一個成功對象,說明對數據庫的操作成功了
// affactedRows: 受影響的行數
// fields: undeinfed
var strSql = 'INSERT INTO person (name, age, gender) VALUES ("趙六", "33", "男")';
// 修改
// result: OkPacket對象,這是一個成功對象,說明對數據庫的操作成功了
// affactedRows: 受影響的行數
// fields: undeinfed
var strSql = 'UPDATE person SET name="無情", age="80" WHERE id = 1'
// 刪除
// result: OkPacket對象,這是一個成功對象,說明對數據庫的操作成功了
// affactedRows: 受影響的行數
// fields: undeinfed
var strSql = 'DELETE FROM person WHERE id = 5'
connection.query(strSql, (err, result, fields) => {
if (err) {
return console.log(err.message);
}
console.log(result);
console.log(fields);
});
六、cookie和session
1、cookie
- nodejs 中設置cookie
- res.wirteHead(200, {'set-cookie', 'uName=admin'});
- 如果要設置硬盤cookie可以使用第三方中間件express: cookie
- 可以通過這個中間件來操作cookie
- 設置過期時間
- 設置起效果的域名
- 。。。
- 可以通過這個中間件來操作cookie
server.js(服務器)
var fs = require('fs');
var express = require('express');
var bodyParser = require('body-parser')
var app = express();
app.use(bodyParser.urlencoded({ extended: false }))
app.use(bodyParser.json())
// 設置靜態文件
app.use('/node_modules', express.static('node_modules'));
// 1.0 設置路由
// 設置一個首頁
app.get('/', function(req, res){
// 請求頭會帶上一個cookie
// console.log(req.headers.cookie);
// 驗證用戶是否登錄:只要判斷請求頭中是否帶有cookie
if (req.headers.cookie === "uName=admin") {
// 說明登錄過:
res.send('用戶已經登錄過了,直接訪問根上當');
} else{
res.send('<script>alert("用戶還沒有登錄");window.location="/login"</script>');
}
});
// 得到登錄頁面
app.get('/login', function(req, res){
fs.readFile('./views/login.html', function(err, data){
res.end(data);
});
});
// 完成提交數據的邏輯
app.post('/login', function(req, res){
// body-parser
var uName = req.body.uName;
var pwd = req.body.pwd;
// 判斷
if (uName === "admin" && pwd === "888") {
// 要將用戶的登錄信息保存起來:用cookie來保存
// 向響應報文頭中加入一段內容:cookie
res.writeHead(200, {
'set-cookie': 'uName=' + uName
});
// 登錄成功
// res.json({
// status: 200,
// msg: '成功'
// });
res.end(JSON.stringify({
status: 200,
msg: '成功'
}));
} else{
// 登錄失敗
res.json({
status: 500,
msg: '不成功'
});
}
});
app.listen(3000, function() {
console.log('running');
});
./views/login.html(客戶端)
<style>
table {
width: 400px;
height: 200px;
border-collapse: collapse;
margin: 0 auto;
}
td {
border: 1px solid #ccc;
}
</style>
<body>
<form id="form">
<table>
<tr>
<td>用戶名:</td>
<td><input type="text" id="uName" name="uName"></td>
</tr>
<tr>
<td>密 碼:</td>
<td><input type="text" id="pwd" name="pwd"></td>
</tr>
<tr>
<td></td>
<td><input type="button" id="login" value="登錄"></td>
</tr>
</table>
</form>
</body>
<script src="../node_modules/jquery/dist/jquery.min.js"></script>
<script>
// 給登錄按鈕注冊事件
$("#login").on('click', function (e) {
e.preventDefault();// 阻止默認事件
// 得到參數
var params = $("#form").serialize();
// var data = `uName=${$('#uName').val()}&pwd=${$('pwd').val()}`;
// 將參數提交到服務器
var ajax = $.ajax({
url: '/login',
type: 'POST',
data: params,
dataType: 'JSON'
});
// promise
ajax.then(data => {
// 判斷狀態
if (data.status === 200) {
alert(data.msg);
// 跳轉到首頁
window.location = '/'
} else {
// 跳轉到登錄頁面
alert(data.msg);
window.location('/login');
}
});
})
</script>
2、session
-
nodejs中不能直接使用session,如果要使用必須借助express的第三方中間件:cookie-session
-
使用步驟:
下載:npm i cookie-session
引用:var cookieSession = require('cookie-session');
-
注冊:
app.use(cookieSession({ name: 'session', keys: ['key1', 'key2'] }))
-
使用
- 設置: req.session.鍵= 值;
- 取值: req.session.鍵
-
server.js(服務器)
var fs = require('fs');
var express = require('express');
var bodyParser = require('body-parser')
var cookieSession = require('cookie-session');
var app = express();
app.use(bodyParser.urlencoded({ extended: false }))
app.use(bodyParser.json())
// 設置靜態文件
app.use('/node_modules', express.static('node_modules'));
// 注冊cookiesession
app.use(cookieSession({
name: 'session',
keys: ['key1', 'key2']
}))
// 1、設置路由
// 設置一個首頁
app.get('/', function(req, res){
// 得到session
// console.log(req.session.uName);
var session = req.session.uName;
if (session) {
res.send('用戶已經登錄過一');
} else {
res.send('<script>alert("用戶還沒有登錄");window.location="/login"</script>');
}
});
// 得到登錄頁面
app.get('/login', function(req, res){
fs.readFile('./views/login.html', function(err, data){
res.end(data);
});
});
// 完成提交數據的邏輯
app.post('/login', function(req, res){
// body-parser
var uName = req.body.uName;
var pwd = req.body.pwd;
// 判斷
if (uName === "admin" && pwd === "888") {
// 要將用戶的登錄信息保存起來:用session來保存
req.session.uName = uName;
res.end(JSON.stringify({
status: 200,
msg: '成功'
}));
} else{
// 登錄失敗
res.json({
status: 500,
msg: '不成功'
});
}
});
app.listen(3000, function() {
console.log('running');
});
./views/index.html(客戶端)
<body>
<form id="form">
<table>
<tr>
<td>用戶名:</td>
<td><input type="text" id="uName" name="uName"></td>
</tr>
<tr>
<td>密 碼:</td>
<td><input type="text" id="pwd" name="pwd"></td>
</tr>
<tr>
<td></td>
<td><input type="button" id="login" value="登錄"></td>
</tr>
</table>
</form>
</body>
<script src="../node_modules/jquery/dist/jquery.min.js"></script>
<script>
// 給登錄按鈕注冊事件
$("#login").on('click', function (e) {
e.preventDefault();// 阻止默認事件
// 得到參數
var params = $("#form").serialize();
// var data = `uName=${$('#uName').val()}&pwd=${$('pwd').val()}`;
// 將參數提交到服務器
var ajax = $.ajax({
url: '/login',
type: 'POST',
data: params,
dataType: 'JSON'
});
// promise
ajax.then(data => {
// 判斷狀態
if (data.status === 200) {
alert(data.msg);
// 跳轉到首頁
window.location = '/'
} else {
// 跳轉到登錄頁面
alert(data.msg);
window.location('/login');
}
});
})
</script>