【node.js】基礎知識

2020-08-10 19:39:06

不會就問就查詢!科技利民,學海無涯2

一、概念

  • Node.js不是語言、不是庫、不是框架;
  • Node.js是JavaScript的執行環境,可以解析、執行JavaScript程式碼;
  • 使JavaScript可以脫離瀏覽器來執行;

二、組成部分
1、瀏覽器中JavaScript:

  • ECMAScript
  • DOM
  • BOM

2、Node.js中JavaScript:

  • ECMAScript
  • 沒有DOM、BOM
  • 爲JavaScript提供了一些伺服器級別的操作API:
    • 檔案讀寫
    • 網路服務的構建
    • 網路通訊
    • http伺服器等。。。

三、特點

  • 事件驅動
  • 非阻塞IO模型(非同步)
  • 輕量和高效
  • 構建於chrome的V8引擎 之上

四、用途

  • web後臺伺服器
  • 命令列工具

五、主要知識點

  • B/S程式設計模型
  • 模組化程式設計
  • Node常用API
  • 非同步程式設計
  • Express開發框架
  • Ecmascript 6

六、安裝使用

  • 官網下載:穩定版(LTS)、最新體驗版(Current);(重複下載會升級覆蓋)
  • 命令列檢查:node --version/node -v
  • 對應目錄下執行js指令碼檔案:node aa.js
    • 建立JavaScript指令碼檔案,不要使用node.js
    • 開啓命令列工具定位到檔案
    • 執行js檔案
  • 讀寫檔案:fs模組(file-system檔案系統:提供所有檔案操作API)
    • 引入模組:var fs = require('fs')
    • 讀檔案:fs.readFile('檔案路徑',回撥函數function(error,data){ if(error){ console.log('友好地提示:讀取失敗') } else{ console.log(data)} })
      • 讀取成功:(data:所讀數據;error:null)
      • 讀取失敗:(data:null;error:錯誤物件(直接展示給使用者不友好))
      • 預設返回二進制數據(以十六進制顯示),使用toString()
      • 例如:fs.readFile('./hello.txt',function(error,data){ console.log(data.toString())})
    • 寫檔案:fs.writeFile('檔案路徑',‘檔案內容’,回撥函數function(error){})
      • 寫入成功:error:null,
      • 寫入失敗:error:錯誤物件
    • 刪除、建立檔案等。。。
  • http服務:http模組
    • 請求、響應都在範例的on方法的回撥函數中;
    • 請求:主要用於告訴伺服器,用戶端傳的是什麼路徑;req.url
    • 響應:用於針對不同路徑做對應響應,只能傳字串或二進制(讀取檔案時的數據就是二進制),所以其他數據需要轉成字串/二進制;
      • 方式一:res.write([響應內容]);res.end();
      • 方式二:直接res.end([響應內容]);
    • 響應內容:必須是字串(通過編碼規則轉成二進制)或二進制,因爲在前後端不同語言間傳輸的數據是以JSON字串形式;
      • JSON.stringify();轉成json字串(序列化:用於傳輸);
var http = require('http')
var server = http.createServer()//返回一個範例
server.on('request',function(request,response){
	console.log('當前存取的路徑是'+ request.url)
	//這行程式碼時展示在伺服器上的;url是埠號後面的部分,預設`/`根路徑
	var url = req.url;
	if(url == '/'){
		response.end('hello 首頁');//中文會有亂碼,要設定響應內容型別
	}
	else{
		response.setHeader('Content-Type','text/plain;charset=utf-8');
		//告訴瀏覽器響應數據型別爲普通文字,字元編碼格式是utf-8;
		response.write('hello 其他');
		//中文會有亂碼,要設定響應內容型別
		response.end();
	}
	//響應到用戶端頁面中,end()方法表示響應結束,否則瀏覽器一直等待。
})
//監聽用戶端開啓網址,伺服器收到請求時回撥
server.listen(3000,function(){
	console.log('伺服器啓動成功了,可以通過 http://127.0.0.1:3000/ 來進行存取!')
})
//當服務啓動後回撥,80埠時用戶端不需要輸埠;

七、Node中的JavaScript

  • EcmaScript
  • 內建的具名的核心模組var [模組名] = require('[模組名]')
    • 常用:fs,http,os,path,
  • 第三方模組:Expressweb開發框架,需要下載參照;
  • 使用者自定義模組:就是常說的模組化程式設計,引入js檔案;
    • Node中沒有全域性作用域,只有模組作用域,即引入的不同js檔案,不能互相存取內部變數;所以要在js檔案內使用exports物件導出內部變數、方法;
    • 引入時可以不寫後綴.js;但必須寫相對路徑./,../,否則會被認爲是核心模組,報錯!

