本篇文章通過超多代碼和圖解來帶大家深入解析Node.js,主要內(nèi)容包括模塊化處理、包的基本應(yīng)用、Express、跨域、操作Mysql數(shù)據(jù)庫等,希望對(duì)大家有所幫助!
一、Node.js簡(jiǎn)介
1.1什么是Node.js
Node.js
是一個(gè)調(diào)用內(nèi)置ApI
并且基于Chrome V8
引擎的js運(yùn)行環(huán)境,之前自己在本地總結(jié)了一些零散的只知識(shí)點(diǎn),今天整合一下發(fā)出來。
官網(wǎng)地址: https://nodejs.org/zh-cn/
1.2 Node.js可以做什么
①基于 Express
框架(http://www.expressjs.com.cn/),可以快速構(gòu)建 Web 應(yīng)用?!鞠嚓P(guān)教程推薦:nodejs視頻教程、編程教學(xué)】
②基于 Electron
框架(https://electronjs.org/),可以構(gòu)建跨平臺(tái)的桌面應(yīng)用
③基于restify
框架(http://restify.com/),可以快速構(gòu)建 API 接口項(xiàng)目
④讀寫和操作數(shù)據(jù)庫
、創(chuàng)建實(shí)用的命令行工具輔助前端開發(fā)、etc…
1.3 Node.js的安裝
下載鏈接:https://nodejs.org/en/
- LTS:長(zhǎng)期穩(wěn)定版
- Current:嘗鮮版
查看版本號(hào)
:node –v
學(xué)習(xí)路線:JavaScript 基礎(chǔ)語法 + Node.js 內(nèi)置 API 模塊(fs、path、http等)+ 第三方 API 模塊(express、mysql 等)
1.4 Node.js的使用
命令
:node js文件名
終端快捷鍵:
①使用 ↑ 鍵,可以快速定位到上一次執(zhí)行的命令
②使用 tab 鍵,能夠快速補(bǔ)全路徑
③使用 esc 鍵,能夠快速清空當(dāng)前已輸入的命令
④輸入 cls 命令,可以清空終端
二、模塊化處理
2.1 什么是模塊化
定義:把復(fù)雜問題簡(jiǎn)單化,分成一個(gè)個(gè)小問題。編程領(lǐng)域中的模塊化
,就是遵守固定的規(guī)則
,把一個(gè)大文件拆成獨(dú)立并互相依賴
的多個(gè)小模塊
把代碼進(jìn)行模塊化拆分的好處:
- 提高了代碼的復(fù)用性
- 提高了代碼的可維護(hù)性
- 可以實(shí)現(xiàn)按需加載
2.2 內(nèi)置模塊
定義:由Node.js官方提供,如fs、http、path
2.2.1 fs文件系統(tǒng)模塊
(1)基本用法
// 引用內(nèi)部模塊 const fs = require('fs'); // 文件讀取 fs.readFile('../files/test-fs.txt', 'utf-8', (err, results) => { if (err) return console.log(err.message);// 錯(cuò)誤信息err null console.log(results); }) // 文件寫入 fs.writeFile('../files/test-fs.txt', 'Node.js', 'utf-8', (err) => { if (err) return console.log(err.message); console.log('寫入文件成功!'); })
注意點(diǎn)
readFile
只能讀取已經(jīng)存在的文件writeFile
寫入內(nèi)容已經(jīng)有文件,則創(chuàng)建同名文件,再寫入文件readFile
需要在writeFile
后面讀取,不然出錯(cuò)
(2)防止動(dòng)態(tài)拼接
node
命令自動(dòng)將當(dāng)前路徑和js腳本文件路徑
拼接,而不管.day總復(fù)習(xí)這個(gè)路徑
- 我們可以使用
絕對(duì)路徑
改善
(3)路徑問題
./
表示當(dāng)前目錄../
表示父級(jí)目錄../..
表示祖父目錄- 動(dòng)態(tài)拼接,首部不能出現(xiàn)
./ ../
,否則拼接失敗 /…/
2.2.2 path內(nèi)置模塊
定義:拼接絕對(duì)路徑
- path.join()
- path.basename()
- path.extname()
const fs = require('fs'); const path = require('path'); const fpath = path.join(__dirname, '/../files/test-fs.txt'); fs.readFile(fpath, 'utf-8', (err, results) => { console.log(__dirname); console.log(path.basename(fpath, '.txt')); console.log(path.extname(fpath)); if (err) return console.log(err.message); console.log(results); }) // test-fs // .txt // Node.js
2.2.3 http內(nèi)置模塊
定義:Node.js
提供創(chuàng)建web服務(wù)器
(1) 初始化
// 導(dǎo)入http模塊 const http = require('http'); //創(chuàng)建web服務(wù)器實(shí)例 const server = http.createServer(); //綁定request事件,監(jiān)聽客戶端請(qǐng)求 server.on('request', (req, res) => { let str = `路徑 ${req.url} 方法 ${req.method}`; console.log(str); // 向客戶端發(fā)送中文前,設(shè)置響應(yīng)頭 res.setHeader('Content-Type', 'text/html;charset=utf-8'); res.end(str); }) //啟動(dòng)服務(wù)器 server.listen(80, () => { console.log('http://127.0.0.1'); })
(2) web服務(wù)器
- 根據(jù)瀏覽器訪問的
url地址
不同,返回相應(yīng)的絕對(duì)路徑
const fs = require('fs'); const http = require('http'); const path = require('path'); const server = http.createServer(); let fpath = ''; server.on('request', (req, res) => { if (req.url === '/') { fpath = path.join(__dirname + '/../files/clock/index.html'); console.log(__dirname); console.log(fpath); } else { fpath = path.join(__dirname + '/../files/clock' + req.url); } fs.readFile(fpath, 'utf-8', (err, results) => { if (err) res.end('404 not find'); res.end(results); }) }) server.listen(80, () => { console.log('http://127.0.0.1'); })
2.3 自定義模塊
定義:用戶自定義的js模塊
//引入本地文件 const custom = require('./01-node.js的使用');
注意:自定義模塊開頭必須有./ …/
2.4 外部模塊
定義:由第三方
提供,使用前需要下載
//下載外部導(dǎo)入 const moment = require('moment');
監(jiān)聽nodemon
npm i nodemon -g
代替node
使用nodedmon
,每次修改內(nèi)容不需要重啟服務(wù)器,自動(dòng)監(jiān)聽
2.5 模塊化處理
模塊作用域定義
:和函數(shù)一致,當(dāng)前模塊定義的方法、變量,只能在當(dāng)前模塊
訪問,防止變量污染
暴露
:通過module.exports
或者exports
暴露出去,使用 require()
方法導(dǎo)入模塊時(shí),導(dǎo)入的結(jié)果,永遠(yuǎn)以module.exports
指向的對(duì)象為準(zhǔn)
2.6 加載機(jī)制
定義: 一次加載緩存,從緩存加載
,內(nèi)置模塊
加載優(yōu)先級(jí)MAX
三、包的基本應(yīng)用
包:概念像node.js
的第三方模塊
,包是基于內(nèi)置模塊
封裝出來的,提供了更高級(jí)、更方便的 API
,極大的提高了開發(fā)效率
npm: 包管理工具
3.1 使用流程
- npm
安裝包
- js
導(dǎo)入包
- 根據(jù)開發(fā)文檔
使用包
// npm i moment const moment = require('moment'); const date = moment().format('YYYY-MM-DD HH:mm:ss'); console.log(date);//2022-09-10 10:43:24
3.2 版本問題
包的版本號(hào)是以“點(diǎn)分十進(jìn)制”形式進(jìn)行定義的,總共有三位數(shù)字,例如 2.24.0
其中每一位數(shù)字所代表的的含義如下:
-
第1位數(shù)字:
大版本
-
第2位數(shù)字:
功能版本
-
第3位數(shù)字:
Bug修復(fù)版本
版本號(hào)提升的規(guī)則:只要前面的版本號(hào)增長(zhǎng)了,則后面的版本號(hào)歸零。
npm i comment@2.22.2
3.3 參數(shù)問題
node_modules
文件夾用來存放所有已安裝到項(xiàng)目中的包。require() 導(dǎo)入第三方包時(shí),就是從這個(gè)目錄中查找并加載包。package-lock.json
配置文件用來記錄 node_modules 目錄下的每一個(gè)包的下載信息,例如包的名字、版本號(hào)、下載地址等。package.json
項(xiàng)目的名稱、版本號(hào)、描述等、用到了哪些包、開發(fā)期間使用的包、部署使用的包devDependencies
:開發(fā)依賴dependencies
:核心依賴
- 注意:程序員不要手動(dòng)修改 node_modules 或 package-lock.json 文件中的任何代碼,npm 包管理工具會(huì)自動(dòng)維護(hù)它們,今后在項(xiàng)目開發(fā)中,一定要把 node_modules 文件夾,添加到 .gitignore 忽略文件中
3.4 npm命令
//安裝包 npm i moment //安裝全局包 npm i 包名 -g //安裝包到開發(fā)階段到devDependencies npm i 包名 -D //安裝所有依賴包 npm install //卸載包 npm uninstall moment //查看已經(jīng)安裝的局部包 npm ls //查看全局安裝的包 npm ls -g
查看包命令:https://blog.csdn.net/qq_41664096/article/details/121797260
3.5 下載鏡像
//查看當(dāng)前npm鏡像 npm config get registry //nrm鏡像工具,安裝為全局鏡像 nrm ls //切換鏡像 nrm use taobao
3.6 開發(fā)自己的包
一個(gè)規(guī)范的包,它的組成結(jié)構(gòu),必須符合以下 3 點(diǎn)要求:
- 包必須以
單獨(dú)的目錄
而存在 - 包的頂級(jí)目錄下要必須包含
package.json
這個(gè)包管理配置文件 - package.json 中必須包含
name,version,main
這三個(gè)屬性,分別代表包的名字、版本號(hào)、包的入口
發(fā)布包到npm
上
- 鏡像切換到npm上
npm login
登錄- 發(fā)布包
npm publish
- 刪除包
npm unpublish 包名 --force
資源:
- https://www.npmjs.com/ 網(wǎng)站上搜索自己所需要的包
- https://registry.npmjs.org/ 服務(wù)器上下載自己需要的包
四、Express
4.1 簡(jiǎn)介
Express
:基于Node.js http
進(jìn)一步封裝,更加高級(jí)的Web開發(fā)框架
對(duì)于前端程序員來說,最常見的兩種服務(wù)器,分別是:
- Web 網(wǎng)站服務(wù)器:專門對(duì)外提供
Web 網(wǎng)頁資源
的服務(wù)器 - API 接口服務(wù)器:專門對(duì)外提供
API 接口
的服務(wù)器
4.2 基本使用
//導(dǎo)入包 const express = require('express'); //創(chuàng)建服務(wù)器 const app = express(); app.get('/user', (req, res) => { res.send({ 男: '18', age: 28 }); }) app.post('/user', (req, res) => { res.send('post請(qǐng)求'); }) app.get('/', (req, res) => { //req.query ?name=zs&age=18 這種數(shù)據(jù) //http://127.0.0.1?name=zs&age=18 console.log(req.query); }) app.post('/:id', (req, res) => { //動(dòng)態(tài)匹配參數(shù) console.log(req.params); }) //啟動(dòng)服務(wù)器 app.listen(80, () => { console.log('http://127.0.0.1'); })
4.3 托管靜態(tài)資源
定義
:通過路徑暴露文件,省去文件路徑的描寫
const express = require('express'); const app = express(); //托管靜態(tài)資源,不需要訪問 app.use('/public', express.static('../files/clock')); app.listen(80, () => { console.log('http://127.0.0.1'); })
推薦VScode插件:postcode
Express 的中文官網(wǎng): http://www.expressjs.com.cn/
4.4 路由
定義
:客戶端與服務(wù)器映射關(guān)系
4.4.1 簡(jiǎn)單掛載
//導(dǎo)入包 const express = require('express'); //創(chuàng)建服務(wù)器 const app = express(); app.get('/user', (req, res) => { res.send({ 男: '18', age: 28 }); }) app.post('/user', (req, res) => { res.send('post請(qǐng)求'); }) //啟動(dòng)服務(wù)器 app.listen(80, () => { console.log('http://127.0.0.1'); })
4.4.2 模塊化路由
為了方便對(duì)路由進(jìn)行模塊化的管理
,Express 不建議將路由直接掛載到 app 上,而是推薦將路由抽離為單獨(dú)的模塊
。
將路由抽離為單獨(dú)模塊的步驟
如下:
-
創(chuàng)建路由
模塊
對(duì)應(yīng)的.js
文件 -
調(diào)用
express.Router()
函數(shù)創(chuàng)建路由對(duì)象 -
向路由對(duì)象上掛載具體的路由
-
使用
module.exports
向外共享路由對(duì)象 -
使用
app.use()
函數(shù)注冊(cè)路由模塊
創(chuàng)建路由對(duì)象
const express = require('express');//導(dǎo)入包 const router = express.Router();//創(chuàng)建路由對(duì)象 //綁定路由規(guī)則 router.get('/user/list', (req, res) => { res.send('user list message'); }) router.post('/user/add', (req, res) => { res.send('user add message'); }) //向外導(dǎo)出路由對(duì)象 module.exports = router;
使用路由對(duì)象
const express = require('express'); const app = express(); const router = require('./11-模塊化路由'); app.use(router); app.listen(80, () => { console.log('http://127.0.0.1'); })
4.5 中間件
中間件:與路由處理函數(shù)不同,必須包含next參數(shù)
4.5.1 基本使用
const express = require('express'); const app = express(); //全局中間件的簡(jiǎn)化形式 app.use((req, res, next) => { console.log('正在使用全局中間件'); next(); }); app.get('/',(req, res) => { res.send('Get message'); }) app.listen(80, () => { console.log('http://127.0.0.1'); })
注意
-
多個(gè)中間件共享
req,res
,上游設(shè)置好,下游的中間件/路由使用 -
中間件
定義先后順序執(zhí)行 -
局部生效
的中間件,定義在app.get('/',中間件,(req, res) => { res.send('Get message'); })
登錄后復(fù)制-
-
路由之前
調(diào)用中間件 -
next()函數(shù)
不能忘,后面不用寫內(nèi)容
4.5.2 中間件分類
(1)應(yīng)用
const express = require('express'); const app = express(); //全局中間件 app.use((req, res, next) => { console.log('全局中間件'); next(); }) //局部中間件 function mw(req, res, next) { console.log('局部中間件'); next(); } app.get('/', mw, (req, res) => { res.send('server is visting'); }) app.listen(80, () => { console.log('http://127.0.0.1'); })
(2)路由
定義:綁定到 express.Router()
實(shí)例上的中間件
(3)錯(cuò)誤
定義:捕獲項(xiàng)目
錯(cuò)誤,防止出錯(cuò),在所有路由之后
定義
const express = require('express'); const app = express(); app.get('/', (req, res) => { throw new Error('服務(wù)器出錯(cuò)'); res.send('server is visting'); }) //全局中間件 app.use((err, req, res, next) => { console.log('Error!' + err.message); res.send('Error!' + err.message); next(); }) app.listen(80, () => { console.log('http://127.0.0.1'); }) //Error!服務(wù)器出錯(cuò)
(4)Express 內(nèi)置
const express = require('express'); const app = express(); // express.json()解析JSON請(qǐng)求體 app.use(express.json()); //解析application/x-www- app.use(express.urlencoded({ extended: false })); app.post('/user', (req, res) => { console.log(req.body); }) app.post('/book', (req, res) => { console.log(req.body); }) app.listen(80, () => { console.log('http://127.0.0.1'); }) // http://127.0.0.1 // { name: 'zs', age: 18 } // [Object: null prototype] { name: '西游記' }
(5)第三方
npm install body-parse
require
導(dǎo)入app.use()
為全局
const express = require('express'); const app = express(); const parser = require('body-parser'); app.use(parser.urlencoded({ extended: false })); app.post('/book', (req, res) => { console.log(req.body); }) app.listen(80, () => { console.log('http://127.0.0.1'); })
注意:Express 內(nèi)置的 express.urlencoded
中間件,就是基于 body-parser
這個(gè)第三方中間件進(jìn)一步封裝出來的。
4.6 自定義中間件
封裝中間件
const querystring = require('querystring'); function parsebody(req, res, next) { let str = ''; req.on('data', (result) => { str += result; }) req.on('end', () => { const body = querystring.parse(str); req.body = body; next(); }) } module.exports = parsebody;
測(cè)試中間件
const express = require('express'); const app = express(); const parsebody = require('./14-自定義中間件'); app.use(parsebody); app.post('/user', (req, res) => { res.send(req.body); console.log(req.body); }) app.listen(80, () => { console.log('http://127.0.0.1'); })
4.7 接口
const express = require('express'); const app = express(); const router = require('./15-接口問題'); app.use(router); app.listen(80, () => { console.log('http://127.0.0.1'); })
4.7.1 GET接口
const express = require('express'); const apiRouter = express.Router(); apiRouter.get('/user', (req, res) => { const query = req.query; res.send({ status: 0, msg: 'GET 請(qǐng)求成功', data: query }); }) module.exports = apiRouter;
4.7.2 POST接口
apiRouter.use(express.urlencoded({ extended: false })); apiRouter.post('/user', (req, res) => { const body = req.body; res.send({ status: 0, msg: 'POST 請(qǐng)求成功', data: body }); })
4.7.3 PUT接口
4.7.4 DELETE接口
4.7.5 區(qū)別
https://blog.csdn.net/qq_42931285/article/details/119852294
https://zhuanlan.zhihu.com/p/135454697
五、跨域
5.1 CORS
5.1.1 原理
概念:由Http
響應(yīng)頭構(gòu)成,決定瀏覽器
是否阻止js代碼
獲取資源,在服務(wù)器端
配置
5.1.2 響應(yīng)頭
//只允許特定的域名訪問、*代表全部 res.setHeader('Access-Control-Allow-Origin', 'http://www.baidu.com'); //配置請(qǐng)求頭信息 res.setHeader('Access-Control-Allow-Headers', 'Content-Type,X-Custom-Header'); //配置請(qǐng)求頭方法 * 代表全部 res.setHeader('Access-Control-Allow-Methods', 'GET,POST,DELETE,PUT');
5.1.3 分類
(1)簡(jiǎn)單請(qǐng)求
- 請(qǐng)求方式:GET、POST、HEAD
- HTTP 頭部信息不超過以下幾種字段:
無自定義頭部字段
、Accept、Accept-Language、Content-Language、DPR、Downlink、Save-Data、Viewport-Width、Width 、Content-Type(只有三個(gè)值application/x-www-form-urlencoded、multipart/form-data、text/plain) - 客戶端與服務(wù)器只發(fā)送
一次請(qǐng)求
(2)預(yù)檢請(qǐng)求
- 請(qǐng)求方式:GET、POST、HEAD之外的方式
自定義
頭部字段OPTION預(yù)檢
,成功后發(fā)送帶有數(shù)據(jù)的請(qǐng)求
5.2 JSONP
概念:只支持GET
請(qǐng)求
六、Mysql數(shù)據(jù)庫
定義:組織
、存儲(chǔ)
、管理數(shù)據(jù)
的倉庫
6.1 SQL命令
6.1.1 查詢
select * from userswhere id>1 and id <5
6.1.2 插入
insert into users(username,password) values('jack','666')
6.1.3 更新
update users set password='666666'where username='jack'
6.1.4 刪除
delete from users where id=9
6.2 Node.js使用
6.2.1 初始化
- 導(dǎo)包:
npm i mysql
//引入mysql const mysql = require('mysql'); //建立數(shù)據(jù)庫連接 const db = mysql.createPool({ url: '127.0.0.1',//數(shù)據(jù)庫IP地址 user: 'root',//賬號(hào) password: '123456',//密碼 database: 'test_db'//操作哪一個(gè)數(shù)據(jù)庫 });
6.2.2 查詢
const queryStr = 'select * from users'; db.query(queryStr, (err, results) => { if (err) return console.log(err.message); console.log(results); }) PS E:FEDjsnode.jsnode.js—資料day總復(fù)習(xí)code> node .18-mysql操作.js [ RowDataPacket { id: 1, username: 'zz', password: '123', status: 0 }, RowDataPacket { id: 2, username: 'ls', password: 'abc', status: 0 }, RowDataPacket { id: 4, username: 'jony', password: '456', status: 0 } ]
6.2.3 插入
const user = { username: 'superman', password: 'jknad' }; const insertStr = 'insert into users set ?'; db.query(insertStr, user, (err, results) => { if (err) return console.log(err.message); if (results.affectedRows == 1) { console.log('插入數(shù)據(jù)成功'); } }) //插入數(shù)據(jù)成功
6.2.4 更新
const user = { id: 10, username: 'super', password: '123456' }; const updateStr = 'update users set ? where id=?'; db.query(updateStr, [user, user.id], (err, results) => { if (err) return console.log(err.message); if (results.affectedRows == 1) { console.log('更新數(shù)據(jù)成功'); } })
6.2.5 刪除
(1) 一般刪除
const deleteStr = 'delete from users where id=?'; db.query(deleteStr, 10, (err, results) => { if (err) return console.log(err.message); if (results.affectedRows == 1) { console.log('刪除成功'); } })
(2) 標(biāo)記刪除
const deleteStr = 'update users set status=1 where id=?'; db.query(deleteStr, 10, (err, results) => { if (err) return console.log(err.message); if (results.affectedRows == 1) { console.log('刪除成功'); } })
七、前后端的身份認(rèn)證
7.1 Web開發(fā)模式
7.1.1 基于服務(wù)端渲染的傳統(tǒng) Web 開發(fā)模式
概念:服務(wù)端在后臺(tái)拼接html頁面
,發(fā)送給客戶端,不需要ajax
特點(diǎn):
- 前端耗時(shí)少
- 有利于SEO
- 占用服務(wù)端資源
- 不利于前后端分離開發(fā)
7.1.2 基于前后端分離的新型 Web 開發(fā)模式
概念:后端提供API
接口,前端通過ajax
調(diào)用接口
特點(diǎn):
- 開發(fā)體驗(yàn)好
- 用戶體驗(yàn)好
- 減輕服務(wù)器渲染壓力
- 不利于SEO
不談業(yè)務(wù)場(chǎng)景而盲目選擇使用何種開發(fā)模式都是耍流氓
- 比如
企業(yè)級(jí)網(wǎng)站
,主要功能是展示而沒有復(fù)雜的交互,并且需要良好的SEO
,則這時(shí)我們就需要使用服務(wù)器端渲染
- 而類似
后臺(tái)管理項(xiàng)目
,交互性
比較強(qiáng),不需要考慮SEO
,那么就可以使用前后端分離
的開發(fā)模式 - 另外,具體使用何種開發(fā)模式并不是絕對(duì)的,為了同時(shí)兼顧了首頁的渲染速度和前后端分離的開發(fā)效率,一些網(wǎng)站采用了
首屏服務(wù)器端渲染 + 其他頁面前后端分離
的開發(fā)模式
7.2 身份認(rèn)證
概念:通過不同的手段(驗(yàn)證碼、密碼、人臉、指紋...
),認(rèn)證客戶的身份
7.3 Session認(rèn)證機(jī)制
7.3.1 Cookie
Cookie:存儲(chǔ)在瀏覽器不超過4KB
字符串,鍵值對(duì)
形式存儲(chǔ)
- 自動(dòng)發(fā)送
- 域名獨(dú)立
- 過期時(shí)限
- 4KB限制
- 容易偽造,不建議存放
隱私數(shù)據(jù)
7.3.2 Session
核心:會(huì)員卡+pos機(jī)認(rèn)證
npm install express-session