node安装
进入node官网
第一个为长期支持的版本,可以理解成稳定版,第二个为发布版本,含有最新的功能。可根据需求下载安装。
下载完成后,打开文件选择路径即可安装。
***若需要切换不同的版本,则需要使用nvm
REPL
Node.js中REPL(交互式解释器) 表示一个电脑的环境,可以在终端输入命令,并接受响应
- 读取(read) - 读取用户输入的js代码
- 执行(exec)- 执行用户输入的js代码
- 打印(print)- 打印用户输入的js代码
- 循环(loop)- 循环操作以上步骤直到用户两次按下 ctrl-c 按钮退出
类似于浏览器的调试窗口,可执行简单的表达式运算,这里不多做解释。
附上一些常用的命令
- ctrl + c 按下两次 - 退出 Node REPL
- ctrl + d - 退出 Node REPL
- 向上/向下 键 - 查看输入的历史命令
- .help - 列出使用命令
在E盘放入一个hello.js,内容为console.log("hello")
,执行终端
自定义模块
1.创建一个模块(一个js就是一个模块)
2.导出一个模块(module.exports=name)
3.引入一个模块并且调用(require)
这里用个例子来解释模块的导出以及导入
dog.js功能模块
let dog={
dogname:'wangcai',
say(){
console.log('wangwang')
}
}
module.exports=dog
main.js引用模块
//引入模块,赋值给cal
const Module=require('./dog')
console.log(Module.dogname);
Module.say()
终端执行
内置模块
文件操作
fs 模块提供了一个 API,用于以模仿标准 POSIX 函数的方式与文件系统进行交互。
这里全部操作只讲解异步操作,同步操作请参照官方文档,同步操作中,可以用try catch语句捕获异常。
异步读取文件目录
fs.readdir(path[, options], callback)
参数 | 解释 |
---|---|
path | 路径 |
options | 可选参数,encoding(可选编码默认UTF-8),withFileTypes(默认false)) |
callback | 回调函数,err(为null则成功,否则返回错误信息),files(目录中的文件名的数组) |
文件目录
代码
const fs=require('fs')
fs.readdir('./',(err,file)=>{
//读取正确err为null,否则为错误信息
if (err) throw err
console.log(file)
})
fs.readdir('./dir',(err,file)=>{
if (err) throw err
console.log(file)
})
输出结果
创建目录
fs.mkdir(path[, options], callback)
参数 | 解释 |
---|---|
path | 路径 |
options | 可选参数,recursive(recursive 属性默认值: false),mode( Windows 上不支持默认值: 0o777) |
callback | 回调函数,err(为null则成功,否则返回错误信息) |
代码
const fs=require('fs')
fs.mkdir('./test',(err)=>{
if (err) throw err
console.log('创建目录成功')
})
重命名
fs.rename(oldPath, newPath, callback
参数 | 解释 |
---|---|
oldPath | 原文件名字 |
newPath | 重命名后的文件名字 |
callback | 回调函数,err(为null则成功,否则返回错误信息) |
代码
const fs=require('fs')
fs.rename('./test','./test1',(err)=>{
if(err) throw err
console.log("重命名成功")
})
写文件
fs.writeFile(file, data[, options], callback)
参数 | 解释 |
---|---|
file | 文件名或文件描述符 |
data | 写入的数据 |
options | 可选参数,encoding(可选编码默认UTF-8),mode(模式),flag(文件系统标志默认w) |
callback | 回调函数,err为null则成功,否则返回错误信息 |
const fs=require('fs')
fs.writeFile('./test.txt','HelloWorld',['utf-8','w'],(err)=>{
if (err) throw err
console.log('文件写入成功')
})
读文件
fs.readFile(path[, options], callback)
参数 | 解释 |
---|---|
path | 文件路径 |
options | 可选参数encoding(可选编码默认null),flag(文件系统标志默认r) |
callback | 回调参数,err(错误信息),data(返回的数据,如果没指定编码,返回原始的 buffer) |
const fs=require('fs')
fs.readFile('./test.txt','utf-8',(err,data)=>{
if(err) throw err
console.log(data)
})
删除文件
fs.unlink(path, callback)
代码
const fs=require('fs')
fs.unlink('./test.txt',(err)=>{
if(err) throw err
console.log('文件删除成功')
})
url处理
url 模块用于处理与解析 URL
url.parse在最新node中为遗留属性,这里不做讲解,采用URL类进行讲解
URL 类
new URL(input[, base])
参数 | 解释 |
---|---|
input | 要解析的绝对或相对的 URL。如果 input 是相对路径,则需要 base。 如果 input 是绝对路径,则忽略 base |
base | 如果 input 不是绝对路径,则为要解析的基本 URL |
代码
const myURL = new URL('https://user:pass@sub.example.com:8080/p/a/t/h?query=string#hash')
console.log(myURL.href)//整个连接
console.log(myURL.protocol)//协议
console.log(myURL.username)//用户名
console.log(myURL.password)//密码
console.log(myURL.host)//主机
console.log(myURL.hostname)//主机名
console.log(myURL.port)//端口号
console.log(myURL.pathname)//路径名
console.log(myURL.search)//请求参数
console.log(myURL.hash)//哈希值
console.log(myURL.searchParams)//URL查询参数的URLSearchParams对象
结果
URLSearchParams 类
URLSearchParams API 提供对 URL 查询部分的读写权限
方法/属性 | 含义 |
---|---|
.append(name, value) | 在查询字符串中附加一个新的键值对 |
.delete(name) | 删除所有键为name的键值对 |
.get(name) | 返回键是name的第一个键值对的值。如果没有对应的键值对,则返回null |
.getAll(name) | 返回键是name的所有键值对的值,如果没有满足条件的键值对,则返回一个空的数组 |
.has(name) | 如果存在至少一对键是 name 的键值对则返回 true |
.set(name, value) | 将 URLSearchParams 对象中与 name 相对应的值设置为 value。 如果已经存在键为 name 的键值对,则将第一对的值设为 value 并且删除其他对。 如果不存在,则将此键值对附加在查询字符串后 |
.toString() | 返回查询参数序列化后的字符串,必要时存在百分号编码字符 |
.entries() | 在查询中的每个键值对上返回一个 ES6 Iterator。 迭代器的每一项都是一个 JavaScript Array。 Array 的第一个项是键 name, Array 的第二个项是值 value |
.keys() | 在每一个键值对上返回一个键的 ES6 Iterator |
.values() | 在每一个键值对上返回一个值的 ES6 Iterator |
代码
const myURL = new URL('https://user:pass@sub.example.com:8080/p/a/t/h?query=string#hash')
//.searchParams方法返回的已经是个URLSearchParams类
console.log(myURL.searchParams.get('query'))
//构建一个URLSearchParams类
let params = new URLSearchParams('a=1&a=2&b=3');
params.append('c', '3')
console.log(params.toString())
params.delete('b')
console.log(params.toString())
console.log(params.getAll('a'))
console.log(params.has('a'))
params.set('a', '4')
console.log(params.toString())
for(let arr of params.entries()){
console.log(arr[0]+'-'+arr[1])
}
for (let name of params.keys()) {
console.log(name);
}
for (let value of params.values()) {
console.log(value);
}
结果
更多请点击url官方文档
第三方模块使用实例
在npm的官网上,有许多第三方模块,这里以发送邮件的模块–nodemailer为例,讲述如何使用第三方模块
0.初始化npm项目
进入终端,执行npm init
输入命令后,分别填写项目信息,也可以不填,填写完成后输入yes。
1.首页搜索nodemailer
2.找到帮助文档或者官网
3.安装模块
npm install nodemailer
4.查看示例使用并且修改
'use strict';
//引入node模块
const nodemailer = require('nodemailer');
//创建发送邮件的对象
let transporter = nodemailer.createTransport({
host: 'smtp.qq.com', //发送方邮箱类型主机号,通过node包里的well-known里services.json查询
port: 465, //端口号
secure: true, // 465端口为true, 其他端口为false
/*通过well-known查询的qq邮箱信息
"QQ": {
"domains": ["qq.com"],
"host": "smtp.qq.com",
"port": 465,
"secure": true
},
*/
auth: {
user: 'xxxxxxxxx@qq.com', //发送方邮箱地址
pass: 'xxxxxxxxx' //mtp验证码
}
/*
qq邮箱mtp开启方法
设置-账户-POP3/IMAP/SMTP/Exchange/CardDAV/CalDAV服务
开通POP3/SMTP服务,其他自行查找或百度
*/
});
transporter.sendMail({
from: '"HelleWorld有限公司" <xxxxxx@qq.com>', // 发件方地址
to: 'xxxxxxxx@qq.com,xxxxxxxx@qq.com', // 多个邮箱用逗号隔开
subject: '验证码', // 主题
text: '您的验证码信息是123456', // 文本信息
html: '<b>您的验证码信息是123456</b>' // html信息,若使用text则html无效
});
简易爬虫
通过get方法获取目标网站,通过cheerio分析网站内容
1.数据处理部分
/*基本构架*/
//根据协议引入http或者https模块
const http=require('http')
let url='https://movie.douban.com/'
http.get(url,(res)=>{
//数据分段,只有接受数据会触发data 事件chunk每次接受新的数据片段
res.on('data',chunk=>{
console.log('数据传输')
})
//数据传输完成
res.on('end',()=>{
console.log('数据传输完毕')
})
}).on('error',(err)=>{
console.log('请求错误')
})
案例:将豆瓣电影的网页爬取下来存在一个html页面下
const http = require('https')
const fs = require('fs')
let url = 'https://movie.douban.com/'
http.get(url, (res) => {
//数据分段,只有接受数据会触发data 事件chunk每次接受新的数据片段
let rawData = ''
res.on('data', chunk => {
rawData += chunk
})
//数据传输完成
res.on('end', () => {
fs.writeFile('./douban.html', rawData, (err) => {
if (err) throw err
console.log('文件写入成功')
})
})
}).on('error', (err) => {
console.log(err)
})
2.安全判断
const http = require('https')
const fs = require('fs')
let url = 'https://movie.douban.com/'
http.get(url, (res) => {
//状态码
const { statusCode } = res
//const statusCode=res.statusCode
//请求头
const contentType = res.headers['content-type']
let error
if (statusCode !== 200) {
error = new Error('请求状态错误');
} else if (!/^ text\/html/.test(contentType)) {
//正则验证
error = new Error('请求类型错误');
}
if (error) {
console.log(error);
res.resume();//清除缓存
return false;
}
}).on('error', (err) => {
console.log(err)
})
3.分析网站内容
通过npm命令安装cheerio模块
npm install cheerio
类似于jq选择器选择元素
const cheerio = require('cheerio')
const $ = cheerio.load('<div class="box"><img src="pictruelink1"><img src="pictruelink2"></div>')
// 类似于jq选择器的各种用法
//$('.box').text('title')
//$('.box').addClass('welcome')
//console.log($.html())
$('img').each((index,el)=>{
console.log(index)
console.log($(el).attr('src'))
})
案例:将豆瓣电影所有图片地址爬取下来打印
const http = require('https')
const cheerio = require('cheerio')
let url = 'https://movie.douban.com/'
http.get(url, (res) => {
//安全判断
//状态码
const { statusCode } = res
//const statusCode=res.statusCode
//请求头
const contentType = res.headers['content-type']
let error
if (statusCode !== 200) {
error = new Error('请求状态错误');
} else if (!/^text\/html/.test(contentType)) {
//正则验证
error = new Error('请求类型错误');
}
if (error) {
console.log(error);
res.resume();//清除缓存
return false;
}
//数据分段,只有接受数据会触发data 事件chunk每次接受新的数据片段
let rawData = ''
res.on('data', chunk => {
rawData += chunk
})
//数据传输完成
res.on('end', () => {
const $ = cheerio.load(rawData)
$('img').each((index,el)=>{
console.log($(el).attr('src'))
})
})
}).on('error', (err) => {
console.log(err)
})
express框架
安装方式
npm install express
HelloWorld
const express=require('express')
//express实例化
const app=express()
//get方法,接受前端的get请求
app.get('/',(req,res)=>{
res.send('Hello World')
})
//监听9501端口,运行服务器
app.listen(9501,()=>{
console.log('Server Start')
})
在浏览器运行http://localhost:9501/开始服务器
get接收参数
.query属性接收get方法传过来的数据
const express=require('express')
//express实例化
const app=express()
app.get('/login',(req,res)=>{
//console.log(req.query)
//打印结果
//{ username: 'bob', password: '123456' }
let { username,password}=req.query
if(username==='bob'&&password==='123456'){
res.send({code:0,msg:'login success'})
}else{
res.send({code:-1,msg:'login fail'})
}
})
app.listen(9501,()=>{
console.log('Server Start')
})
在浏览器输入http://localhost:9501/login?username=bob&password=123456开启服务器查看运行结果
post接收参数
post解析参数需要用到body-parser插件
安装方法
npm install body-parser
body属性接受post方法传递过来的参数
const express=require('express')
const bodypaser=require('body-parser')
//express实例化
const app=express()
//app.use为中间件的使用方法,见下文
//解析表单数据 application/x-www-form-urlencoded
app.use(bodypaser.urlencoded({ extended: false }))
//解析json格式数据 application/json
app.use(bodypaser.json())
app.post('/login',(req,res)=>{
//console.log(req.body)
// { username: 'bob', password: '123456' }
let { username,password}=req.body
if(username==='bob'&&password==='123456'){
res.send({code:0,msg:'login success'})
}else{
res.send({code:-1,msg:'login fail'})
}
})
app.listen(9501,()=>{
console.log('Server Start')
})
中间件
中间件就是在收到请求之后以及发出响应之前执行的一些函数,也可将其理解成一个拦截器。
原型为:
app.use([path,] function [, function…])
path如果为空,则默认为’/’
中间件分以下三种
1.内置中间件
express.static用来处理静态资源,是express的内置中间件,具体用如下
//在node里建立一个img文件夹放入pic.jpg
const express=require('express')
//引入path路径处理模块
const path=require('path')
const app=express()
//使用内置中间件
//__dirname为当前文件所在的目录,path中的join方法用于拼接路径
//app.use(express.static(path.join(__dirname,'./img')))
//访问http://localhost:9595/pic.jpg即可查看图片
app.use('/tupian',express.static(path.join(__dirname,'./img')))
//访问http://localhost:9595/tupian/pic.jpg即可查看图片
app.listen(9595,()=>{
console.log('Server Start')
})
2.自定义中间件
自定义中间件的函数为function(req,res,next),可以为全局的,可以为局部的。next表示是否继续往下执行
例:多端口验证token(这里token先用一个随机的字符串表示)
const express=require('express')
const app=express()
//先进入中间件,判断传入的是否有一个参数为token有则往下执行,没有则返回结果,不往下执行
app.use('/',(req,res,next)=>{
let {token}=req.query
if(token){
next()
}else{
res.send('缺少token')
}
})
app.get('/test1',(req,res)=>{
res.send('test1')
})
app.get('/test2',(req,res)=>{
res.send('test2')
})
app.listen(9595,()=>{
console.log('Server Start')
})
//访问http://localhost:9595/test1,显示缺少token
//http://localhost:9595/test2?token=fsdklv,显示test2
例:路由的封装
express.Router()方法封装路由,封装路由可以让工程模块化。
建立一个router.js
const express=require('express')
//express.Router()方法封装路由
const router=express.Router()
router.get('/login',(req,res)=>{
res.send("this is login")
})
router.get('/register',(req,res)=>{
res.send("this is register")
})
//导出router
module.exports=router
main.js
const express=require('express')
//引入路由
const userRouter=require('./router')
const app=express()
//中间件,路径为/user进入封装好的路由
app.use('/user',userRouter)
app.listen(9595,()=>{
console.log('Server Start')
})
//访问http://localhost:9595/user/login 显示this is login
//访问http://localhost:9595/user/register,显示this is register
3.第三方中间件
上文中使用body-parser插件就是第三方中间件的用法
mysql
安装mysql依赖
npm install mysql
数据库连接
var mysql = require('mysql');
var connection = mysql.createConnection({
host: 'localhost',//主机地址 (默认:localhost)
user: 'root',//用户名
password: 'logan123',//密码
database: 'testDB'//数据库名
});
//连接数据库
connection.connect((err) => {
if (err) throw err
console.log('数据库连接成功')
});
//断开数据库
connection.end();
sql语句操作。
具体的sql语句的用法这边不做讲解,以查询数据和插入数据为例。
user数据表
var mysql = require('mysql');
var connection = mysql.createConnection({
host: 'localhost',//主机地址 (默认:localhost)
user: 'root',//用户名
password: '******',//密码
database: 'testDB'//数据库名
});
//连接数据库
connection.connect((err) => {
if (err) throw err
console.log('数据库连接成功')
});
connection.query("select * from user",(err, result)=>{
if(err) throw err
console.log(result)
})
connection.query("insert into user values(4,'rose','789654')",(err, result)=>{
if(err) throw err
console.log(result)
})
//断开数据库
connection.end();
执行结果
user表
防sql注入
var mysql = require('mysql');
var connection = mysql.createConnection({
host: 'localhost',//主机地址 (默认:localhost)
user: 'root',//用户名
password: '******',//密码
database: 'testDB'//数据库名
});
//连接数据库
connection.connect((err) => {
if (err) throw err
console.log('数据库连接成功')
});
let addsql="insert into user values(?,?,?)"
let addSqlParams = [4, 'rose','789654'];
connection.query(addsql,addSqlParams,(err, result)=>{
if(err) throw err
console.log(result)
})
//断开数据库
connection.end();
解决跨域
cors模块
express中可用cors来解决跨域问题
安装方法
npm install cors
使用
const cors=require('cors')
const app=express()
app.use(cors())
文件上传
安装multer模块
npm install multer
配置storage(磁盘存储引擎)
const storage = multer.diskStorage({
destination: (req, file, cb) => {
//cb第二个参数为存储的路径
cb(null, './upload')
},
filename: (req, file, cb) => {
//cb第二个参数为文件的名字
cb(null, file.originalname)
}
})
实例化multer
const upload = multer({
storage: storage
});
post方法使用
//.single为单个文件上传。键名为'key'
app.post('/upload', upload.single('key'), (req, res) => {
res.send('ok');
})
优化代码
1.设置上传文件大小数量限制
const uploadConfig = multer({
limits:{
fileSize: 50*1024,
//上传文件数
files: 5,
}
}
//若文件大小不正确,则会抛出一个错误
2.设置文件上传类型
const uploadConfig = multer({
limits:{
fileSize: 50*1024,
files: 5
},
fileFilter:(req,file,cb)=>{
if(file.mimetype == 'image/jpeg'||file.mimetype == 'image/jpg'){
cb(null, true)
} else {
cb(null, false)
//抛出一个错误
cb(new Error())
}
}
})
//这里若文件类型不正确,则不会接收,但是不会抛出错误,所以需要手动抛出一个错误
完整代码
const uploadConfig = multer({
limits: {
fileSize: 100*1024,
files: 5
},
storage: multer.diskStorage({
destination: (req, file, cb) => {
//cb第二个参数为存储的路径
cb(null, './upload')
},
filename: (req, file, cb) => {
//截取文件的后缀名
let exts = file.originalname.split('.')
//取最后一个元素
let ext = exts[exts.length - 1]
//文件名为时间戳加上4个随机数字
let filename = (new Date()).getTime()
let randnum = parseInt(1000 + Math.random() * 8999)
cb(null, `${filename}${randnum}.${ext}`)
}
}),
fileFilter: (req, file, cb) => {
if (file.mimetype == 'image/jpg' || file.mimetype == 'image/jpeg') {
cb(null, true)
} else {
cb(null, false)
//抛出一个错误
cb(new Error())
}
}
})
let upload = uploadConfig.single('key')
app.post('/upload', (req, res) => {
upload(req, res, (err) => {
if (err instanceof multer.MulterError) {
return res.send(err.message)
} else if (err) {
//此处接收文件类型限制抛出的错误
return res.send("type error")
}
res.send("sucess")
})
})
JWT
JSON Web Token基于Token的身份验证
安装
npm install jsonwebtoken
生成token
jwt.sign(payload, secretOrPrivateKey, [options, callback])
payload-存储加密信息
secretOrPrivateKey-key,加密的钥匙
expiresIn-设置有效期,不带单位默认为秒。也可以是时间跨度字符串
let payload={
us:'test',
ps:'123456'
}
let secretOrPrivateKey="dsadas"
jwt.sign(payload,secretOrPrivateKey, {expiresIn:60*60},(err,token)=>{
if(err) throw err
console.log(token)
})
解密token
jwt.verify(token, secretOrPublicKey, [options, callback])
token-由jwt.sign生成的token
secretOrPrivateKey-加密的key,用于解析token的payload里的信息
let token="eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJ1cyI6IjEyMyIsInBzIjoiMTIzIiwiaWF0IjoxNTc1MzQyNjI4LCJleHAiOjE1NzU0MjkwMjh9.Fk7w2zeve5NNLYRJpt3ErIR0INvpFgpdi_4oNr9xju0"
let secretOrPrivateKey="dsadas"
jwt.verify(token, secretOrPrivateKey, (err, data) =>{
if(err.name==="TokenExpiredError"){
console.log("token过期")
}else if(err.name==="TokenExpiredError"){
console.log("无效的token")
}
else{
console.log(data)
}
})