八、伺服器端的ip與埠號

  • IP:ip地址用來定位計算機;
  • 埠號:0~65536,埠號用於定位具體的應用程式;(因爲一臺計算機有多個應用程式,靠IP地址無法區分通訊)
    • 用戶端中瀏覽器等應用程式會預設找一個空閒埠來與伺服器通訊;
    • 一些指定埠號與指定服務聯繫,不會被預設使用;
    • 80埠號,一般在上線部署時用,瀏覽器預設加80,使用者就不用寫了;
    • 3000埠號,一般在開發測試時用;
  • 所有需要聯網通訊的應用程式都會佔用一個埠號;
  • 互相通訊的計算機都知道彼此IP地址、埠號;
  • 所以一臺伺服器可以開啓多個服務,每個服務對應一個埠號。

九、響應內容型別

問題原因:
伺服器預設發送的json字串數據(讀取檔案時檔案內容編碼成的二進制所採用的規則由檔案建立時的編碼環境決定,一般用開發工具建立的檔案都是utf-8)就是utf-8編碼,但是瀏覽器預設不知道是什麼編碼,會按當前操作系統預設的編碼格式,如:中文操作系統的預設編碼規則:GBK,兩者規則不同導致中文亂碼。
(對於直接傳二進制數據流時,因爲不同編碼規則對數位、字母的編碼都一樣,在通過二進制解碼html檔案前部分字母是都很正常,中文操作系統用GBK正常解碼二進制數據,識別看到meta便知道用UTF-8,所以在傳html檔案時要宣告型別和編碼規則,元數據meta中定義的編碼格式等同於在伺服器端響應編碼格式)

解決方案:
宣告響應數據的型別(普通文字text/plain、html程式碼text/html等),並告訴瀏覽器編碼格式charset=utf-8
不管以字串形式還是二進制傳的都可以視情況需要在伺服器設定響應數據型別:提供數據型別和編碼格式給瀏覽器

發送的數據與對應響應型別:
Content-Type 對照表

  • 圖片需要指定響應型別,但不需要指定編碼格式;
  • 一般只爲字元數據(中文、字母、數位、符號等)指定編碼格式 utf-8
    如:res.setHeader('Content-Type','text/plain;charset = utf-8')
    如:res.setHeader('Content-Type','text/html;charset = utf-8')
  • 特別地:對於響應讀取html檔案的內容時,可以不寫上述宣告是因爲瀏覽器自動識別html程式碼,並且html程式碼中meta元數據也可以宣告編碼格式。

十、程式碼書寫風格與分號的使用

常見程式碼書寫風格規範:

一般推薦不加分號,必須加分號的情況:

  • 以模板字串``開頭的程式碼前加;
  • ()開頭的程式碼前加;()
  • []開頭的程式碼前加;[]

十一、node.js實現 Apache功能

1、實現瀏覽器輸入域名+埠號/檔名,就顯示伺服器上指定檔案對應的內容;

  • 利用if語句判斷使用者輸入的路徑;
  • 利用fs模組,針對不同路徑找到對應檔案並讀取內容(可以直接響應,因爲fs.readFile()預設返回的data就是二進制(以十六進制顯示),如果要對data進行處理可以toString()轉爲字串);

2、實現在瀏覽器顯示檔案目錄功能;

  • 利用http核心模組建立服務;
  • 利用fs.readdir(path[, options], callback)核心模組讀取檔案目錄資訊,返回陣列形式,用於模板引擎替換目錄數據;
  • 怎麼替換?:要使用模板引擎內建方法:{{each [陣列名]}} <p>陣列中的{{$value}}</p> {{/each}},遍歷陣列中每一項從而生產每一項
  • 利用fs.readFile(path[, options], callback)核心模組讀取用於目錄顯示的html模板檔案並結合模板引擎如:art-template第三方模組替換目錄內容(所以要data.toString()將二進制先轉爲字串,當做替換模板使用);
  • 重點:怎麼判斷目錄是資料夾還是檔案?也有模組可以實現;
  • 最終將改好的字串響應給瀏覽器res.end(newData),實現在瀏覽器顯示目錄、點選目錄。

