Node
- Node是一個基于Chrome V8引擎的 javaScript代碼運作環境
- 什麼軟體可以運作javaScript ,它就是javaScript的運作環境
- Node.js是運作在代碼環境之上的語言
- 由ECMAScript 和子產品的API組成
Node.js全局對象global
- 在浏覽器中全局對象是window,在Node中全局對象是global
- 全局對象global 中 以下方法可以在任何地方使用, global可以省略
- console.log()
- setTimeout()
- clearTimeout()
- setInterval()
- clearInterval()
javaScript的弊端
- 浏覽器端javaScript在使用時存在兩大問題,命名沖突(命名污染)和檔案依賴
- 子產品化開發需要依賴第三方庫檔案,具有代表性有require.js 和sea.js
環境變量Path
- 将一些要執行的軟體的根目錄存儲到系統當中,在指令行中目前目錄沒有找到要執行的檔案名時,系統會自動去這些目錄下查找有沒有同名的檔案名,有就會執行.
子產品化開發(導入導出)
- 一個javaScript檔案就是一個子產品.
- 子產品化開發的好處就是當一個子產品出問題時,我們隻需要将這個子產品取出 進行修複就好,其他子產品還是正常在運作的
- 子產品内部定義的變量和函數預設情況下在外部無法得到
- 子產品内部可以使用exports對象進行成員導出,使用require方法導入其他子產品
要導出暴露(共享)出去的變量或函數的 a.js 檔案:
const add = (n1,n2) => n1+n2; //定義一個加法函數
exports.add = add; //第一個add為exports對象的屬性,第二個add為函數
要導入外部變量或函數的 b.js 檔案:
let a = require('./a.js'); //用一個變量接收傳回值
a.add(10,20); //輸出a可以檢視共享的變量或函數 函數可以代入參數調用來使用
- exports是module.exports的别名(位址引用關系) ,是以導出導入變量用module.exports對象也是可以的
- 當exports和module.exports指向同一個對象時,可以同時添加屬性和方法,當給module.exports對象重新指派(覆寫原有的屬性方法)時,導出對象最終以module.exports準
系統子產品 fs 檔案操作
- 系統子產品: Node環境提供的API ,這些API都是以子產品的方式進行開發的,是以這些API又叫系統子產品
- 檔案子產品fs 可以 讀取檔案, 寫入檔案, 建立檔案夾
- 要使用fs的方法 必須先引入fs子產品
- 讀取檔案
- fs.readFile(‘檔案路徑/檔案名稱’,[‘檔案編碼’],callback) callback 就是回調函數
- 讀取檔案是硬碟的操作,需要時間(耗時操作), 是以用一個回調函數來傳回讀取的結果
const fs = require('fs') //引入fs子產品
//要讀取其他子產品的 js檔案b.js内
fs.readFile('./a.js','UTF8',(err,doc) => {
//讀取a.js檔案 如果讀取成功err=null 失敗 err就是一個對象,裡面存儲錯誤資訊
if (err == null) { //如果讀取成功
console.log(doc) //輸出doc(檔案讀取的結果,就是檔案的内容)
}
})
2.寫入檔案
- fs.writeFile(‘檔案路徑/檔案名稱’ , ‘要寫入的内容’ , callback)
- 如果沒有要寫入的這個檔案,系統會自動幫我們建立
const fs = require('fs') //引入fs子產品
fs.writeFile('./dome.txt','要寫入的内容可以是變量',err => {
if (err != null) { //當寫入失敗的時候
console.log(err); //輸出列印錯誤資訊 并且直接退出函數
return;
}
console.log('檔案内容寫入成功') //寫入成功時,控制台列印成功
})
系統子產品path 路徑操作
- 路徑拼接 path.join(‘路徑’,‘路徑’…)
- 由于不同作業系統的分隔符不同 \ 或 / 我們需要用路徑拼接方法讓系統自動幫我們拼接相應的分隔符
const path = require('path') //引入path子產品
//因為路徑拼接不是耗時操作 可以直接用變量儲存傳回值
let finalPath = path.join('視訊','動漫','喜洋洋與灰太狼')
console.log(finalPath) // window以\分割 是以會分割成 視訊\動漫\喜洋洋與灰太狼
相對路徑and絕對路徑
- 相對路徑有時候相對的是指令行工具的目前工作目錄,容易路徑錯誤,是以大多數情況下使用絕對路徑
- 在讀取檔案或者設定檔案路徑都會選擇絕對路徑
- 可以使用**__dirname**獲得目前檔案所在目錄的絕對路徑
const fs = require('fs') //引入fs子產品
const path = require('path') //絕對路徑是路徑操作要引入 路徑子產品
//讀取檔案API 自動擷取目前檔案的絕對路徑 //報錯回調函數
fs.readFile(path.join(__dirname,'檔案名'),'uft8',(err,doc) => {
console.log(err)
console.log(doc)
})
第三方子產品
- 别人編譯好,具有特定功能 可以直接使用的子產品叫第三方子產品.
- 由于第三方子產品通常由多個檔案組成放在一個檔案夾中,又名為 包
- 第三方子產品兩種存在形式
- 以js檔案的形式存在,提供實作具體功能的API接口 類似jQuery
- 以指令行工具形式存在,輔助項目開發
- 第三方子產品下載下傳平台: npmjs.com 第三方子產品的存儲和分發倉庫
- 下載下傳: npm install 子產品名稱 (預設下載下傳到指令行工作目錄下)
- 本地安裝就是子產品安裝到目前項目當中 (庫檔案)
- 全局安裝就是安裝到公共目錄,所有的項目都可以使用 (指令行工具)
- 解除安裝 npm uninstall 子產品名稱 (直接删除檔案夾也可以)
第三方子產品nodemon 儲存自動運作
- 指令行工具中 : npm install nodemon -g 下載下傳(-g全局比對)
- 當這個檔案被儲存時,指令行工具會自動再運作這個被儲存的檔案, 大大提高了工作效率
- ctrl+c在指令行工具中是終止操作的意思
第三方子產品 nrm 切換國内下載下傳位址
- 下載下傳位址切換工具,因為npm預設的下載下傳位址在國外,國内下載下傳速度慢. 是以要切換到國内同步了國外npm的伺服器網址.這樣可以提高下載下傳速度
- 指令行工具使用: npm install nrm -g 下載下傳(-g全局比對)
- nrm ls 查詢可用下載下傳位址清單 (前面有*号的是目前預設的下載下傳位址)
- nrm use 要切換的下載下傳位址
第三方子產品 gulp
- 建構項目,HTML CSS JS檔案壓縮合并
- 文法轉換(es6,less)
- 公共檔案抽離
- 修改檔案浏覽器自動重新整理
- 1.指令行工具使用 npm install gulp 下載下傳(要使用的庫檔案下載下傳)
- npm install gulp -cli 安裝gulp指令工具
- 2.在項目根目錄建立gulpfile.js檔案 (名字不能更改)
- 3.建立一個src目錄檔案 将原代碼全部放入其中, 建立一個dist檔案 用于放置建構後的檔案
- 4.在gulpfile.js檔案中編寫任務
- 5.在指令行工具中執行gulp任務
- 一個html要運作 需要拷貝html css js 還有圖檔音頻等檔案
gulp中提供的方法:
- gulp.src() 擷取任務要處理的檔案
- .pipe 比對目前任務擷取的檔案 , 進行何種操作 (指派任務)
- gulp.dest() 輸出檔案
- gulp.task() 建立gulp任務 (第一個參數是任務名,第二個參數是回調函數)
- gulp.watch 監控檔案的變化
const gulp = require('gulp') //引入gulp子產品
gulp.task('要建立的任務名',() => { //task 建立任務
gulp.src('./src/css/base.css') //要處理的檔案
.pipe(gulp.dast('dist/css')) //要輸出的檔案(相當于拷貝到另一個檔案夾中)
})
- 要使用gulp的方法 需要下載下傳一個東西
- 指令行工具 : npm install gulp-cli -g
- 使用時在目前目錄名 指令行工具輸入 : gulp 要輸出的任務名
gulp插件使用
- 在指令行工具下載下傳插件
- 檢視相應文檔
- 調用插件
- gulp-htmlmin html檔案壓縮
- gulp-csso 壓縮css
- gulp-babel javaScript文法轉換(es6轉es5)
- 會根據目前運作環境,轉換支援目前環境的代碼
- gulp-less less文法轉換css 等等
- gulp-uglify 壓縮混淆javaScript (壓縮js檔案)
- 在文法轉換完畢後使用
1. require()引用插件的子產品
2.使用gulp.task()建立任務
task的回調函數中進行:
3.gulp.src()擷取要處理的檔案
//*代表所有的, less檔案或css檔案 可以以數組的方式傳遞多個
gulp.src(['./src/css/*.less','./src/css/*.css'])
4. pipe(調用要對要處理檔案使用的下載下傳好的插件的方法)
pipe(less()) prpe(csso()) //将less轉換為css 将css進行壓縮
5. pipe(gulp.dest(輸出處理後的檔案到相應檔案夾) //複制處理後的檔案至建構後的檔案夾
任務建構(指令行工具執行多個任務)
task第二個參數 如果不寫函數 寫中括号,數組元素寫任務名,當這個任務被執行時,會依次執行數組裡的任務
gulp.task('default',['任務名1','任務名2','任務名3','任務名4'])
package.json項目描述檔案
項目描述檔案,記錄目前項目資訊,例如項目名稱,版本号,作者,github位址,目前項目依賴了哪些第三方子產品, 可以讓他人快速了解項目資訊,下載下傳依賴檔案. 使用npm init -y 生成 路徑不能有中文
- 下載下傳第三方子產品時會自動産生package-lock.json,它的作用是, 鎖定包的版本号 加快下載下傳速度
- package.json中scripts選項中的作用
- 存儲指令的别名 , 當我們要頻繁使用的指令比較長的時候 可以給它起個别名
- 通過 npm run ‘别名’ 來運作
-
項目依賴
- 在項目的開發階段和線上營運階段,都需要依賴的第三方包,稱為項目依賴。
- 使用
指令下載下傳的檔案會預設被添加到package.json檔案的dependencies字段中。npm install 包名
- 在dependencies字段中的第三方子產品就是項目依賴
- 開發依賴
- 在項目的開發階段需要依賴,線上營運階段不需要依賴的第三方包,稱為開發依賴。
- 使用
指令下載下傳的檔案會預設被添加到package.json檔案的devDependencies字段中。npm install 包名 --save-dev
- 在devDependencies字段中的第三方子產品就是開發依賴
-
為什麼記錄依賴項
- Node.js中下載下傳的第三方封包件擁有非常多的細碎檔案,将項目通過移動硬碟傳遞給别人時傳輸速度非常慢.
- 使用git工具管理項目時,不希望git管理node_modules檔案夾,也不會将其上傳到github中.
- 當其他人擷取到項目時,可以在項目根目錄下執行
指令,npm工具會自動去package.json檔案中查找項目依賴檔案并下載下傳.npm install
- 當項目上線以後,可以直接運作
下載下傳項目依賴。如果不加–production,會下載下傳全部依賴npm install --production
子產品查找規則
- require(’./find’); 當子產品擁有路徑 沒有字尾時
- 先查找有沒有同名js檔案 ,再查找同名檔案夾
- 有同名檔案夾,就會去目前檔案夾中的package.js檔案中main選項中的入口檔案
- 如果main選中的沒有不存在或者沒有指定入口檔案, 找同名檔案夾中的index.js
- 如果檔案夾中沒有index.js檔案 就會報錯
- require(‘find’); 當子產品沒有路徑也沒有字尾時,會先被當作一個系統子產品
- 如果參數不是一個系統子產品,會去node_modules檔案夾中看看是否有同名的js檔案
- 接下來的查詢步驟和當子產品擁有路徑沒有字尾時一樣
伺服器端基礎
網站的組成 :
- 用戶端(html css js) : 在浏覽器運作的部分, 能讓使用者看到并與之互動的界面程式.
- 伺服器端(node.js) : 在伺服器中運作的部分 , 負責 存儲資料和處理應用邏輯.
- 用戶端 通過請求 伺服器 , 伺服器端接收請求并邏輯處理 響應回 用戶端
- Node網站伺服器:它能夠接收用戶端的請求,将接收的請求邏輯處理後響應
IP位址 域名 端口
- IP位址 : 網絡環境下的唯一辨別,相當于計算機的編号吧,用于通路伺服器,但是由于ip位址都是數字,不便記憶,是以就有了域名.
- 域名 : 就是上網所用的網址,計算機會在DNS伺服器自動将域名轉換為IP位址 然後使用IP位址請求伺服器
- 端口 : 用于區分用戶端的不同的軟體請求是哪一種應用程式, 是在一段範圍内的數字(0~65535),每一個應用程式都有一個不同的端口号,如果一個端口号被一個軟體占用了, 另一個軟體再去綁定就會報錯.
- netstat -anolfindstr 3000 檢視3000端口是哪個程序的PID在占用
- 3000網站服務 110 郵件服務 27017 資料庫服務 21檔案上傳服務
URL的組成(統一資源定位符)
- URL傳輸協定組成 : 協定//域名:端口/ 路徑?參數
- http: // www.baidu.com / news / 20190110/123456789.html
- 網站一般使用的都是http協定.
uri : 統一資源辨別符
- 作用 : 本地資源定位
建立Web伺服器 (http子產品)
//引用系統子產品htttp
const http = require('http')
//http的一個方法擷取http網站伺服器對象
const app =http.createServer();
//為網站伺服器添加事件(request請求事件) req請求對象 res響應對象
app.on('request',(req,res) => {
//res響應對象的end方法 響應請求對象 參數寫響應的内容
res.end()
})
//監聽3000視窗 網站伺服器對象方法linsten 監聽
app.listen(3000)
//因為這個伺服器是在本機上的,通路使用:localhost:3000 (localhost通路本機域名)
//netstat -anolfindstr 3000 檢視3000端口是哪個程序的PID在占用
http協定
http協定就是超文本(可以傳輸文本圖檔音頻視訊)傳輸協定 ,協定規定了用戶端和服務端網址互動的一些規則
- http屬于短連接配接 一次請求 一次響應
- 而長連接配接 是一直連接配接的 一般都是聊天通訊用的
封包
- http響應和請求傳遞的資料塊就是封包,又分請求封包和響應封包
- 請求封包
- 請求方式(method) : 用戶端發送請求封包有兩種方式get(請求資料)和post(上傳資料)
- 在擷取資料時一般用get,上傳(修改)資料用post(安全但是資料量大一些)
- post方式請求頭比get多 一個content-type 内容類型和content-length 内容長度
- 服務端内部根據length判斷傳遞是否完畢
//修改成post請求方式 用戶端寫入進來
//method: 指定目前表單送出的方式(post或get)
//action: 指定目前表單送出的位址
<from method='post' action='http://localhost:3000'>
<input type='submit' name=''/>
</from>
- 在請求事件中可以通過 if 判斷req.method的值是get或post 進行不同的操作
- 請求位址: 請求事件内請求對象 req的一些方法
- req.headers //擷取請求頭的封包
- 通過 req.headers[‘屬性’] 可以輸出請求封包内不同屬性的值
- req.url //擷取請求位址
- 擷取使用者輸入請求過來的域名後的首頁名(如/index 或 /list)
- 通過 if 判斷使用者輸入不同的網址進入不同的頁面
- req.method //擷取請求的方式
- 通過 req 請求對象的屬性method獲得 (請求方式預設是get)
- req.headers //擷取請求頭的封包
- 響應封包
- 響應頭 :傳回的資料類型 : res.writeHead(HTTP狀态碼,{content-type:‘内容類型 ; charset=utf8’}) 服務端是通過響應對象res的writeHead方法響應給服務端請求的結果
- HTTP狀态碼 響應此次請求的結果
- 100系列 代表伺服器在等待
- 200系列 請求成功 206 斷點續傳成功 (迅雷下載下傳暫停任務,關閉後再打開還可以繼續下載下傳)
- 300系列 重定向 (重新跳轉)
- 400系列 用戶端請求文法有誤 404 請求的資源沒有被找到 前端出現問題
- 500系列 服務端錯誤(505) 伺服器崩潰 後端出現問題
- 内容類型 此次請求成功時 響應(傳回)的内容的檔案類型
text/html : HTML格式
text/plain :純文字格式
text/xml : XML格式
image/gif :gif圖檔格式
image/jpeg :jpg圖檔格式
image/png:png圖檔格式
以application開頭的媒體格式類型:
application/xhtml+xml :XHTML格式
application/xml : XML資料格式
application/atom+xml :Atom XML聚合格式
application/json : JSON資料格式
application/pdf :pdf格式
application/msword : Word文檔格式
application/octet-stream : 二進制流資料(如常見的檔案下載下傳)
application/x-www-form-urlencoded : <form encType=””>中預設的encType,form表單資料被編 碼為key/value格式發送到伺服器(表單預設的送出資料的格式)
另外一種常見的媒體格式是上傳檔案之時使用的:
multipart/form-data : 需要在表單中進行檔案上傳時,就需要使用該格式
-
application/json
3.charset 編碼類型 如果不寫 輸入中文會亂碼
get請求參數 (url内置子產品)
- 用戶端向伺服器發送請求時,有時需要攜帶一些參數 這些請求參數被放在浏覽器位址欄中, ?之後的都是請求參數 多個參數以& 分開
- 服務端通過請求對象 req.url() 方法 可以獲得請求參數
- node提供了一個url 内置子產品 裡面的 url.parse() 可以更友善得擷取用戶端傳遞過來的參數
- parse解析 解析req.url 各個部分 第二個參數false以字元串表現 true 以對象形式表現
//擷取 url内置子產品
const url = require('url')
// 以字元串表現
url.parse(req.url,false)
// query為?後面的請求參數 以對象的形式接收請求參數
url.parse(req.url,true).query
//pathname 擷取不包含參數的請求位址
//在傳遞參數時/index後面會有很多參數 是以需要用這個來邏輯處理給用戶端進入哪個頁面
url.parse(req.url,true).pathname
post請求參數
- post請求參數不在位址欄,而是在請求封包當中,通過事件的方式接受的,并且會将資料分割為多份 傳遞過來,是以需要以字元串拼接的方式接受
const http = require('http')
const app =http.createServer();
//處理請求參數子產品
const querystring = require('querystring')
app.on('request',(req,res) => {
//建立一個變量 用于拼接傳遞過來的參數
let str = '';
//當有參數傳遞過來時觸發data事件
req.on('data', chunk(每次傳遞過來的參數) => str += chunk ;)
//當有參數傳遞完時 觸發end事件
req.on('end', () => {
//使用querystring子產品的parse()方法将字元串轉換成對象
querystring.parse(str)
})
res.end('歡迎登入')
})
app.listen(3000)
路由
- 路由就是指根據用戶端的請求位址,伺服器端接收後進行邏輯處理再響應用戶端,這個過程就叫路由.
//擷取http子產品 和http網站伺服器對象
//擷取 url 子產品
//為網站伺服器對象添加請求事件{
//先通過req.method判斷請求方式是get還是post
//再if判斷 使用者輸入的不包含參數的請求位址來顯示不同的内容
}
靜态資源
- 服務端響應不需要進行處理的檔案,就是靜态資源. 例如:html css javaScript等
- 相同的請求位址,響應不同的資源,就是動态資源.
- 先通過url 擷取使用者輸入的路徑
- 然後通過 path 來拼接 檔案的 絕對路徑
- 利用fs 讀取檔案内容
- 把内容響應給用戶端浏覽器
第三方子產品mime
- 下載下傳npm install mime
- const mime = require(‘mime’)
- mime.getType(‘要判斷的路徑’) 會自動根據路徑 傳回檔案的類型
- 通常在響應用戶端 傳回資源的類型不确定時使用
同步API和異步API(異步API傳回結果的擷取)
- 同步API會從上至下依次執行,隻有一個執行完成後才會執行下一個
- 異步API需要等同步API執行完畢後 才會根據 觸發的先後順序執行
- 擷取傳回結果都是同步API 是以return 無法獲得異步API的傳回結果
- 要想擷取異步API的傳回結果就需要用到回調函數
function getResult(callback) {
setTimeout(function(){
callback({ //兩秒後調用callback函數 裡面填想要異步傳回的結果
想要異步傳回的結果
})
},2000)
}
getResult(function(data){ //函數中寫一個形參data用于接收 想要異步傳回的結果
console.log(data) //同步輸出傳回的結果
})
- 函數調用的時候才會運作裡面的代碼,是以異步API觸發時調用函數 就可以實作異步程序執行後再執行同步程序
異步程序回調地獄和Promise解決方案
- 多層重複的異步程序進行嵌套,就叫回調地獄.
- Promise本身是一個構造函數,要使用promise解決回調地獄的問題 需要使用new運算符建立Promise構造函數的執行個體對象
- 在建立對象的時候需要傳入一個匿名函數,匿名函數中有兩個參數 resolve,reject
- Promise執行個體對象方法:
- then()方法 ,用于調用resolve函數
- catch()方法也有個方法用于調用reject函數
- finally()方法 成功與否都會執行(非标準)
- 如果傳回值是個普通值,自動轉換成promise對象,并作為新promise對象的resolv
function p1() {
return new Promise((resolve,reject) => {
fs.readFile('./1.txt','utf8',(err.data) => {
resolve(data)
})
})
}
function p2() {
return new Promise((resolve,reject) => {
fs.readFile('./2.txt','utf8',(err.data) => { //當檔案讀取的時候
resolve(data) //
})
})
}
p1().then((r1) => { //先調用p1函數
console.log(r1) //r1就是接收Promise的 函數resolve(data) 内的傳回值data
return p2(); //當第一個異步API讀取完畢後 再傳回包裹第二個異步API的函數調用
})
.then((r2) => { //上一個函數傳回了的調用鍊式連結了這個 可以達到依次執行的效果
console.log(r2) //r2就是接收Promise的 函數resolve(data) 内的傳回值data
})
promise對象方法
- all 同時處理多個異步任務,所有任務都完成才能得到傳回結果
- rece 同時處理多個異步任務,隻要有一個完成就傳回結果,
promise.all([p1,p2,p3]).then(function(result){
console.log(result)
})
promise.race([p1,p2,p3]).then(function(result){
console.log(result)
})
異步函數 async (es7)
異步函數就是異步程式設計文法的優化版,可以讓異步代碼寫成同步形式,讓代碼不再有回調函數嵌套.
文法: const fn = async () => {}
async funciton fn() {}
- 普通函數定義前面加 asnc 就是異步函數
- 異步函數的returrn傳回結果會被包裹在promise對象中. return替代了resolve方法
- 在異步函數内部使用throw關鍵字抛出程式異常 後面可以填錯誤的資訊
- throw一旦執行,後面的代碼将不再執行 ,可以利用catch去捕獲這個錯誤資訊
- 調用異步函數再鍊式調用then方法擷取異步函數的執行結果
- 調用異步函數再鍊式調用catch方法擷取異步函數執行的錯誤資訊
await關鍵字
- 隻能出現在異步函數中, await後面隻能寫promise對象.
- await關鍵字可以暫停異步函數向下執行,直到promise對象獲得傳回結果
async function p1() {
return 'p1';
}
async function p2() {
return 'p2';
}
async function p3() {
return 'p3';
}
async function run() {
let r1 = await p1() //r1擷取完p1異步函數的傳回值才會執行後面的代碼
let r2 = await p2()
let r3 = await p3()
console.log(r1,r2,r3) //r1,r2,r3擷取完畢後輸出傳回結果
}
異步函數對傳回值不是Promise的異步API需要進行包裝
//此時并沒有調用promisify()方法
let promisify = require('util').promisify
//調用promisify包裝異步API就能傳回Promise對象
let readFile = promisify(fs.readFile)
建立一個異步函數
async function Run() {
let r1 = await readFile('需要讀取的檔案','utf8') //r1接收讀取的檔案内容
console.log(r1)
}
Run()
資料庫
是專門用來存儲資料的,可以把資料持久化的,有序的存儲起來.
資料庫MongoDB概念
- database 資料庫 mongoDB資料庫軟體中可以建立多個資料庫
- collection 集合 一組資料的集合,可以了解為JavaScript中的數組
- document 文檔 一條具體的資料,可以了解為JavaScript中的對象
- field 字段 文檔中的屬性名稱,可以了解為JavaScript中的對象屬性
啟動服務或者是停止服務
- 指令行 net start mongodb
- 指令行 net stop mongodb
利用第三方mongoose包操作資料庫
- 下載下傳 npm install mongoose
// 引用mongoose包
const mongoose = require('mongoose');
// 資料庫連結connect()方法 playground 如果沒有會自動建立這個集合
mongoose.connect('mongodb://localhost/playground',{ useNewUrlParser:
true, useUnifiedTopology: true})
.then(() => console.log('資料庫連接配接成功'))
.catch(err => console.log('資料庫連接配接失敗', err));
向資料庫中導入資料
- mongoimport -d 資料庫名稱 -c 集合名稱 -file 要導入的資料檔案路徑
- mongoimport要加入系統環境變量 不然無法使用
向資料庫中加入資料
- 連接配接MongoDB資料庫
- 建立規則
- 建立集合執行個體
- 插入集合執行個體
- 儲存
- user.save()
建立集合
- 分為兩步, 一是對集合設定規則, 二是建立集合
- 字段的規則:
type: String, Boolean,Object//類型
type: mongoose.Schema.Types.ObjectId //規定類型隻能是唯一辨別_id 用于關聯
ref:'user' //要關聯的集合
minlength: 2, //最小長度
maxLength: 5, //最大長度
required: true,// 必填項
trim: true// 是否去除兩邊的空格
min: //最小值 判斷number
max: //最大值 判斷number
enum: //枚舉 隻能在規定的内容中進行選擇
validate: //自定義驗證器
validator //函數中去定義我們的規則
message //裡面設定錯誤資訊
default: //預設值
unique:true 查詢資料庫是否有這個值 如果有就會報錯
// 設定集合規則
const userSchema = new mongoose.Schema({
name: String,
author: String,
tags: [ String ],
data: {
type: Date, default: Date.now
},
isPublished: Boolean
});
// 建立集合并應用規則
const Course = mongoose.model('Course', courseSchema);
//暴露這個集合
module.exports = {
Course
}
資料庫增删改查
- 實際上就是向集合中的資料進行各種操作。
- 因為是耗時操作 是以要用異步函數 async和 await
增create()
删Delete()
//找到符合條件的第一個删除
User.findOneAndDelete({條件}).then()
// 可以删除多個,如果不帶參數,代表清空目前集合
User.deletMany().then()
改date()
- 傳回值是一些修改結果的資訊 如: 是否修改成功 修改成功幾個等
// 修改一條資料 第二個值填修改後的對象
User.updateOne({查詢的條件},{要修改的值}).then()
//如果第一個參數不寫,代表修改所有
User.updateMany({查詢的條件},{要修改的值}).then()
查
User.find().sort('age').then() 升序查找
User.find().sort('-age').then() 降序查找
User.find().skip(跳過幾項).limit(查詢幾條資料).then()
User.find().select('name age').then() 指定去查詢集合中的 name 和age 字段,如果你不想去查詢某個字段 在這個字段前面加一個 -
User.find({hobbies: {$in: ['足球']}}).then(result => console.log(result)) in代表是包含
User.find({age: {$gt: 20, $lt: 40}}).then(result => console.log(result)) $gt 代表是大于 $lt 代表是小于
集合關聯
- 描述集合與集合之間的關系
- 一對多的關系
- 關系是 一對多 一個使用者會有多篇文章
- 在多的一方去設定 關聯字段(外鍵)
- 我們在 文字的集合規則中我們去定義
- type: mongoose.Schema.Types.ObjectId,
- ref: ‘User’ 設定要關聯的 集合
- 查詢的時候就可以把關聯集合的資訊都查詢過來
- 文章集合對象.find().populate(‘關聯字段’)
- 多對多的關系
- 一對一的關系
- 一對多的關系
模闆引擎
是因為我們之前大量拼接字元串,導緻我們代碼很臃腫,而且邏輯跟 結構 混在了一起,不友善進行維護
模闆引擎的出現,讓我們結構和邏輯分離開來了。這樣後續友善進行維護
art-template 第三方的模闆引擎 是 騰訊出品
- 安裝 npm install art-template
- 導入 模闆引擎 : const template = require(‘art-template’)
- 我們最好用 絕對路徑 path.join(__dirname,‘views’,‘index.art’)
- html檔案也是可以的
- 然後用 template(path, 資料對象), 傳回一個字元串,而這個字元串就是我們和頁面結構拼接好的内容
- art檔案中 可以通過這個資料對象的屬性将其輸入到art檔案結構中
模闆的文法
原始文法<% %>
标準文法: {{ 資料 }}
原文輸出:{{@ value}} //前面加@可讀取html标簽
原始文法:<%= 資料 %>
//如果資料中攜帶HTML标簽,預設不會解析标簽,會将其轉義後輸出。使用以下方式可以解析标簽。
{{@ value }}
條件判斷
<!-- 标準文法 -->
{{if 條件判斷1}}
...
{{else if 條件判斷2}}
...
{{/if}}
<!-- 原始文法 -->
<% } if (條件判斷1) { %>
...
<% } else if (條件判斷2) { %>
...
<% } %>
資料循環
//标準寫法 index 索引 value 此次循環的内容
{{each target}}
{{$index}} {{$value}}
{{/each}}
<!-- 原始文法 -->
<% for(var i = 0; i < target.length; i++){ %>
<%= i %> <%= target[i] %>
<% } %>
子模闆
可以将網站公共固定不動區塊(頭部、底部)抽離到單獨的檔案中,再通過include插入到需要的模闆中.
<!-- 标準文法 -->
{{include './header.art'}}
<!-- 原始文法 -->
<% include('./header.art') %>
模闆繼承
- 搭建架構的時候使用 可以封裝 共同的骨架,可以使用 block 将内容插入到想要插入的地方
//基礎子產品art檔案中在要插入内容的地方使用block代表插槽,每個插槽設定不同的名字用于區分
{{block 'head'}} ... {{/block}}
//在要繼承的檔案中 使用extend +路徑 代表要繼承的art檔案 block标簽中輸入要插入的内容即可
{{extend './layout.html'}}
{{block 'head'}} ... {{/block}}
defaults.imports模闆引擎導入第三方子產品方法
- 所有模闆都可以使用這個變量
//導入日期
template.defaults.imports.變量名 = 變量值
//template是要導入的第三方子產品第一個dateFormat是傳入進去使用的變量名 第二個是第三方子產品的dateFormat函數
template.defaults.imports.dateFormat = dateFormat;
//使用 模闆中
{{$imports.dateFormat('要處理的日期')}}
配置模闆引擎根目錄和字尾名
- 利用 template(就不需要寫前面的路徑,也不需要寫字尾名)
//template.defaults.root 配置根目錄
template.defaults.root = path.join(__dirname,'views','06.art')
//template.defaults.extname 配置模闆引擎的 字尾名
template.defaults.extname = ('.art')
const html = template('0.6', { time:new Date()});
Express架構 第三方子產品
- 基于Node平台的web伺服器應用開發架構
- 把一些共性的方法都進行了封裝,不需要我們去再做處理
- 下載下傳 npm install express
const express = require('express')
//建立web伺服器
const app = express()
//監聽get請求
app.get('/',(req,res) => {
//響應浏覽器
res.send()
})
//監聽get請求
app.post('/',(req,res) => {
res.send()
})
//監聽端口
app.listen(8080);
res.send() 響應浏覽器res,end()優化版
- send方法會自動檢測響應的内容類型
- send方法會自動設定http狀态碼(200 404等)
- send方法會自動設定響應的内容類型及編碼(content-type内的檔案類型和utf8)
中間件
- 也是用于監聽請求,但是中間件可以進行傳遞 next()
- next()隻能傳遞字元串 想要傳遞對象(對象可以存放多個參數) ,使用JSON.stringify()轉換成字元串 再通過JSON.parse() 轉換回對象
- 直到 res.send() 響應用戶端才會結束
- 可以将複雜的請求分步驟進行處理
應用場景:
- 路由守衛
- 我們項目中 有一些頁面是需要登入才能進行通路的,此時我們就可以通過中間件來進行攔截。 如果是登入狀态,那麼就可以下發到對應的中間件,如果不是登入狀态,提示使用者進行登入
- 網站維護
- 利用中間件來進行全局的請求的監聽,隻要網站進行了維護,那麼就阻止我們的請求下發
- 自定義404頁面
- 把我們這個中間件寫在 最下面
- 異常的處理
- 同步錯誤進行中間件
- app.use((err, req, res, next) => {}) 全局的異常的捕獲的中間件, 隻要出現了throw抛出異常,就會執行到這個中間件裡面 err.message屬性中就是自定義的錯誤資訊
- 回調函數錯誤進行中間件
- 如果next() 參數是錯誤資訊 将會直接觸發錯誤進行中間件
app.use('/index',(req,res,next)=> { fs.readFile('/随便寫了個錯的.txt','uft8',(err.date)=> { if (err != null) { next(err) } else { res.send(date) } }) }) //錯誤進行中間 app.use((err, req, res, next) => { res.status(500).send(err.message) })
- 異步函數錯誤進行中間件 try catch
app.get('/', async (req,res,next)=> { //如果try中的條件錯了 進入catch内執行ex為錯誤資訊 反之跳過catch向下執行 try { await User.find({name:'張三'}) }catch(ex) { next(ex) } }) //錯誤進行中間 status()修改狀态碼 app.use((err, req, res, next) => { res.status(500).send(err.message) })
路由子產品化
- 将不同的路由抽取成一個個js檔案 通過暴露的方式在主js檔案中使用
- app.use(’/user’, home) home 為暴露出來 自定義名字的路由
//home.js
const home = express.Router() 建立路由對象
home.get('/index'(req,res)=>{ res.send('内容') })
module.exports = home;
//app.js
const home = require('home.js路徑')
app.use('/home',home)
- 通過localhost:端口/home/index 才可以通路到’内容’
參數的擷取
- get 方式
- 直接利用 req.query 就能拿到 請求的參數,并且我們架構幫我們把這個參數轉成了對象
- post 方式
- 建議使用第三方的子產品 body-parser
- const parse = require(‘body-parser’)
- app.use(bodyParser.urlencoded({ extended: false })) 配置路由
-
- 通過req.body 就能得到資料
- 路由參數
- 配置路由參數的規則
- 配置了幾個參數 請求路徑(路徑傳遞參數)就要填寫幾個參數
- app.get(’/list/:id/:name’)
- localhost:8080/list/123/zhangsan
- req.params 擷取路徑參數
- {id: ‘123’, name: ‘zhangsan’}
express-art-template模闆引擎
- 為了讓art-template和express更好地配合,封裝了express-art-template.
- 下載下傳: npm install art-template express-art-template 兩個都要下載下傳
//設定字尾名為art的檔案使用express-art-template模闆引擎
app.engine('art',require('express-art-template'))
//設定模闆預設存放目錄
app.set('views',path.join(__dirname,'views'))
//預設拼接art字尾
app.set('views engine','art')
- res.render(‘路徑’) 響應浏覽器 模闆引擎用 和end() send()一樣
靜态資源托管static()
app.use(express.static(path.join(__dirname)))
路徑符 ‘/’ 是伺服器根目錄絕對路徑 根目錄就是靜态資源托管的位置
app.locals 模闆引擎導入變量
将變量設定到app.locals對象下面,這個資料在所有的模闆中都可以擷取到。
//app.js中 也可以存放複雜資料類型
app.locals.userName = '張三';
//模闆art中 輸入 即可使用
{{ userName }}
建立不同路由對象
- const home = express.Router()
- const list = express.Router()
location.href 修改跳轉頁面
express重定向
res.redirect(‘路徑’)
設定登入狀态cookie與session
- cookie供伺服器端存儲資料(以域名的形式區分)
- cookie有過期時間,超過時間就會自動删除
- 每次請求都會自動發送到伺服器
- session則是一個對象 存儲在伺服器端
- 驗證賬号密碼正确時會在session中生成一個sessionID發送寫入用戶端的cookie中
- 每次用戶端發送請求時, 服務端會判斷cookie中的sessionID是否存在在服務端,如果有就說明登入過,沒有就說明沒登入過
- npm install express-session
const session =require('express-session')
//配置session
app.use(session({
//secret:後面的參數是可以自定義的
secret:'secret key',
//不預設生成sessionid
saveUninitialized:false,
//設定一天過期時間
cookie:{
maxAge:24*60*60*1000
}
}))
//登入成功時 在session中存入一些資料 服務端會随機生成sessionID
req.session.username = user.username
删除登入狀态cookie與session
//在要推出登入的路由中
app.get('/logout',(req,res)=> {
//删除session
req.session.destroy(function(){
//删除cookie
res.clearCookie('connect.sid');
//重定向
res.redirect('/admin/login')
})
})
joi表單驗證子產品
const Joi = require('joi')
const Schema = {
// alphanum隻能是字母或者數字 require必選
username: Joi.string()alphanum().min(3).max(10).required().error(new Error('錯誤資訊'))
// regex 使用正則規則驗證這個字段
password:Joi.string().regex(/^[a-zA-Z0-9]{3,30}$/)
//[] 隻能是數組中的資料類型
acssess_token:[Joi.string(),Joi.Number()]
//integer 隻能是整數
birthyear: Joi.number().integer().min(1900).max(2013),
//email 隻能是郵箱格式
email: Joi.string().email()
}
//validate 驗證 第一個參數驗證對象, 第二個參數驗證的規則 傳回poi
const result = Joi.validate({ username: 'abc', birthyear: 1994 }, schema);
擷取資料user集合中資料的數量: let count = user.countDocuments({})
input檔案上傳和解析表單子產品formidable第三方子產品
- 檔案上傳是以二進制上傳的,非二進制上傳的資料相對簡單點
- form表單域 要上傳檔案的話 必須要有enctype=“multipart/form-data” 屬性
- 安裝: npm install formidable
// 引入formidable子產品
const formidable = require('formidable');
// 建立表單解析對象
const form = new formidable.IncomingForm();
// 設定檔案上傳路徑 推薦寫絕對路徑
form.uploadDir = path.join(__dirname,"/public/uploads");
// 是否保留表單上傳檔案的擴充名
form.keepExtensions = true;
// 對表單進行解析
form.parse(req, (err, fields, files) => {
// fields 存儲普通請求參數 相當于req.body
// files 存儲上傳的檔案資訊
res.send('ok')
});
js實作圖檔即時預覽
- 需要擷取要上傳的表單控件和顯示預覽的圖檔
- 為表單添加multiple屬性可以上傳多個檔案
//用戶端中
//file上傳檔案表單控件添加change事件 當選擇完要上傳的檔案時觸發
file.onchange = function() {
//建立檔案讀取對象
var reader = new FileReader();
//讀取要上傳的檔案 因為會有多個,讀取的檔案是個數組 是以有[0]擷取第一個上傳的檔案
reader.readAsDataURL(this.files[0]);
//讀取檔案是異步API 是以要建立onload事件進行讀取完成後的操作
reader.onload = function () {
//preview 為預覽圖檔 将這個圖檔的屬性 修改為讀取結果
preview.src = reader.result;
}
}
express項目初始化
- 建立項目所需檔案夾
- public 靜态資源
- model 資料庫操作
- route 路由
- views 模闆檔案
- 生成項目描述檔案
- npm init -y
- 下載下傳模闆引擎第三方子產品
- npm install express mongoose art-template express-art-template
- 入口檔案app.js中 建立網站伺服器
- 引入express子產品
- 建立伺服器對象 express()
- 監聽端口listen
- 建立不同路由對象
- const home = express.Router()
- const list = express.Router()
mongoDB資料庫添加賬号
- 以管理者身份運作powershell
- 連接配接資料庫mongo
- 檢視資料庫 show dbs
- 切換到admin資料庫集合 use admin
- 切換後為這個集合添加建立超級管理者賬戶 db.createUser({user:‘root’,pwd:‘root’,roles:[‘root’]})
- user賬号 pwd 密碼 roles角色權限(root超級管理者, readWrite 讀寫權限)
- 解除安裝mongodb服務
- 停止服務 net stop mongodb
- mongod -remove
- 建立mongodb服務
- mongod --logpath=“C:\Program Files\MongoDB\Server\4.1\log\mongod.log” --dbpath=“C:\Program Files\MongoDB\Server\4.1\data” --install --auth
- logpath 填mongod.log路徑 dbpath 填 data檔案夾路徑
- –auth 不填 就不用驗證資料庫賬号密碼
- 開啟服務 net start mongodb
- mongod --logpath=“C:\Program Files\MongoDB\Server\4.1\log\mongod.log” --dbpath=“C:\Program Files\MongoDB\Server\4.1\data” --install --auth
在項目中使用賬号連接配接資料庫(為資料庫建立普通賬号)
- 以管理者身份運作PowerShell
- mongo進入mongodb資料庫操作環境
- use admin 切換到admin資料庫
- db.auth(‘root’,‘root’) 登入admin資料庫
- use alibaixiu 切換到要建立普通賬戶的資料庫(沒有會自動建立這個資料庫)
- db.createUser({user:‘賬号名稱’,pwd:‘賬号密碼’,roles:[‘readWrite’]}) 建立賬戶 權限為讀寫即可
- exit 退出mongodb資料庫操作環境
mongoose.connect('mongodb://blogs:[email protected]/blogs',{ useNewUrlParser:
true, useUnifiedTopology: true})
//('mongodb://賬号:密碼@域名/集合)
//blogs是賬戶blog是密碼
開發環境和生産環境
環境就是項目運作的地方,項目處在開發階段運作在開發人員的電腦上,就是開發環境。
項目開發完成後,将項目放到真實的網站伺服器上,此時的環境就是生産環境。
- 開發環境和生産環境需要連接配接的資料庫是不同的,是以我們的代碼需要通過判斷運作環境來運作不同的項目配置。
設定系統環境變量判斷環境
- 為此電腦添加一個環境變量名:NODE_ENV
- 開發環境值設定成development
- 生成環境設定成production
//可以通過process.env.NODE_ENV 擷取設定的環境變量NODE_ENV的值
if (process.env.NODE_ENV === 'development') {
//開發環境
} else {
//生産環境
}
開發環境列印用戶端發送服務端的請求 morgan子產品
- npm install morgan
- const morgan = require(‘morgan’)
- app.use(morgan(‘dev’)) 生産環境無法列印請求
config子產品
根據不同的運作環境自動去對應的json檔案中擷取配置資訊
- 下載下傳 npm install config
- 項目根目錄建立config檔案夾
- 建立default.json development.json production.json 檔案
- 預設/開發環境/生産環境 搜尋對應的運作環境json檔案沒有配置資訊屬性就會去 default(可以用于存儲公共部分) 中查找
- json檔案中至少要放一個空對象 不然會報錯
- json檔案中配置資訊的屬性名也要用引号""
- 建立default.json development.json production.json 檔案
const config = require('config')
//擷取配置資訊屬性值
config.get('配置資訊屬性名')
将密碼存儲到環境變量中
- config檔案夾下建立custom-environment-variables.json
- 将密碼值存儲到一個系統環境變量中
//在custom-environment-variables.json檔案中
{
"db": {
pwd:"環境變量名"
}
}
ajax可以使用四種請求方式便于浏覽代碼 (restful API)
- get 擷取 post 建立添加 put 修改 delete 删除
- 特點為路由名一般為集合名稱 請求路徑相同 請求方式不同
- 對單獨資料操作 ajax請求時 url:’/users/1’ req.params.id=1
app.get('/users',(req,res)(){})
app.post('/users',(req,res)(){})
app.put('/users/:id',(req,res)(){
req.params.id; //可以擷取路徑參數中的id
})
app.delete('/users/:id',(req,res)(){})
xml基礎介紹
- xml代表可擴充标記語言,他的作用和html一樣是用來傳輸和存儲資料的,一樣可以通過html的dom操作來操作xml
- 簡單來說就是伺服器傳回一些類似html的代碼 雖然 擷取這些資料的代碼不同,但是同樣可以使用dom方式操作他們
//當伺服器傳回的是xhr資料時 使用xhr.responseXML 擷取傳遞過來的xml代碼 var xhr = new XMLHttpRequest(); xhr.open('get','/xml') xhr.send() xhr.onload = function() { console.log(xhr.responseXML) } //伺服器路由 中 通過響應頭告訴用戶端傳回的是xml資料 app.get('/xml',(req,res)=>{ res.header('content-type','text/xml') res.send('xml資料') })