天天看點

8-NodeJS基礎資料庫

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子產品
  1. 讀取檔案
    • 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 路徑操作
  1. 路徑拼接 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插件使用
  1. 在指令行工具下載下傳插件
  2. 檢視相應文檔
  3. 調用插件
    • 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 ‘别名’ 來運作
  1. 項目依賴
  • 在項目的開發階段和線上營運階段,都需要依賴的第三方包,稱為項目依賴。
  • 使用

    npm install 包名

    指令下載下傳的檔案會預設被添加到package.json檔案的dependencies字段中。
  • 在dependencies字段中的第三方子產品就是項目依賴
  1. 開發依賴
  • 在項目的開發階段需要依賴,線上營運階段不需要依賴的第三方包,稱為開發依賴。
  • 使用

    npm install 包名 --save-dev

    指令下載下傳的檔案會預設被添加到package.json檔案的devDependencies字段中。
  • 在devDependencies字段中的第三方子產品就是開發依賴
  1. 為什麼記錄依賴項
  2. Node.js中下載下傳的第三方封包件擁有非常多的細碎檔案,将項目通過移動硬碟傳遞給别人時傳輸速度非常慢.
  3. 使用git工具管理項目時,不希望git管理node_modules檔案夾,也不會将其上傳到github中.
  • 當其他人擷取到項目時,可以在項目根目錄下執行

    npm install

    指令,npm工具會自動去package.json檔案中查找項目依賴檔案并下載下傳.
  1. 當項目上線以後,可以直接運作

    npm install --production

    下載下傳項目依賴。如果不加–production,會下載下傳全部依賴

子產品查找規則

  1. require(’./find’); 當子產品擁有路徑 沒有字尾時
  • 先查找有沒有同名js檔案 ,再查找同名檔案夾
  • 有同名檔案夾,就會去目前檔案夾中的package.js檔案中main選項中的入口檔案
  • 如果main選中的沒有不存在或者沒有指定入口檔案, 找同名檔案夾中的index.js
  • 如果檔案夾中沒有index.js檔案 就會報錯
  1. 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響應和請求傳遞的資料塊就是封包,又分請求封包和響應封包
  1. 請求封包
  • 請求方式(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)
  1. 響應封包
  • 響應頭 :傳回的資料類型 : res.writeHead(HTTP狀态碼,{content-type:‘内容類型 ; charset=utf8’}) 服務端是通過響應對象res的writeHead方法響應給服務端請求的結果
    1. HTTP狀态碼 響應此次請求的結果
    • 100系列 代表伺服器在等待
    • 200系列 請求成功 206 斷點續傳成功 (迅雷下載下傳暫停任務,關閉後再打開還可以繼續下載下傳)
    • 300系列 重定向 (重新跳轉)
    • 400系列 用戶端請求文法有誤 404 請求的資源沒有被找到 前端出現問題
  • 500系列 服務端錯誤(505) 伺服器崩潰 後端出現問題
  1. 内容類型 此次請求成功時 響應(傳回)的内容的檔案類型
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等
  • 相同的請求位址,響應不同的資源,就是動态資源.
    1. 先通過url 擷取使用者輸入的路徑
    2. 然後通過 path 來拼接 檔案的 絕對路徑
    3. 利用fs 讀取檔案内容
    4. 把内容響應給用戶端浏覽器
第三方子產品mime
  • 下載下傳npm install mime
  • const mime = require(‘mime’)
  • mime.getType(‘要判斷的路徑’) 會自動根據路徑 傳回檔案的類型
  • 通常在響應用戶端 傳回資源的類型不确定時使用
同步API和異步API(異步API傳回結果的擷取)
  • 同步API會從上至下依次執行,隻有一個執行完成後才會執行下一個
  • 異步API需要等同步API執行完畢後 才會根據 觸發的先後順序執行
  • 擷取傳回結果都是同步API 是以return 無法獲得異步API的傳回結果
  1. 要想擷取異步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要加入系統環境變量 不然無法使用
向資料庫中加入資料
  1. 連接配接MongoDB資料庫
  2. 建立規則
  3. 建立集合執行個體
  4. 插入集合執行個體
  5. 儲存
    • 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() 響應用戶端才會結束
  • 可以将複雜的請求分步驟進行處理