補充:第三方art-template模板引擎規則是識別字串內{{}}中的變數名;(可在前端html頁面中使用,也可以在後端node中安裝使用)
補充:在Node中使用方式:

var template = require('art-tmplate')
var ret = template.render(data.toString(),{name:'jack'})
//改後的新字串

十二、用戶端渲染與伺服器端渲染

  • 渲染:就是將數據以html元素這種瀏覽器可以識別的方式顯示;
  • 用戶端渲染:採用ajax等非同步操作請求數據,速度更快;但不利於SEO,爬蟲抓不到非同步數據(在原始碼中看不到數據:如京東商品的評論區分頁功能);
  • 伺服器端渲染:直接傳過來帶數據的html頁面,爬蟲在原始碼中能找到數據,有利於SEO;(在網頁原始碼中可以看到數據,如京東商品展示功能)
  • 所以一般網站前後端渲染都會用到,爲了更快、也爲了SEO。

十三、伺服器對靜態資源的處理

背景
當我們在html頁面中使用link、script、img、iframe、video、audio等需要再次發請求引入靜態資源的標籤時,其實就是自動又向伺服器發了一次請求(請求路徑分爲:網路路徑、url檔案路徑會拼接在伺服器地址後面;),所以伺服器端也會對這些請求做響應。(如果沒有響應,瀏覽器就會一直處於等待狀態,沒法渲染頁面)

處理方式
伺服器將靜態資源統一放到一個資料夾public中,然後在html頁面中通過檔案路徑參照,這樣可以通過req.url判斷只要是以/public/開頭的就直接把這個url當做檔案路徑去找這個檔案,然後響應回去;所以html頁面中使用的路徑要是檔案路徑。

十四、伺服器對錶單提交的處理

  • 傳統req.url返回的是整個根目錄及以後的部分;包含了路徑和query資訊;
  • 當用戶端使用get請求時,伺服器要想得到query數據,單從req.url中不容易獲取,所以有了專門處理路徑的核心模組:var url = require('url')
  • 使用這個模組的parse方法可以得到一個路徑物件;
  • var pathObj = url.parse(req.url,true);第二個參數用於將query的屬性值以物件形式呈現,預設是字串,這裏會涉及編碼格式;
  • 然後就是判斷pathObj.pathname路徑字串來決定響應內容;
  • 並向模板中使用上pathObj.query物件;
  • 比如,新增到已有陣列中,並使用重定向讓頁面重新整理;
    • 重定向:響應設定302狀態碼(臨時重定向,再次發請求,瀏覽器不記住重定向後的地址,相當於每次都是第一次,還是會向原網址發請求),瀏覽器收到這個狀態碼,就直接去響應頭中找Location;在響應頭中設定Location告訴瀏覽器重定向的路徑;
    • res.statusCode = 302 res.setHeader('Location','/') res.end()
  • 但是這一步不是數據持久化,伺服器一重新啓動新加的數據會沒有。
    在这里插入图片描述

十四、Node.js的測試方法

  • 瀏覽器中有F12偵錯工具
  • node中也有偵錯工具:>node命令列直接輸命令,可以直接用node中API,按ctrl+c+c退出。

十五、Node.js的模組系統

模組分類:

  • 核心模組
  • 第三方模組
  • 自定義模組

1、Node.js中的模組化基於Commen.JS;

  • 導出的是:module.exports物件;內部預設var module.exports={}; var exports = module.exports; return module.exports
  • 所以想導出什麼,往物件中新增,一般導出單個用:module.exports=;導出多個用:exports.屬性名=module.exports={}

2、require載入規則

  • 優先從快取載入;快取有的就不執行內部程式碼,而是返回物件module.exports
  • require('fs'):引入核心模組
  • require('./aa.js'):引入自定義模組(路徑方式)
  • require('art-template'):引入第三方模組
    核心模組、第三方模組的載入:在node_module檔案下的同名檔案中找package.json中的main屬性對應的值,如index.js,然後去這個檔案並執行;如果main沒有,那預設找index.js;本資料夾沒有則往外找package.json、index.js,直到找到根目錄。

