文章目錄
一、nodejs安裝
1、普通方式安裝
訪問官網 ,下載,安裝。
運行 node -v 和 npm -v 測試
2、使用nvm安裝
nvm:nodejs 版本管理工具,可以切換多個nodejs版本
mac os :使用 brew install nvm
windows: github 中搜索 nvm-windows
nvm使用:
配置淘寶鏡像:
nvm root 找到 nvm 安裝目錄
找到 settings.txt 文件
添加:
node_mirror: https://npm.taobao.org/mirrors/node/
npm_mirror: https://npm.taobao.org/mirrors/npm/
nvm list 查看當前所有的已安裝的node版本
nvm install v10.13.0 64 安裝置頂版本的nodejs
nvm use 10.13.0 64 切換到指定版本
二、nodejs 和 javascript區別
ECMAScript是語法規範
nodejs = ES + nodejs API
1、ECMAScript
定義了語法,寫JavaScript 和 nodejs都必須遵守
變量定義 ,循環,判斷,函數
原型和原型鏈,作用域和閉包、異步
文檔:http://es6.ruanyifeng.com/
2、javascript
-
使用ECMAScript語法規範,外加Web API ,缺一不可
-
DOM操作,BOM操作,事件綁定,AJAX等
-
兩者結合,即可完成瀏覽器端的任何操作
3、nodejs
文檔:http://nodejs.cn/api/
-
使用ECMAScript語法規範,外加nodejs API ,缺一不可
-
處理http、處理文件等
-
兩者結合,即可完成server端的任何操作
三、npm
1、npm引入依賴的版本
"5.0.3",
"~5.0.3",
"^5.0.3"
“5.0.3”表示安裝指定的5.0.3版本,“~5.0.3”表示安裝5.0.X中最新的版本,“^5.0.3”表示安裝5.X.X中最新的版本
四、yarn
npm i yarn -g 全局安裝yarn
npm install -g yrm yarn鏡像源控制
yrm ls
yrm use taobao
yrm test taobao
yrm current
yarn config get registry
yarn config set registry https://registry.npm.taobao.org -g
yarn config set electron_mirror https://npm.taobao.org/mirrors/electron/ -g
yarn cache clean
yarn
-
並行安裝:無論 npm 還是 Yarn 在執行包的安裝時,都會執行一系列任務。npm 是按照隊列執行每個 package,也就是說必須要等到當前 package 安裝完成之後,才能繼續後面的安裝。而 Yarn 是同步執行所有任務,提高了性能。
-
離線模式:如果之前已經安裝過一個軟件包,用Yarn再次安裝時之間從緩存中獲取,就不用像npm那樣再從網絡下載了。
- 安裝版本統一:爲了防止拉取到不同的版本,Yarn 有一個鎖定文件 (lock file) 記錄了被確切安裝上的模塊的版本號。每次只要新增了一個模塊,Yarn 就會創建(或更新)yarn.lock 這個文件。這麼做就保證了,每一次拉取同一個項目依賴時,使用的都是一樣的模塊版本。npm 其實也有辦法實現處處使用相同版本的 packages,但需要開發者執行 npm shrinkwrap 命令。這個命令將會生成一個鎖定文件,在執行 npm install 的時候,該鎖定文件會先被讀取,和 Yarn 讀取 yarn.lock 文件一個道理。npm 和 Yarn 兩者的不同之處在於,Yarn 默認會生成這樣的鎖定文件,而 npm 要通過 shrinkwrap 命令生成 npm-shrinkwrap.json 文件,只有當這個文件存在的時候,packages 版本信息纔會被記錄和更新。
- 更簡潔的輸出:npm 的輸出信息比較冗長。在執行 npm install 的時候,命令行裏會不斷地打印出所有被安裝上的依賴。相比之下,Yarn 簡潔太多:默認情況下,結合了 emoji直觀且直接地打印出必要的信息,也提供了一些命令供開發者查詢額外的安裝信息。
- **多註冊來源處理:**所有的依賴包,不管他被不同的庫間接關聯引用多少次,安裝這個包時,只會從一個註冊來源去裝,要麼是 npm 要麼是 bower, 防止出現混亂不一致。
- 更好的語義化: yarn改變了一些npm命令的名稱,比如 yarn add/remove,感覺上比 npm 原本的 install/uninstall 要更清晰。
Yarn和npm命令對比:
npm | yarn |
---|---|
npm install | yarn |
npm install react --save | yarn add react |
npm uninstall react --save | yarn remove react |
npm install react --save-dev | yarn add react --dev |
npm update --save | yarn upgrade |
五、commonjs
nodejs裏默認包含commonjs
1、單個導入導出:
a.js
function add(a, b) {
return a + b
}
module.exports = add
b.js
const add = require('./a')
const sum = add(10, 20)
console.log(sum)
運行:
node b
2、多個導入導出:
a.js
function add(a, b) {
return a + b
}
function mul(a, b) {
return a * b
}
module.exports = {add, mul}
b.js
//es6 解構
const {add, mul} = require('./a')
const a = add(10, 20)
const b = mul(100, 200)
console.log(a)
console.log(b);
運行:
node b
但是:如果導出多個的時候是這樣導出的:
module.exports = add
module.exports = mul
第二個會覆蓋第一個導出
3、另外一種導出
function add(a, b) {
return a + b
}
function mul(a, b) {
return a * b
}
exports.add=add
exports.mul=mul
console.log(module.exports)
打印
{ add: [Function: add], mul: [Function: mul] }
說明 exports 和 module.exports是一塊內存空間,但是
exports = {'add': add, 'mul': mul}
或者
exports = { add, mul}
都不可以
4、引入的測試
a.js
function add(a, b) {
return a + b
}
function mul(a, b) {
return a * b
}
exports.add = add
exports.mul = mul
b.js 直接引入對象
const a = require('./a')
console.log(a.add(5, 6))
console.log(a.mul(5, 6))
這樣是可以的
5、es6語法ecport和default
export命令規定的是對外的接口,必須與模塊內部的變量建立一一對應關係。
// 寫法一
export var m = 1;
// 寫法二
var m = 1;
export {m};
// 寫法三
var n = 1;
export {n as m};
export default 命令
使用export default命令,爲模塊指定默認輸出。
// export-default.js
export default function () {
console.log('foo');
}
6、引入npm包:
npm i lodash --save --registry=https://registry.npm.taobao.org
const _ = require('lodash')
let arr = _.concat([1, 2], 3)
console.log(arr)
const {concat} = require('lodash')
let arr = concat([1, 2], 3)
console.log(arr)
六、知識節點
1、開發流程
定目標>定需求>定ui設計>定技術方案>開發>聯條>測試>上線>評估
2、pv和uv
pv訪問量(Page View),即頁面訪問量,每打開一次頁面PV計數+1,刷新頁面也是。
UV訪問數(Unique Visitor)指獨立訪客訪問數,一臺電腦終端爲一個訪客。
PV(訪問量):PV反映的是瀏覽某網站的頁面數,所以每刷新一次也算一次。就是說PV與來訪者的數量成正比,但PV並不是頁面的來訪者數量,而是網站被訪問的頁面數量。
UV(獨立訪客):可以理解成訪問某網站的電腦的數量。網站判斷來訪電腦的身份是通過來訪電腦的cookies實現的。如果更換了IP後但不清除cookies,再訪問相同網站,該網站的統計中UV數是不變的。
3、訪問url的過程
- dns解析,找到ip地址
- 三次握手,建立tcp連接
- 發送http請求,數據傳輸
- server接收到http請求,處理,並返回
- 客戶端收到返回數據,處理數據(渲染頁面,執行js)
- TCP四次揮手,斷開連接
三次握手即可建立TCP連接
1、第一次握手:客戶端發送syn包(seq=x)到服務器,並進入SYN_SEND狀態,等待服務器確認;
2、第二次握手:服務器收到syn包,必須確認客戶的SYN(ack=x+1),同時自己也發送一個SYN包(seq=y),即SYN+ACK包,此時服務器進入SYN_RECV狀態;
3、第三次握手:客戶端收到服務器的SYN+ACK包,向服務器發送確認包ACK(ack=y+1),此包發送完畢,客戶端和服務器進入ESTABLISHED狀態,完成三次握手。
握手過程中傳送的包裏不包含數據,三次握手完畢後,客戶端與服務器才正式開始傳送數據。理想狀態下,TCP連接一旦建立,在通信雙方中的任何一方主動關閉連接之前,TCP 連接都將被一直保持下去。
爲什麼需要三次握手呢?
相互確認!
爲了保證服務端能收接受到客戶端的信息 並能做出正確的應答而進行前兩次(第一次和第二次)握手,爲了保證客戶端能夠接收到服務端的信息 並能做出正確的應答而進行後兩次(第二次和第三次)握手。
買手機的時候試通話功能的時候:
1老機(客戶端)打給新機(服務器) : 喂 , 聽到了嗎 ?
2新機回覆老機 : 聽到了 , 你聽到了嗎 ?
3老機 : 聽到了聽到了 …
四次揮手即可斷開TCP連接
1、第一次揮手:主動關閉方發送一個FIN,用來關閉主動方到被動關閉方的數據傳送,也就是主動關閉方告訴被動關閉方:我已經不會再給你發數據了(當然,在fin包之前發送出去的數據,如果沒有收到對應的ack確認報文,主動關閉方依然會重發這些數據),但此時主動關閉方還可以接受數據。
2、第二次揮手:被動關閉方收到FIN包後,發送一個ACK給對方,確認序號爲收到序號+1(與SYN相同,一個FIN佔用一個序號)。
3、第三次揮手:被動關閉方發送一個FIN,用來關閉被動關閉方到主動關閉方的數據傳送,也就是告訴主動關閉方,我的數據也發送完了,不會再給你發數據了。
4、第四次揮手:主動關閉方收到FIN後,發送一個ACK給被動關閉方,確認序號爲收到序號+1,至此,完成四次揮手。
白話文:
1、第一次揮手,瀏覽器對服務器說:“煞筆,我不再給你發數據啦,但可以接受數據。”
2、第二次揮手,服務器對瀏覽器說:“騷貨,我知道啦!”
3、第三次揮手,服務器對瀏覽器說:“騷貨,我也不再給你發數據啦!”
4、第四次揮手,瀏覽器對服務器說:“煞筆,我知道啦!”
七、node-http搭建後臺服務
const http = require('http')
const querystring = require('querystring')
module.exports = function () {
const server = http.createServer((req, res) => {
const method = req.method
const url = req.url
const path = url.split("?")[0]
const query = querystring.parse(url.split("?")[1])
//設置返回格式爲json
res.setHeader('Content-Type', 'application/json')
//返回的數據
const resData = {method, url, path, query}
if (method === "GET") {
res.end(JSON.stringify(resData))
}
if (method === "POST") {
let postData = ''
req.on('data', chunk => postData += chunk.toString())
req.on('end', () => {
resData.postData = postData
//返回
res.end(JSON.stringify(resData))
})
}
})
server.listen(8000)
console.log('OK')
}
八、node搭建博客後臺服務
1、cross-env
-
講解
它是運行跨平臺設置和使用環境變量(Node中的環境變量)的腳本。
我們在自定義配置環境變量的時候,由於在不同的環境下,配置方式也是不同的。
- 例如在window和linux下配置環境變量。
#node中常用的到的環境變量是NODE_ENV,首先查看是否存在 set NODE_ENV #如果不存在則添加環境變量 set NODE_ENV=production #環境變量追加值 set 變量名=%變量名%;變量內容 set path=%path%;C:\web;C:\Tools #某些時候需要刪除環境變量 set NODE_ENV=
- 在linux下配置
#node中常用的到的環境變量是NODE_ENV,首先查看是否存在 echo $NODE_ENV #如果不存在則添加環境變量 export NODE_ENV=production #環境變量追加值 export path=$path:/home/download:/usr/local/ #某些時候需要刪除環境變量 unset NODE_ENV #某些時候需要顯示所有的環境變量 env
- cross-env的作用是什麼?
當我們使用 NODE_ENV = production 來設置環境變量的時候,大多數windows命令會提示將會阻塞或者異常,或者,windows不支持NODE_ENV=development的這樣的設置方式,會報錯。因此 cross-env 出現了。我們就可以使用 cross-env命令,這樣我們就不必擔心平臺設置或使用環境變量了。也就是說 cross-env 能夠提供一個設置環境變量的scripts,這樣我們就能夠以unix方式設置環境變量,然而在windows上也能夠兼容的。
-
安裝
npm install --save-dev cross-env
-
package.json中使用
"scripts": { "dev": "cross-env NODE_ENV=dev node ./bin/www", "prod": "cross-env NODE_ENV=prod node ./bin/www" }
-
獲取環境變量
cosnt env = process.env.NODE_ENV
2、nodemon
nodemon用來監視node.js應用程序中的任何更改並自動重啓服務,非常適合用在開發環境中。
nodemon將監視啓動目錄中的文件,如果有任何文件更改,nodemon將自動重新啓動node應用程序。
nodemon不需要對代碼或開發方式進行任何更改。nodemon只是簡單的包裝你的node應用程序,並監控任何已經改變的文件。nodemon只是node的替換包,只是在運行腳本時將其替換命令行上的node。
安裝在全局。
cnpm install -g nodemon
九、nodejs操作 mysql
1、安裝mysql插件
yarn add mysql
2、demo
const mysql = require('mysql')
//創建連接對象
const con = mysql.createConnection({
host: '192.168.100.230',
user: 'root',
password: '123456',
port: '3306',
database: 'blog'
})
//開始執行
con.connect()
//執行sql語句
const sql = 'select * from user'
con.query(sql, (err, result) => {
if (err) {
console.error(err)
return
} else {
console.log(result)
}
})
const sql2 = "update user set name = 'c' where name = 'b' "
con.query(sql2, (err, result) => {
if (err) {
console.error(err)
return
} else {
console.log(result)
}
})
//關閉連接
con.end()
3、封裝
配置:
'use strict';
const env = process.env.NODE_ENV; //環境參數
//配置
let MYSQL_CONF
if (env === 'dev') {
MYSQL_CONF = {
host: '192.168.100.230',
user: 'root',
password: '123456',
port: '3306',
database: 'blog'
}
}
if (env === 'production') {
MYSQL_CONF = {
host: 'online' +
'',
user: 'root',
password: '123456',
port: '3306',
database: 'blog'
}
}
module.exports = {
MYSQL_CONF
}
工具
const mysql = require('mysql')
const {MYSQL_CONF} = require('../conf/db')
//創建連接對象
const con = mysql.createConnection(MYSQL_CONF);
//開始連接
con.connect();
//統一執行sql的函數
function exec(sql) {
return new Promise((resolve, reject) => {
con.query(sql, (err, result) => {
if (err) {
console.error(err);
reject(err)
} else {
console.log(result);
resolve(result)
}
});
})
}
module.exports = {
exec,
escape: mysql.escape// 預防sql注入
}
十、cookie
1、存儲在瀏覽器中的一段字符串(最大5kb)
2、跨域不共享
3、格式如 k1=v1;k2=v2;因此可以存儲格式化數據
4、每次發送http請求,會將請求與的cookie一起發送給server
5、server可以修改cookie並返回給瀏覽器
6、瀏覽器中也可以通過js修改cookie(有限制)
//js查看cookie
document.cookie
//js累加cookie
document.cookie='k1=v1;'
1、server端nodejs操作cookie
//解析cookie
req.cookie = {}
let cookieStr = req.headers.cookie || "";
cookieStr.split(';').forEach(item => {
if (!item) {
return
}
let arr = item.split('=');
let key = arr[0].trim();
let value = arr[1].trim();
req.cookie[key] = value;
})
//設置cookie
const getCookieExpires = () => {
console.log(new Date(Date.now() + 24 * 60 * 60 * 1000).toGMTString())
return new Date(Date.now() + 24 * 60 * 60 * 1000).toGMTString()
}
res.setHeader('Set-Cookie', `username=${result.username};path=/; httpOnly;expires=${getCookieExpires()}`)
httpOnly:只能通過http修改cookie,用 js修改cookie(document.cookie='username=cc') 只會添加一條新cookie而不是修改原來的cookie,然後帶到後端之後只會接收到原來的cookie;但是可以在瀏覽器的application中直接手動修改原來的cookie
十一、nodejs操作redis
1、安裝
yarn add redis
2、demo
const redis = require('redis')
//創建客戶端
let redisClient = redis.createClient(6379, '192.168.100.142');
redisClient.on('error', err => {
console.log(err)
})
//測試
redisClient.set('my', 'ha', redis.print)
redisClient.get('my', (err, val) => {
if (err) {
console.log(err)
return
}
console.log(val);
//退出
redisClient.quit();
})
3、封裝
配置:
'use strict';
const env = process.env.NODE_ENV; //環境參數
//配置
let MYSQL_CONF;
let REDIS_CONF;
if (env === 'dev') {
MYSQL_CONF = {
host: '192.168.100.230',
user: 'root',
password: '123456',
port: '3306',
database: 'blog'
}
REDIS_CONF = {
port: 6379,
host: '192.168.100.230'
}
}
if (env === 'production') {
MYSQL_CONF = {
host: 'online' +
'',
user: 'root',
password: '123456',
port: '3306',
database: 'blog'
}
REDIS_CONF = {
port: 6379,
host: '192.168.100.230'
}
}
module.exports = {
MYSQL_CONF
}
工具redis.js
const redis = require('redis')
const {REDIS_CONF} = require('../conf/db')
//創建客戶端
let redisClient = redis.createClient(REDIS_CONF.port, REDIS_CONF.host);
redisClient.on('error', err => {
console.log(err)
})
function set(key, value) {
if (typeof value === 'object') {
value = JSON.stringify(value)
}
redisClient.set(key, value, redis.print)
}
function get(key) {
return new Promise((resolve, reject) => {
redisClient.get(key, (err, val) => {
if (err) {
reject(err)
return
}
console.log(val);
if (val == null) {
resolve(null)
return;
}
try {
resolve(JSON.parse(val))
} catch (e) {
resolve(val)
}
})
})
}
module.exports = {get, set}
十二、nodejs操作文件
1、path.join和path.resolve的區別
path.join() 方法使用平臺特定的分隔符把全部給定的 path 片段連接到一起,並規範化生成的路徑。
長度爲零的 path 片段會被忽略。 如果連接後的路徑字符串是一個長度爲零的字符串,則返回 '.',表示當前工作目錄。
“平臺特定的分隔符”:
windows下文件路徑分隔符使用的是"\"
Linux下文件路徑分隔符使用的是"/"
“path片段”:即是說,該方法接收的是多個路徑的部分或全部,然後簡單將其拼接。
“規範化”:顧名思義,如果你給出的路徑片段中任一路徑片段不是一個字符串,則拋出TypeError。
我們來舉個例子:
path.join('/foo', 'bar', 'baz/asdf', 'quux', '..');
// 返回: '/foo/bar/baz/asdf'
這裏需要注意:如果路徑中出現"..",那麼它前面的路徑片段將被丟失。
path.resolve() 方法會把一個路徑或路徑片段的序列解析爲一個絕對路徑。
給定的路徑的序列是從右往左被處理的,後面每個 path 被依次解析,直到構造完成一個絕對路徑。 例如,給定的路徑片段的序列爲:/foo、/bar、baz,則調用 path.resolve('/foo', '/bar', 'baz') 會返回 /bar/baz。
如果處理完全部給定的 path 片段後還未生成一個絕對路徑,則當前工作目錄會被用上。
生成的路徑是規範化後的,且末尾的斜槓會被刪除,除非路徑被解析爲根目錄。
長度爲零的 path 片段會被忽略。
如果沒有傳入 path 片段,則 path.resolve() 會返回當前工作目錄的絕對路徑。
path.resolve('/foo/bar', './baz');
// 返回: '/foo/bar/baz'
path.resolve('/foo/bar', '/tmp/file/');
// 返回: '/tmp/file'
path.resolve('wwwroot', 'static_files/png/', '../gif/image.gif');
// 如果當前工作目錄爲 /home/myself/node,
// 則返回 '/home/myself/node/wwwroot/static_files/gif/image.gif'
總結一下:path.join只是簡單的將路徑片段進行拼接,並規範化生成一個路徑,而path.resolve則一定會生成一個絕對路徑,相當於執行cd操作。
2、file 操作 demo
const fs = require('fs')
const path = require('path')
const fileName = path.resolve(__dirname, './data.txt')
//1、一次性讀取文件內容
function test_read() {
fs.readFile(fileName, (err, data) => {
if (err) {
console.log(err)
} else {
console.log(data.toString())
}
});
}
//2、一次性寫入文件
function test_write() {
const content = '這是寫入的內容\n';
const opt = {
flag: 'a' //追加寫入 覆蓋用'w'
};
fs.writeFile(fileName, content, opt, err => {
if (err) console.log(err)
})
}
//3、判斷文件是否存在
function test_exist() {
fs.exists(fileName, exists => console.log(exists))
}
test_read();
test_exist();
3、stream 操作 demo
const path = require('path');
const fs = require('fs');
//1、測試標準輸入輸出
function test_std() {
process.stdin.pipe(process.stdout)
const http = require('http');
const server = http.createServer((req, res) => {
req.pipe(res)
})
server.listen(8000)
}
//2、測試stream複製文件
function test_copy() {
let aPath = path.resolve(__dirname, 'a.txt');
let bPath = path.resolve(__dirname, 'b.txt');
let cc = path.join(__dirname, 'b.txt');
console.log(aPath)
console.log(bPath)
console.log(cc)
let readStream = fs.createReadStream(aPath);
let writeStream = fs.createWriteStream(bPath);
readStream.pipe(writeStream);
readStream.on('data', chunk => {
console.log('copy :', chunk.toString())//一點點的讀取
})
readStream.on('end', () => {
console.log('copy done')
})
}
//3、測試stream讀取file
function test_read() {
const http = require('http');
const server = http.createServer((req, res) => {
const fileName = path.resolve(__dirname, './data.txt');
let readStream = fs.createReadStream(fileName);
readStream.pipe(res)
})
server.listen(8000)
}
//4、測試stream創建並寫入文件
function test_write() {
let c = path.resolve(__dirname, 'c.txt');
let writeStream = fs.createWriteStream(c);
writeStream.write('還是返回肯定是減肥', error => {
console.log(error)
})
}
//5、測試 readline
function test_readline() {
let filePath = path.resolve(__dirname, 'c.txt');
const readline = require('readline')
//創建readStream
let readStream = fs.createReadStream(filePath);
//創建readline 對象
let rl = readline.createInterface({input: readStream});
//逐行讀取
let count = 0;
rl.on('line', data => {
console.log(data)
count++;
})
//監聽讀取完成
rl.on('close', args => {
console.log(`總共有${count}行`)
})
}
// test_std();
// test_copy();
// test_read();
// test_write();
test_readline();
4、封裝
const path = require('path');
const fs = require('fs');
function createWriteStream(fileName) {
let logPath = path.resolve(__dirname, '../../logs', fileName);
return fs.createWriteStream(logPath, {flags: 'a'});
}
const accessWs = createWriteStream('access.log');
const errorWs = createWriteStream('error.log');
const eventWs = createWriteStream('event.log');
//寫日誌
function log(writeStream, logContent) {
writeStream.write(logContent + '\n')
}
function accessLog(logContent) {
log(accessWs, logContent);
}
function errorLog(logContent) {
log(errorWs, logContent);
}
function eventLog(logContent) {
log(eventWs, logContent);
}
//測試
accessLog(`-- ${Date.now()}`)
module.exports = {accessLog, errorLog, eventLog}
十三、crontab 拆分日誌
1、腳本
copy.sh
#!/bin/sh
cd [路徑]/myblog-simple/logs
cp access.log $(date +%Y-%m-%d).access.log
echo "" > access.log #清空
2、創建定時任務
crontab -e #創建任務
0 0 0 * * * sh copy.sh
crontab -l #查看任務
十四、xss攻擊防護
1、 安裝
yarn add xss
2、使用
const xss = require('xss');
let content = `<script>window.alert('sd')</script>`;
console.log(xss(content));
結果:
<script>window.alert('sd')</script>
十五、nodejs 加密數據
const crypto = require('crypto');
//密匙
const SECRET_KEY = 'sadasd_dsads2';
//md5加密
function md5(content) {
let md5 = crypto.createHash('md5');
return md5.update(content).digest('hex');
}
//加密函數
function genPassword(password) {
const str = `password=${password}&key=${SECRET_KEY}`; //就是md5加鹽
return md5(str);
}
//測試
console.log(genPassword('12324'));