應用場景:

  1. 路由守衛
    • 我們項目中 有一些頁面是需要登入才能進行通路的,此時我們就可以通過中間件來進行攔截。 如果是登入狀态,那麼就可以下發到對應的中間件,如果不是登入狀态,提示使用者進行登入
  2. 網站維護
    • 利用中間件來進行全局的請求的監聽,隻要網站進行了維護,那麼就阻止我們的請求下發
  3. 自定義404頁面
    • 把我們這個中間件寫在 最下面
  4. 異常的處理
    1. 同步錯誤進行中間件
    • app.use((err, req, res, next) => {}) 全局的異常的捕獲的中間件, 隻要出現了throw抛出異常,就會執行到這個中間件裡面 err.message屬性中就是自定義的錯誤資訊
    1. 回調函數錯誤進行中間件
    • 如果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)
    }) 
               
    1. 異步函數錯誤進行中間件 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 才可以通路到’内容’
參數的擷取
  1. get 方式
    • 直接利用 req.query 就能拿到 請求的參數,并且我們架構幫我們把這個參數轉成了對象
  2. post 方式
    • 建議使用第三方的子產品 body-parser
    • const parse = require(‘body-parser’)
    • app.use(bodyParser.urlencoded({ extended: false })) 配置路由
      1. 通過req.body 就能得到資料
  3. 路由參數
    • 配置路由參數的規則
    • 配置了幾個參數 請求路徑(路徑傳遞參數)就要填寫幾個參數
    • 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
  1. cookie供伺服器端存儲資料(以域名的形式區分)
    • cookie有過期時間,超過時間就會自動删除
    • 每次請求都會自動發送到伺服器
  2. session則是一個對象 存儲在伺服器端
    • 驗證賬号密碼正确時會在session中生成一個sessionID發送寫入用戶端的cookie中
  3. 每次用戶端發送請求時, 服務端會判斷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項目初始化

  1. 建立項目所需檔案夾
    • public 靜态資源
    • model 資料庫操作
    • route 路由
    • views 模闆檔案
  2. 生成項目描述檔案
    • npm init -y
  3. 下載下傳模闆引擎第三方子產品
    • npm install express mongoose art-template express-art-template
  4. 入口檔案app.js中 建立網站伺服器
    • 引入express子產品
    • 建立伺服器對象 express()
    • 監聽端口listen
  5. 建立不同路由對象
    • const home = express.Router()
    • const list = express.Router()

mongoDB資料庫添加賬号

  1. 以管理者身份運作powershell
  2. 連接配接資料庫mongo
  3. 檢視資料庫 show dbs
  4. 切換到admin資料庫集合 use admin
  5. 切換後為這個集合添加建立超級管理者賬戶 db.createUser({user:‘root’,pwd:‘root’,roles:[‘root’]})
    • user賬号 pwd 密碼 roles角色權限(root超級管理者, readWrite 讀寫權限)
  6. 解除安裝mongodb服務
    1. 停止服務 net stop mongodb
    2. mongod -remove
  7. 建立mongodb服務
    1. 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 不填 就不用驗證資料庫賬号密碼
    2. 開啟服務 net start mongodb
在項目中使用賬号連接配接資料庫(為資料庫建立普通賬号)
  1. 以管理者身份運作PowerShell
  2. mongo進入mongodb資料庫操作環境
  3. use admin 切換到admin資料庫
  4. db.auth(‘root’,‘root’) 登入admin資料庫
  5. use alibaixiu 切換到要建立普通賬戶的資料庫(沒有會自動建立這個資料庫)
  6. db.createUser({user:‘賬号名稱’,pwd:‘賬号密碼’,roles:[‘readWrite’]}) 建立賬戶 權限為讀寫即可
  7. 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檔案中擷取配置資訊
  1. 下載下傳 npm install config
  2. 項目根目錄建立config檔案夾
    • 建立default.json development.json production.json 檔案
      • 預設/開發環境/生産環境 搜尋對應的運作環境json檔案沒有配置資訊屬性就會去 default(可以用于存儲公共部分) 中查找
      • json檔案中至少要放一個空對象 不然會報錯
    • 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資料')
    })
               

繼續閱讀