3、package.json會自動記錄同檔案中npm下載時帶--save、--save-dev的第三方模組;不帶不記錄;
4、使用npm init會自動生成package.json檔案;
5、使用npm install會自動根據package.json中記錄下載包;用於拷貝專案時下載包。
6、npm命令
7、解決npm被牆問題(國外網站存取慢):

  • 使用nrm源管理器;可以用npm命令,但是源變了;
  • 下載指定源:npm install --global cnpm;這是可以用新源:cnpm init
  • cnpm是淘寶映象,每10分鐘從npm中更新一次包數據。
  • 設定npm以指定源,npm config list檢視:npm config set registry https://registry.npm.taobao.org;這樣還是npm命令,源變了;

十六、Node.js的第三方模組 :Express

1、輕便的web伺服器開發框架;(封裝的http核心模組)
2、安裝npm install express --save
3、存取路徑:

  • 公開指定目錄:app.use('/public/',express.static('./public/'));使用者可以輸入以/public/開頭的路徑;
  • 指定單個目錄(路由):app.get('/',function(req,res){});使用者存取根目錄時的情況;
  • 預設:當使用者輸入沒有定義的路徑,express內部的設定方案時:返回404頁面。

4、在express中的res響應方法會自動結束,不用res.end()

十七、Node.js的路徑問題

1、fs檔案操作中檔案路徑的相對路徑中./可以省略;
2、require模組載入時的相對路徑中./不能省,省略就是載入核心模組/第三方模組;
3、相對路徑含義:

  • ./aa.js:當前目錄中的aa.js
  • /aa.js:當前磁碟根目錄中
  • ../aa.js:上一級目錄中
  • c:/aa.js:C槽根目錄中

十八、Node.js的服務自動重新啓動

nodemon:用於修改程式碼儲存後自動重新啓動伺服器;

  • node install --global nodemom
    nodemon aa.js

十九、express的靜態資源服務

1、通過路由匹配存取指定資源:app.get('/',function(req,res){})、app.post('/',function(req,res){})
2、通過公開指定目錄存取目錄內檔案:

  • app.use('/public/',express.static('./public/')):以/public/爲根目錄開頭的路徑,後面的部分會去相應檔案中找,並以正確編碼顯示;
  • app.use(express.static('./public/')):只要路由中沒有定義的,會直接擷取根目錄/後面部分,去./public/資料夾內部中找,找到就顯示;

3、使用模板引擎模組art-template

  • 安裝:npm install --save art-template express-art-template
  • 設定:識別.art結尾的檔案,並使用模板引擎;可以在app.engine中更改設定;
    • var express = require(‘express’)
    • var app = express()
    • app.engine('art',require('express-art-template'))//可改後綴名
    • app.set(‘view options’,{debug:process.env.NODE_ENV !== ‘production’})
  • 使用:內部設定自動去同目錄下的views資料夾中查詢這個模板檔案(res.render('index.art')不傳物件參數就是直接傳頁面資訊);
    • app.get('/',function(req,res) { res.render('index.art',{ user:{ name:'aaa' } }) })//自動結束響應
  • 重定向方法:res.redirect('/')

4、獲取請求數據

  • get方法:req.query//就是物件形式
  • post方法:藉助第三方模組body-parser,,res.body也是物件形式;
    在这里插入图片描述

二十、使用express實現增刪改查數據(Create Update Read Delete )

1、CURD起步

  • 新建資料夾npm init,初始化package.json
  • 安裝第三方模組express、art-template、express-art-template
  • 設定、使用模組,建立伺服器檔案;

2、以.json檔案爲數據儲存方式,對檔案數據進行CURD

  • 利用fs核心模組,讀取檔案數據;
  • 將二進制數據toString()轉爲json字串(或者fs.readFile('./aa.json','utf8',function(err,data){}));
  • 將json字串JSON.parse(data)轉爲json物件;
  • 最後使用內部屬性JSON.parse(data).students,用來做模板引擎數據;

3、設定伺服器端路由:router.js

  • 使用express中的router,var router = express.Router()–router.js
  • 載入路由,app.use(router)–app.js

4、建立操作頁面

  • 新增學生頁面
  • 編輯學生頁面
  • 刪除學生頁面

5、封裝對學生數據的操作:students.js

利用ID獲取數據和儲存編輯時注意:

  • 通過req.query、req.body獲取的提交數據是物件形式,但物件中屬性值都是字串型別(所以id屬性也是),需要轉變student.id = parseInt(student.id)
  • 通過JSON.parse(data)將json檔案中字串轉爲物件形式時,內部屬性值原本是什麼型別還不變;

刪除前彈框確認:node環境中沒有confirm,所以在模板頁面中判斷;

  • <a href="/students/delete?id={{$value.id}}" onclick="if(confirm('確定刪除?')==false)return false;">刪除</a>

