文章目录
一、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'));