編輯頁面男女性別選中的判斷:

    <script>
      window.onload = function () {
        if ("{{student.gender}}" == 0) {//express-art-template模組的語法
          document.getElementsByName('gender')[0].checked = 'checked';
        } else {
          document.getElementsByName('gender')[1].checked = 'checked';
        }
      }
    </script>

6、es6方法

Array.prototype.find = function(Func){
	for(var i = 0;i<this.length;i++){
		if(Func(this[i],i)){
			return this[i]//i;返回‘i’,就是findIndex方法
		}
	}
}
  • find:
	var id = '1'
    var stu = students.find(function(item,index){
      return item.id == parseInt(id)
    })
  • findIndex:
	var id = 1
    var delId = students.findIndex(function(item,index){
      return item.id == parseInt(id)
    })

7、非同步程式設計–回撥函數

用於得到一個函數內部非同步操作的結果,在非同步函數內部呼叫執行;

  • 常見非同步操作:
    • setTimeout、readFile、writeFile、ajax
  • 使用方式:正常函數–>傳參數:–>正常函數中使用非同步操作–>在非同步操作中呼叫這個回撥函數–>並將非同步操作的值傳給這個回撥函數作參數
function add(x,y,callback){
	//var x = 10
	//var y = 20
	//var callback = function(sum){...}
	setTimout(function(){
		var sum = x+y
		callback(sum)
	},1000)
}
add(10,20,function(sum){
	return sum
})//30,函數內部執行function

二十一、JavaScript模組化及其他補充

  • node.js中模組化:藉助CommonJS
  • EcmaScript 6中:自帶的模組化規範(官方規範)
  • 瀏覽器中模組化:藉助第三方庫
    • require.js:也稱爲AMD規範
    • sea.js:也稱爲CMD規範
  • EcmaScript 6:通過編譯器工具打包–EcmaScript 5;以防止瀏覽器不支援;
  • 新技術的目的是爲了提高效率,增加可維護性,可以利用編譯器工具打包讓低版本瀏覽器可以執行。
  • npm5版本以上,npm init時會自動生成package.json,當下載包時,會自動生成package-lock.json檔案,記錄下載包以其依賴包的資訊(真正版本號、下載地址等),這樣當拷貝專案時npm install時就會參考這個檔案;
    • 作用:使包的下載速度更快且與開發時版本一樣,沒有這個鎖檔案就會安裝最新版本的包。

二十二、MongoDB數據庫

MongoDB 是一個基於分佈式檔案儲存的數據庫。由 C++ 語言編寫。旨在爲 WEB 應用提供可延伸的高效能數據儲存解決方案。
MongoDB 是一個介於關係數據庫和非關係數據庫之間的產品,是非關係數據庫當中功能最豐富,最像關係數據庫的。

1、(NoSQL)MongoDB 是一個面向文件儲存的數據庫,操作起來比較簡單和容易。
2、下載、安裝、設定環境變數:bin檔案目錄(用於全域性使用)
在这里插入图片描述

  • MongoDB compass:視覺化工具,類似mysql等關係型數據庫所使用的navicat數據庫管理工具;
  • 現在Navicat也支援mongodb數據庫了:Navicat for MongoDB

3、檢查安裝是否成功:mongod --version
4、基本使用:

  • 手動建立數據儲存目錄:在某盤根目錄下建立:D:\data\db

  • 修改數據庫儲存目錄:mongod --dbpath=數據庫儲存目錄路徑//mongod --dbpath=D:\Mongodb\4.0\data;修改前關閉本地MongoDB服務;
    在这里插入图片描述

  • 開啓數據庫服務命令:> mongod

  • 關閉數據庫:ctrl+c

  • 連線數據庫:mongo

  • 關閉連線:exit

  • 檢視所有數據庫:show dbs;預設由系統數據庫

  • 檢視當前數據庫:db;預設有個test數據庫,只有新增數據後才能 纔能在檢視所有數據庫時看到;

  • 切換至指定數據庫,沒有就建立:use [數據庫名稱];也是新增數據後才能 纔能用show dbs看到;

  • 建立一條數據:db.stu.insertOne({"name":"Jack"})

  • 檢視當前數據庫中集合:show collections//stu,類似陣列

  • 檢視集閤中數據:db.stu.find() //{"_id":ObjectId("[自動生成]"),"name":"Jack"}
    在这里插入图片描述