三阶段笔记
node基础(写作简单api接口)
-
node.js
-
javascript运行环境
-
javascript运行在浏览器的时候:操作DOM,操作BOM,语法ECMAScript
-
javascript运行在node的时候:
-
操作文件
-
操作数据库
-
开启web服务 ...
-
不能操作DOM,不能操作BOM
-
-
-
node.js的安装
-
版本识别: 16.14.2
node -v
-
LTS: 建议安装稳定版 Current: 最新版本
-
主版本号.子版本号.修订版本号 v16.14.2
-
子版本号是偶数的稳定版本,是奇数的不稳定版本
-
修订版本号的变化是内部bug的一些处理
-
建议安装LTS的偶数版本
-
-
-
在node环境中运行js代码
-
方法一:(不推荐)
-
直接在终端运行: node 回车
-
进入js可以输入编辑环境js代码
-
-
方法二:
-
把你要在node在环境中运行js代码写在一个js文件
-
终端运行: node js文件路径
-
-
node的版本管理
-
我要在公司管理多个node项目
-
项目一: node 10.16.0
-
项目二: node 14.12.2
-
需要在电脑上安装多个不同的node随时切换版本 就可以使用nvm这个node版本管理工具
-
-
-
-
API
-
(路径模块)
-
**path.join()**
-
将所有给定的路径拼接成完整的路径
-
标准化生成的路径
-
:path.join目录2',目录3/目录4',目录5'
-
:/目录1/目录2/目录3/目录4/目录5/目录
-
-
**path.resolve()**
-
同path.join()
-
区别:将路径或路径片段转换为绝对路径
-
path.resolve(__dirname,'static')
-
-
-
url模块
-
**url.parse()**
-
:将网站分析成对象
-
:
url.parse(urlString[,parseQueryString, slashesDenoteHost])
-
urlString:表示url地址
-
parseQueryString:如果是布尔值true,和解析查询字符串,否则不分析
-
slashesDenoteHost:如果为true表示//后面,/前面是主机名(如果是完整的url地址不影响)
-
:url对象
-
-
**url.format()**
-
: 把url对象解析成url地址字符串
-
: url.format(url对象)
-
: url地址字符串
-
-
**url.resolve()**
-
:把两段url构成一个完整的片段url
-
: url地址字符串
-
-
-
querystring模块(对查询字符串进行更强的分析)
-
** querystring.stringify() **
-
:将对象转换为查询字符串
-
默认使用&分隔(由第二个参数决定)
-
var o1 = querystring.stringify({ name:'pipi', course:[11,22,33] },',') //name=pipi,course=11,course=22,course=33
-
-
键值用=连接(连接由第三个参数决定)
-
var o1 = querystring.stringify({ name:'pipi', course:[11,22,33] },, //name:pipi,course:11,course:22,course:33
-
-
:查询字符串
-
var o1 = querystring.stringify({ name:'kimi', course:['nodejs','vue','react'] }) //name=kimi&course=nodejs&course=vue&course=react
-
-
**querystring.parse()**
-
:将查询字符串转换为对象
-
:querystring.parse(查询字符串要转换[,自定义分隔符,自定义键值
-
:对象
-
var o1 = querystring.parse("name=kiki&pwd=1234") // { name: 'kiki', pwd: '1234' }
-
-
** querystring.escape()**
-
字符串url编码
-
var o1 = querystring.escape("http://www.baidu.com/search?wd=千锋教育"); //http://www.baidu.com/search?wd=?%8%83%E9%94%8B%E6%95%99%E8%82%B2
-
-
** querystirng.unescape()**
-
对url进行解码
-
var o1 = querystring.unescape('http://www.baidu.com/search?wd=千锋教育') //http://www.baidu.com/search?wd=千锋教育
-
-
-
fs模块
-
__filename:当前文件的绝对路径
-
__dirname: 当前文件所在目录的绝对路径
-
fs.stat(获取文件/文件夹信息)
-
fs.stat(__filename,(err,stats)=>{ // err:是错误信息 // stats:获取到的文件信息 console.log(stats) ;// 对象 // 判断是不是文件 console.log(stats.isFile()); // 判断是不是文件夹 console.log(stats.isDirectory()); })
-
-
fs.mkdir(创建文件夹)
-
fs.mkdir('./public',(err)=>{ if(!err){ // 如果没有错误信息 console.log('创建文件夹成功') } })
-
-
fs.writeFile(向文件写入内容 异步)
-
fs.writeFile(filePath,text,(err)=>{ if(!err){ console.log('文件写入成功') } })
-
-
fs.appendFile (向文件追加内容 异步)
-
fs.appendFile(filePath,text,(err)=>{ if(!err){ console.log('成功写入文件') } })
-
-
fs.readFile(读取文件)
-
//方法一 fs.readFile(filePath,(err,data)=>{ if(!err){ // 编码格式转换ss console.log(data.toString())ss } })
-
//方法二 fs.readFile(filePath,'utf-8',(err,data)=>{ if(!err){ console.log(data) } })
-
-
fs.readdir(读取文件目录)
-
const dirPath = path.resolve(__dirname,'public') // 读取文件目录 fs.readdir(dirPath,(err,files)=>{ // err:错误 // files:是一个数组,里面是读取到的文件夹里面的子文件/子文件夹名称 if(!err){ console.log(files) } }) //返回一个s
-
-
fs.rename(文件名修改)
-
fs.rename("要重命名的文件地址","新名字",(err)=>{ if(!err){ console.log('文件名修改成功') } })
-
const fs = require('fs') const path = require('path') const filePath = path.resolve(__dirname,'public','js/a.js') const newName = path.resolve(__dirname,'public','js/index.js') fs.rename(filePath,newName,(err)=>{ if(!err){ console.log('文件名修改成功') } })
-
-
fs.rm(删除文件)
-
fs.rm(filePath,(err)=>{ if(!err){ console.log('文件删除成功') } })
-
-
fs.rm(删除文件夹)
-
s.rm(dirPath,{recursive:true},(err)=>{ if(!err){ console.log('文件夹删除成功') } })
-
注:需要添加{recursive:true},表示递归删除文件夹里面的内容再删除文件夹
-
-
-
events模块
-
创建事件触发器(第一步)
-
const EventEmitter = require('events');
-
const emitter = new EventEmitter()
-
-
定义事件监听器(第二步)
-
emitter.on
(可触发多次) -
emitter.on('many',()=>{ console.log('many事件发生了') })
-
emitter.once
(只触发一次) -
emitter.once('one',()=>{ console.log('one事件发生了') })
-
触发(emit)
-
emitter.emit('many') emitter.emit('one')
-
-
-
-
stream模块(流模块)
-
fs.createReadStream(fromPath)
(inp) -
fs.createWriteStream(toPath)
(out) -
-
once
-
inp.once('data',(chunk)=>{ //chunk获取到的数据流 })
-
-
on
-
inp.on('data',(chunk)=>{ //chunk获取到的数据流 })
-
inp.on('end',()=>{ console.log('数据读取完成'); out.end() })
-
inp.on('error',()=>{ console.log('数据写入错误') })
-
-
pipe
-
inp.pipe(out) //文件读取流(inp)与 文件写入流(out)之间建立一个管道
-
-
-
-
http模块
-
http.request()
-
语法:
-
第一步: 书写请求行
-
const req = http.request(url,callback)
const req = http.request(options,callback)
-
-
第二步: 书写请求体
-
req.write()
-
-
第三步: 结束请求
-
req.end()
-
-
-
例子
-
//方法一 const https = require('https') const url = "https://www.lagou.com/" let html = ""; const req = https.request(url,(res)=>{ // 设置响应的编码集 res.setEncoding('utf-8'); // 当有数据可以读取的时候触发data事件 res.on('data',(chunk)=>{ html += chunk; }) // 当数据读取完成的时候触发end事件 res.on('end',()=>{ console.log(html) }) // 当有数据读取错误的时候触发error事件 res.on('error',(err)=>{ console.log(err) }) }) req.end()
-
方式二 const req = https.request({ host:"www.lagou.com", ports:"443", method:'get', path:'/', headers:{} },(res)=>{ // 设置响应的编码集 res.setEncoding('utf-8'); // 当有数据可以读取的时候触发data事件 res.on('data',(chunk)=>{ html += chunk; }) // 当数据读取完成的时候触发end事件 res.on('end',()=>{ console.log(html) }) // 当有数据读取错误的时候触发error事件 res.on('error',(err)=>{ console.log(err) }) }) req.end();
-
-
-
http.get()
-
语法:同http.request()
-
区别:
-
不用设置method,是get请求
-
不用书写req.write(),get请求没有请求主体
-
不用调用req.end(),会自动调用
-
-
例子:
-
https.get(url,(res)=>{ // 设置响应的编码集 res.setEncoding('utf-8'); // 当有数据可以读取的时候触发data事件 res.on('data',(chunk)=>{ html += chunk; }) // 当数据读取完成的时候触发end事件 res.on('end',()=>{ console.log(html) }) // 当有数据读取错误的时候触发error事件 res.on('error',(err)=>{ console.log(err) }) })
-
-
-
http.createServer()
-
作用:开启服务器
-
nodejs实现webServer
-
原理:根据req.url来区分客户的请求路径,根据不同的访问路径,给用户响应不同的资源
-
const http = require('http'); const fs = require('fs') const path = require('path') let num = 0; const server = http.createServer((req,res)=>{ // 只要有客户端请求,就会执行这个函数 // req:请求对象 // res:响应对象 // 获取请求路径 let url = req.url; if(url!="/favicon.ico"){ if(url=="/"){ url = "/index.html" } // 设置响应头 res.setHeader('content-type',"text/html;charset=utf-8") // 通过请求路径,拼接出文件的本地地址 const filePath = path.resolve(__dirname,'public'+url) fs.stat(filePath,(err,stats)=>{ // 文件路径不存在 if(err){ res.statusCode = 404; res.end(`${url} 不存在`) } // 如果是一个文件 else if(stats.isFile()){ res.statusCode = 200; fs.createReadStream(filePath).pipe(res) } // 如果是一个文件夹 else if(stats.isDirectory()){ fs.readdir(filePath,(err,files)=>{ res.statusCode = 200; res.end(files.join('/')); }) } }) } }) server.listen(8080,()=>{ console.log('服务器开启在:http://10.20.158.132:8080') })
-
-
: 没有http.post方法
-
-
-
cheerio(第三方模块)
-
安装:
npm install cheerio -D
-
使用方法和jquery类型
-
jquery操作的是dom节点
-
cheerio操作html字符串
-
可用于爬虫(具体看E:\2022资料\tree-grup\资料\01node-api\源代码\spider.js)
-
-
express(web 开发框架)
-
:
npm install express -S
-
使用三步走
-
//导入: const express = require('express'); const path = require('path') const app = express() //静态资源中间件函数: express自己写好的,你可以直接使用 //express.static(root, [options]) app.use(express.static(path.resolve(__dirname,'文件夹名'))) //监听 app.listen(9090,()=>{ console.log("http://10.20.158.121:9090") })
-
[利用 Express 托管静态文件 - Express 中文文档 | Express 中文网 (expressjs.com.cn)](https://www.expressjs.com.cn/starter/static-files.html)
-
-
-
app.use('路由',中间件函数)
-
如果不写路由,就可以匹配所有的请求
-
如果写路由,就必须以路由开头的才能匹配
-
app.use('/abc',(req,res)=>{ //只要有请求,就会执行这个函数是经过express封装的请求对象,可以使用原生node的方法,也有自己的方法 //req:是经过express封装的请求对象,可以使用原生node的方法,也有自己的方法 //res:响应对象,可以使用原生node的方法,也有自己的方法 res.end('all') })
-
-
路由中间件
-
如何实现?
-
一个主js文件(main.js)
-
const express = require('express'); // 这个包是为了解析请求主体(要安装) const bodyParser = require('body-parser') const app = express() app.use(bodyParser.json()) // 解析 application/json 格式的请求主体 app.use(bodyParser.urlencoded({ extended: true })) // 解析 application/x-www-form-urlencoded 格式的请求主体 app.use('/user',require('./routers/user.js')) app.listen(8080)
-
-
一个专门存储接口api的文件夹(routers),里面放接口文件(user.js)
-
根据需求写接口
-
// 用户相关接口 // /user/login?un=xxx&pw=xxx // /user/register?un=xxx&pw=xxx
-
-
// 路由中间件 const express = require('express'); const router = express.Router(); // 登录接口 router.get('/login',(req,res)=>{ // 获取请求的查询字符串 console.log(req.query); res.json(req.query) }) // 注册接口 router.post('/register',(req,res)=>{ // 如果要使用req.body获取请求主体 // 需要安装: npm install body-parser -S // 获取请求的请求主体 console.log(req.body) res.json(req.body) }) 注意:导出 module.exports = router;
-
-
-
app.get('路由',中间件函数)
-
匹配get请求,路由要求精确匹配
-
如果有写*,表示匹配所有路由
-
app.get('/login',(req,res)=>{ // express封装的res上的方法 // res.send('字符串') res.send('hello world') // res.json(对象) res.json({a:1,b:2}) })
-
响应:(选其一)
-
res.send('字符串')
-
res.json(对象)
-
-
-
app.post('路由',中间件函数)
-
匹配post请求,路由要求精确匹配
-
如果有写*,表示匹配所有路由
-
app.post('/api',(req,res)=>{ res.json({path:'/api',message:"post"}) })
-
-
app.all('路由',中间件函数)
-
匹配所有请求,路由要求精确匹配
-
如果有写*,表示匹配所有路由
-
专门用于处理404的
-
app.all('*',(req,res)=>{ res.json({code:404}) })
-
-
模板中间件(后端把html拼接好,直接返回给前端可以使用ejs模板引擎进行文件渲染)
-
:
npm install ejs -S
-
:(在当前这js文件的同级下有一个views的文件夹,里面存储list.ejs文件用来写模板中间件)
-
app.set('views',path.resolve(__dirname,'views'));
告诉app,模板文件所在的根目录 -
告诉app,模板文件用哪个包解析
app.set('view engine','ejs');
-
res.render('在设置的模板文件目录中的模板文件路径',要在模板文件中使用的变量集合)
-
const express = require('express'); const path = require('path') const app = express(); // 后端把html拼接好,直接返回给前端 // 可以使用ejs模板引擎进行文件渲染 // 下载: npm install ejs -S // 配置: // 告诉app,模板文件所在的根目录 app.set('views',path.resolve(__dirname,'views')); // 告诉app,模板文件用哪个包解析 app.set('view engine','ejs'); app.get('/data',(req,res)=>{ // 在配置了模板引擎以后,res就多了一个方法 : // res.render('在设置的模板文件目录中的模板文件路径',要在模板文件中使用的变量集合) // 在views/list.ejs中可以使用变量:age和arr和count // 把解析完的结果返回 res.render('list',{ age:"<h1>hello world</h1>", arr:[ {name:'吴波',task:'纪律委员'}, {name:'海文',task:'学习委员'}, {name:'小磊',task:'班级班长'}, ], count:100 }) }) app.listen(9090)
-
-
-
-
路由
-
运行跨域请求
-
下载: npm install cors -S
-
导入:
const cors = require('cors')
-
使用:
app.use(cors())
-
-
解析请求主体
-
npm install body-parser
-
app.use(bodyParser.json()) // 解析 application/json 格式的请求主体 app.use(bodyParser.urlencoded({ extended: true })) // 解析 application/x-www-form-urlencoded 格式的请求主体
-
-
-
mongodb
-
mysql:关系型数据库 -- 表结构
-
mongodb:非关系型数据库 -- 数据库里面是一个一个的json文件
-
在术语上
-
数据库: database
-
mysql => 数据库里面有表: table
-
mongodb => 数据库里面有集合: collection
-
mysql => 表里面有数据行: row
-
mongodb => 集合里面有文档: document
-
-
常用的shell命令:
-
1、帮助命令
-->help
-->db.help()
2、数据库操作命令
-->show dbs
-->use dbname 切换数据库
-->db / db.getName() 查看当前数据库名称
-->db.stats() 显示当前DB的状态
-->db.version() 查看当前DB的版本
-->db.getMongo() 查看当前DB的连接的主机地址
-->db.dropDatabase() 删除当前DB
3、创建数据库和集合
-->use project 不存在就创建,存在就切换至
-->db.createCollection('user') // 创建user集合
-->show dbs
-->show collections / db.getCollectionNames()
-->db.getCollection('music') 获取指定集合
-->db.printCollectionStats() 打印指定集合的状态
4、集合中的文档操作:
-->db.user.insertOne({}) 向集合中插入文档
-->db.user.insertMany([{},{}])
-->db.user.save({})
-->db.user.updateOne({"name":"cyr"}, {$set:{"age":100}})
-->db.user.updateMany({},{$set:{}})
-->db.user.deleteOne({"name":"jiaming"})
-->db.user.deleteMany({})
-->db.user.remove({}) // 要指出删除的条件
-->save和insert的区别:
+ 新增的数据中存在主键,则再次插入相同的主键时insert() 会提示错误
+ 而save() 则更改原来的内容为新内容
+ 没有saveMany命令
5、聚集集合查询
-->db.集合名.find({查询条件对象},{显示对象})
-->db.user.find() 查询所有记录
-->db.user.find({age:22}) 查询age=22的记录
-->db.user.find({age:{$gt: 22}}) 查询age>22的记录
-->db.user.find({age:{$lt: 22}}) 查询age<22的记录
-->db.user.find({age:{$gte: 22}}) 查询age>=22的记录
-->db.user.find({age:{$lte: 22}}) 查询age<=22的记录
-->db.user.find({age:{$gte:20, $lte:30}}) 查询age>=20 && age<=30的记录
-->db.user.find({name:/cyr/}) 查询name中包含'cyr'的记录
-->db.user.find({name:/^cyr/}) 查询name以'cyr'开头的记录
-->db.user.find({},{name:1,age:1}) 查询所有记录,只返回name和age字段(1-显示 0-不显示)
-->db.user.find({age:{$gt:20}},{name:1,age:1}) 查询age>20的记录,只返回name和age字段
-->db.user.find().sort({age:1}) 按age进行升序排列
-->db.user.find().sort({age:-1}) 按age进行降序排列
-->db.user.find({},{name:1,age:1,_id:0}).sort({age:1})
-->db.user.find({name:'cyr',age:22}) 查询name='cyr' && age=22的记录
-->db.user.find().limit(5) 只查询前5条记录
-->db.user.find().skip(10) 查询10条以后的所有数据
-->db.user.find().skip(5).limit(5) 查询第6~10条记录
-->db.user.find({$or:[{age:20},{age:25}]}) 查询age=20或者age=25的记录
-->db.user.findOne() 查询满足条件的第一条记录
-->db.user.find({age:{$gte:25}}).count() 查询满足条件的记录的总条数
-
常用:
-
use 数据库名
-
db.createCollection('user') 创建集合
-
db.user.insertOne({}) 向集合中插入文档
-
-
-
-
node操作mongodb数据库
-
使用第三方模块: mongoose
-
下载:
npm install mongoose -S
-
入门创建五步骤
-
const mongoose = require('mongoose');
-
链接数据库 mongodb://localhost/数据库名
mongoose.connect('mongodb://localhost/qf');
-
创建表(第一个参数:表名尽量用复数,第二个参数:定义表里面的字段)
mongoose.model('表名',表结构
-
const Users = mongoose.model('users', { name:String, age:Number, pw:String, create_time:Number });
-
-
创建一条数据
-
const student = new Users({ name: '程磊', age:12, pw:'123456', create_time: Date.now() });
-
-
把创建好的数据插入集合中
-
student.save().then((doc)=>{ console.log('文档插入集合成功') console.log(doc) })
-
-
-
链接数据库
-
// 1 导入mongoose模块 const mongoose = require('mongoose'); // 2 链接数据库,pinxixi是要链接的数据库名称 mongoose.connect('mongodb://127.0.0.1:27017/pinxixi') // 3 获取数据库链接 const db = mongoose.connection; // 4 事件监听,判断是否链接成功 db.once('open',()=>{ console.log('数据库链接成功') }) db.on('error',(err)=>{ console.log(err) })
-
-
创建数据集合
-
require('./01'); // 导入mongoose模块 const mongoose = require('mongoose'); // 创建集合的字段名和数据类型的规范 const articleSchema = mongoose.Schema({ title:String, content:String, createTime:Number, author:String }) // 根据上面的规范创建一个articles表 const articles = mongoose.model('articles',articleSchema) // 导出创建好的articles表 module.exports = articles;
-
-
插入数据
-
//导入要操作的表 const articlesModel = require('./02') articlesModel.insertMany([ { title:'tree', content:'mongoose 进行插入操作指南3', createTime:Date.now(), author:'多多' }, { title:'four', content:'mongoose 进行插入操作指南4', createTime:Date.now(), author:'次次' } ]).then((doc)=>{ console.log(doc); })
-
-
查询数据
-
//导入要操作的表 const articlesModel = require('./03') //查询title:'tree'的数据 articlesModel.find({title:'tree'}).then((doc)=>{ console.log(doc); }) //查询时间在2022/5/11 13:23:00之前的数据 articlesModel.find({createTime:{$lt:new Date('2022/5/11 13:23:00').getTime()}}).then((doc)=>{ console.log(doc); }) //查询 id articlesModel.findById('627b46c08f5a4300fc74fe7c').then((doc)=>{ console.log(doc); })
-
-
更新数据
-
//更新一条 articlesModel.updateOne({author:"张三"},{$set:{title:"1111"}}).then(result=>{ console.log(result); // 查看更新以后的数据 articlesModel.find({}).then(doc=>{ console.log(doc) }) })
-
//更新多条 articlesModel.updateMany({author:'次次'},{$set:{title:'danger'}}).then(res=>{ console.log(res); articlesModel.find({}).then(res=>{ console.log(res); }) })
-
-
删除数据
-
//删除一条 articlesModel.deleteOne({title:'tree'}).then(res=>{ console.log(res); articlesModel.find({}).then(doc=>{ console.log(doc); }) })
-
//删除多条 articlesModel.deleteMany({title:'four'}).then(res=>{ console.log(res); articlesModel.find({}).then(doc=>{ console.log(doc); }) })
-
-
-
注意:
-
使用模块请先导入
-
如果一个请求经过多个中间件,多个中间的req和res是共享的
-
-
npm包管理工具
-
什么是npm
-
是基于commonjs规范的包管理工具
-
node安装完成以后,npm同步安装
npm -v
-
nrm可以切换仓库镜像源,建议使用淘宝镜像
-
-
管理模块
-
npm init
生成package.json文件 -
作用: 便于模块管理 便于代码转移
-
项目依赖 和 开发时依赖的区别
-
-
模块安装
-
查看指定包信息:
npm info name
-
查看安装了哪些包:
npm list
-
全局安装:
npm install name --global
-
简写: npm i name -g
-
-
本地安装项目依赖:
npm install name --save
-
简写:
npm i name -S
-
-
本地安装开发时依赖:
npm install name --save-dev
-
简写:
npm i name -D
-
-
安装指定版本
-
npm install name@版本号 --save
-
-
模块安装完成,在node_modules文件夹里面
-
这个文件夹一般不随项目转移
-
-
模块卸载
-
npm uninstall name -g
-
npm uninstall name -S
-
-
-
yarn:另一个包管理工具
-
安装yarn:
npm install yarn -g
-
yarn和npm二选一,即可
-
重要概念
-
BSR 客户端渲染(可异步渲染)
-
前端利用ajax等数据交互方式向后端请求数据
-
把数据变成html字符串,进行页面的局部刷新
-
方法:ajax/jsonp/fetch -> 获取数据 -> html节点/html字符串 -> 插入页面
-
优点:灵活,前后端分离,方便前后端更新维护
-
缺点:对SEO不友好,增加http请求次数,减缓了页面加载速度
-
-
SSR 服务端渲染
-
在后端把数据从数据库取出来以后,直接通过模板引擎渲染成html字符串
-
客户端请求页面的时候,直接返回这个页面拼接好的html字符串
-
也就是说后端可以把数据直接插入html字符串
-
优点:对SEO友好,减少http请求次数,加快了页面加载速度
-
缺点:不灵活,前后端耦合性太高
-
-
Rest风格的api设计
-
每一个url代表一种资源
-
客户端使用GET,POST,DELETE,PUT,PATCH等方式请求资源
-
从客户端到服务端的每个请求必须包含理解请求所必要的信息
-
get类型 /user 获取所有用户信息
-
post类型 /user 插入用户信息
-
delete类型 /user/1 删除id=1用户信息
-
put类型 /user/1 更新id=1用户的所有信息
-
patch类型 /user/1 更新id=1用户的部分信息
-
-
安装:
npm install -g json-server
-
创建一个db.json文件,并写入一个接收的数组
-
{ “user”:[] }
-
-
json-server --watch db.json
-
使用postman访问(http://localhost:3000/user)
-
post请求即为添加数据 参数写在body中
-
-
-
跨域问题
-
出现:协议/端口/域名有一个不同就是跨域
-
解决
-
cors跨域
-
在目标服务器设置跨域请求头
-
res.header("Access-Control-Allow-Origin", "*"); res.header("Access-Control-Allow-Methods","PUT,POST,GET,DELETE,== OPTIONS"); res.header("X-Powered-By",' 3.2.1') res.header("Content-Type", "application/json;charset=utf-8");
-
-
一般不自己写,引入一个(给目标文件引入)
-
npm install cors -S
-
const cors = require('cors')
-
app.use(cors())
-
-
-
jsonp
-
不是xhr请求,是利用script的src可以跨域的特点
-
提前准备一个函数,通过src请求后端接口返回这个函数的调用,实参就是需要的数据,函数名通过callback传递: callback=函数名
-
如果a文件需要访问b中的数据
-
a文件中需要准备一个返回的函数,并一个script标签连接请求地址
-
function getList(data){ console.log(data) } 插入一个script标签请求后端接口,后端返回一个函数调用 通过callback参数告诉后端要函数名 <script src="http://localhost:9090/list?callback=getList"></script>
-
b文件则调用
-
const express = require('express'); const app = express(); app.get('/list',(req,res)=>{ res.jsonp({host:"b server",port:9090}) }
-
-
-
代理
-
引入包
http-proxy-middleware
-
使用:
-
导入:
const express = require('express');
const { createProxyMiddleware } = require('http-proxy-middleware');
-
设置
app.use('路径',createProxyMiddleware({
}))
-
app.use("/list",createProxyMiddleware({ target:'http://localhost:9090/', changeOrigin:true }))
-
-
-
-
token
-
token引入
-
客户端频繁向服务器发送请求,大量的接口需要权限验证,就需要大量的数据库查询用户名和密码是有有效,服务器压力大,有没有一种办法可以不用频繁进行数据库验证,token就是为此产生的
-
-
token长什么样?
-
LKJjLHhlHwerJlkhl324HyiyHhIUYiyGkgIUYG234kgIUYTGKgIUYiuyG
-
-
token的目的
-
减轻服务器的压力
-
减少频繁的查询数据库
-
-
token从哪里来
-
token是在服务器产生的,当客户端登录或者调用接口的时候,会返回token,客户端用户收到token以后,保存在前端(比如保存在localStorage中),之后请求其他有访问权限的后端接口时,需要把token携带上传递给后端进行验证
-
-
如何把token传递给后端呢?
-
这要根据后端的需要,通常会把token放在headers中进行传递
-
-
使用:
-
下载包:
npm install jsonwebtoken
-
[jsonwebtoken - npm (npmjs.com)](https://www.npmjs.com/package/jsonwebtoken)
-
WebSocket
-
是什么
-
用于建立服务器和客户端的双向交互通信,打开一条通道,客户端可以主动给服务器发消息,服务器也可以主动给客户端发消息,这个链接的一端就被称为socket
-
-
主要功能
-
向对方发送消息
-
基于事件驱动接收对方发送的消息
-
-
WebSocket和socket.io之间的区别
-
socket.io是一个开源库,他对WebSocket进行封装
-
增强了浏览器的兼容性
-
使用起来更方便功能更强大
-
使用on绑定事件监听
-
如果绑定的是自定义事件,用emit触
-
-
案例(E:\2022资料\tree-grup\test\04-day\socketserver)
-
聊天室开发
-
socket服务器开发(socket底层是基于net模块的)
== 接收客户端的消息(基于事件驱动的)
== 群发消息给所有客户
-
socket客户端开发
== 接收服务端的消息(基于事件驱动的),要渲染在页面上
== 把消息发送给服务器
-
解释
-
socket:就是链接到socket服务器的另一端
-
通过io可以群发
-
通过socket可以私聊
-
-
例子:
-
// const socket = io('http://10.20.158.121:9090') const socket = io('http://192.168.0.106:9090') let count = 0 socket.on('message-from-server',(message)=>{ count++ $('#top').append('<div class="row">'+message+'</div>') $('#top').scrollTop(count*35) }) $('#send').click(()=>{ let val = $('#message').val().trim() socket.emit('message-from-client',val) $('#message').val('') }) $('#message').keyup((e)=>{ if(e.keyCode == 13){ let val = $('#message').val().trim(); socket.emit('message-from-client',val) $('#message').val('') } })
-
-
-
-
pm2部署项目
-
管理多个node项目
-
安装:npm install -g pm2
-
pm2管理单个node项目
-
启动: pm2 start 启动服务器的入口js文件
-
列出所有应用: pm2 list
-
结束应用: pm2 stop id
-
删除进程: pm2 delete id
-
-
pm2管理多个node项目
-
使用命令PM2 ecosystem 或者 pm2 init,初始化配置文件ecosystem.config.js
-
配置ecosystem.config.js文件
-
name 指定项目名称
-
script 指node项目的入口启动文件
-
-
启动|重启|停止|删除多个node服务器
-
pm2 [start|restart|stop|delete] ecosystem.config.js
-
pm2 list
-
下载两个包 npm i socket.io -s nom i socket socketserver public index.html socket.io.js(nodemoudel中的) app.js
配置路由 并检测postman 写接口
-
-
vue2
-
vue: 渐进式JavaScript 框架
-
选择vue的原因
-
超快的虚拟dom,很多省心的优化
-
繁荣的生态系统
-
vue@2+vue-cli+vue-router@3+vuex@3+element ui+vue-i18n@8+...
-
vue@3+vue-cli+vue-router@4+vuex@4+element pluse+vue-i18n@9+.. == 对开发人员友好 == 兼容性: Vue 不支持 IE8 及以下版本 == 我们使用的v版本2.6.14
-
-
选项
-
data html标签中可以使用data里定义的变量,使用插值语法
{ {表达式}}
,可以通过vue实例访问里面的data选项-
data:{}
-
data:function(){return {}}
-
-
el 选项指定vue实例接管的根标签,书写根标签的css的选择器
-
methods 里面定义在html标签中可以使用的方法名
-
-
$mount方法 挂载
-
template选项 指定要替换el指定的标签的html字符串
-
template的优先级高于el指定标签本身的
-
vue实例在编译html字符串的时候如果现象有template, 就用template选项里面的html字符串,如果没有就用el指定标签本身的html字符串
-
实际工作用一般都是用template,写在body里面的属性和标签名,遵循html标准,不区分大小写,写在template里面的属性和标签名,区分大小写
-
-
computed选项(计算属性)
-
在computed里定义的函数,使用的时候直接写函数名,不要写函数名(),内部会自动执行这个函数,显示返回值
-
计算属性的语法:
-
属性名: 用于获取变量值的函数
-
属性名: {get:获取变量值的函数,set:设置变量值的函数}
-
-
-
-
指令:
-
文本类指令
-
v-text
-
v-html
-
v-pre 加了这个指令的标签及其子元素不会被编译
-
v-once 加了这个指令的标签及其子元素只会被编译一次,后续就当静态内容
-
v-cloak vue编译完成以后,会把所有标签上看到的v-cloak移除(隐藏未编译的 Mustache 标签直到实例准备完毕)
-
与
[v-cloak]{ display:none; }
配用
-
-
-
v-on
-
语法
-
v-on:事件类型="事件处理函数名"
-
v-on:事件类型="事件处理函数名()"
-
v-on:事件类型="事件处理函数名(参数1,参数2,...)"
-
有一个特别的参数$event
-
-
v-on:可以简写成: @
-
-
事件修饰符
-
.prevent: 阻止事件默认行为
-
.stop: 阻止冒泡
-
.self: 只有事件的target是我自己,才触发事件,冒泡上来不触发 (大都用于父类)
-
.once:只发生一次
-
.enter
v-on:keyup.enter="addTask"
-
.right
-
v-on:keydown.alt.67=""
-
v-on:mousedown.left=""
-
-
-
v-bind
-
语法:
v-bind:属性名="js表达式"
-
简写:
:属性名="js表达式"
-
使用:
-
图片:src上
<img :src="imgSrc" alt="/>
-
class动态属性
-
动态属性原始语法
-
<div v-bind:class="className"></div>
-
-
class数组语法
-
<div v-bind:class="['bg','border']"></div>
-
-
class对象语法
-
<div :class="{bg:false,border:true,r:false}"></div>
-
-
class数组语法和对象语言一起使用
-
<div :class="['border',{bg:false},{r:true}]"></div>
-
-
-
style动态属性
-
动态属性原始语法
-
<div :style="style"></div>
-
-
style的对象语法
-
<div :style="{background:'red',border:'10px solid #ccc'}"></div'
-
-
style的数组语法
-
<div :style="[{background:'red'},{border:'10px solid #ccc'}]"></div>
-
-
-
-
-
条件渲染指令: v-if
-
通过控制节点的插入和销毁来控制显示隐藏
-
语法:
v-if="值为true就显示,值为false就隐藏"
-
如果要切换显示隐藏的有多个元素可以把他们放在一起
vue里面有一个标签标签叫做类似js里面的()
-
-
v-show 通过控制节点的样式的display来显示和隐藏
-
语法:
v-show="值为true就显示,值为false就隐藏
-
-
v-for循环渲染
-
语法:
-
v-for="item in arr"
-
v-for="(item,index) in arr"
-
v-for="(val,key) in obj"
-
v-for="(val,key,index) in obj"
-
-
v-for和v-if强烈不推荐在同一个标签上使用
-
vue@2 : v-for 的优先级比 v-if 更高
-
vue@3 : v-if 的优先级比 v-for 更高
-
-
v-for推荐配合key属性使用
-
key必须是不能重复的
-
key建议使用字符串或者number
-
key属性是在比较不同的虚拟dom的时候起作用
-
如果key相同,就是原来的那个节点
-
如果key不相同,就不是同一个节点,就需要销毁不存在的key对应的节点
-
-
-
v-model
-
双向绑定(text变量的变化会导致界面的更新,界面上文本框或者文本域的value的变化会导致text这个变量的更新)
-
修饰符
-
.lazy
-
.trim
-
.number
-
-
复选框:
-
<input type="checkbox" value="football" v-model="flag">
-
如果flag初值是布尔值:复选框把选中的状态双向绑定到flag这个变量上
-
如果flag初值是数组:把复选框选中元素的value同步到这个数组里面
-
-
v-model用于组件元素上
-
<son v-model="text"></son>
等价于<son v-bind:value="text" @input="inputHandler"></son>
-
-
总结:
-
v-model
== 还可以使用在组件上
== 用于双向数据绑定
== 数据的变化导致界面更新
== 界面更新导致数据的更新
== v-model默认把变量绑定到value属性上
== 变量值的变化会导致元素value的变化
== v-model默认监听input事件
== 把元素的value值更新到变量上
-
-
-
-
render选项
-
优先级: render渲染函数>template模板字符串>el元素的html字符串
-
render(h){ return h('p',{ class:{ a:true, b:true }, style:{ fontSize:'100px' }, attrs:{ id:'box', index:123 } },[ h('h3',"我是p里面的h3标签") ]) }
-
-
过滤器
-
过滤器可以在
{ {}}
和 v-bind:属性名="" 里面使用 -
使用方法:
-
{ {变量|过滤器|过滤器(实参1,实参2,....)}}
可接多个 -
v-bind:属性名="变量|过滤器|过滤器(实参1,实参2,....)"
-
-
全局过滤器
-
语法:Vue.filter('过滤器名称',function(val){})
-
val就是使用过滤器 | 前面的变量值
-
return 返回值就是经过处理以后新值
-
-
-
局部过滤器
-
语法:
-
new Vue({ filters:{ "过滤器名称":function(val,arg1,arg2,...){ // val就是使用过滤器 | 前面的变量值 // arg1,arg2,...给过滤器函数传递的其他参数 } } })
-
-
-
-
侦听器(watch)
-
定义变量的侦听器,一旦被侦听的变量的值发生改变,就会触发侦听器
-
语法:
-
{要侦听的变量名: 数据变化的处理函数}
-
{要侦听的变量名:{
handler:数据变化的处理函数,
deep:是否深度监听,
immediate:定义函数的时候立即执行一次
}}
-
-
对象类型如何侦听?
-
当侦听的变量为一个对象(复杂数据类型)时,直接改变对象中的值,由于监听的对象,比较的是地址,地址没有变化无法监听到,需要监听地址里面内容的变化,需要
-
如果对象list的地址变了,那这个对象的值就变化(也可以侦听到)
-
this.list = { ...this.list, name:this.newName }
-
-
-
-
自定义指令
-
指令都是v-开头的,所以我们自定义指令的时候,不用写v-, 用的时候要加上
-
- 所有vue实例都能使用
-
语法:
-
Vue.directive('指令名',{ // 当被绑定的元素插入到 DOM 中时执行inserted这个钩子函数 inserted:()=>{}, // 只调用一次,指令第一次绑定到元素时调用 bind:()=>{}, // 所在组件的 VNode 更新时调用 update:()=>{} })
-
-
简写语法:
-
Vue.directive('指令名',(el,binding)=>{ // bind和update的时候会执行 })
-
-
-
- 只有定义指令的vue实例可以使用
-
new Vue({ directives:{ '指令名': {钩子函数}, '指令名': 钩子函数简写 } })
-
例子:
-
Vue.directive('pos',(el,binding)=>{ // bind和update的时候会执行 console.log(el);// 指令所在的节点 console.log(binding);// 书写的修饰符的细节信息 console.log(binding.value);// =后面表达式的值101 console.log(binding.arg);// :后面的left console.log(binding.modifiers);// 指令的修饰符集合 // pos这个指令可以设置元素的定位 // el.style.left = '100px' el.style[binding.arg] = binding.value+"px" })
-
-
-
-
混入:mixin
-
定义一个对象,这个对象包含一些vue选项
-
:
-
Vue.mixin(要混入的对象)
-
所有实例都能使用混入的对象里面的vue选项
-
-
:
-
new Vue({mixins:[要混入的对象1,要混入的对象2,...]})
-
只有定义混入的vue实例可以使用混入的vue选项
-
-
注意:
-
混入对象的选项和实例选项有冲突,用实例的选项的
-
全局混入:慎用,一般用于做插件
-
vue@2 在mixin合并data的时候是深合并
-
vue@3 在mixin合并data的时候是浅合并
-
生命周期函数在混入的时候,不会冲突,都执行
-
-
-
中央事件总线
-
即借助vue的一个实例去提供一个’电信公司‘,其他组件可借助这个vue的实例来通信
-
实现
-
let bus = new Vue()
-
bus.$emit('其他组件自定义函数名',要传的数据)
(通过事件 发送消息的人) -
mounted(){ bus.$on('自定义函数名',(data)=>{ console.log(data) }) }
(接收信息的人,即去bus这个电信公司办卡的人)
-
-
-
-
$refs
-
使用(通常在mounted中获取)
-
如果给一个普通的标签添加 ref=’xxx‘属性,使用vm.$refs.xxx 获取其真实DOM节点
-
如果v-for循环出的标签有ref='xx'属性,我就可以通过 vm.$refs.xx 获取这个真实的dom节点的集合
-
如果一个组件有ref='xx'属性,我就可以通过 vm.$refs.xx 获取这个组件实例对象
-
-
-
provide与inject选项
-
在祖先实例中通过provide选项定义的变量,可以在其后代中通过inject选项引入来进行使用
-
使用:
-
祖先实例中:
provide:{ num:100 }
-
子孙组件中:
inject:[ ’num‘ ]
直接{ { num }}使用
-
-
-
axios
https://unpkg.com/axios/dist/axios.min.js
-
基于 promise 的 HTTP 库,可以用在浏览器和 node.js 中
-
请求方法的简写方法
-
axios.get(url[, config])
-
axios.post(url[, data[, config]])
-
-
请求方法完整
-
axios(config)
-
aixos(url,config)
-
-
axios的实例
-
在实例中设置配置,后面直接用实例发请求,就所有的请求的baseURL都是设置好的,发请求的时候如果用server发请求,和axios发请求的方法是一样的,只不过用server发就都设置好了baseURL和responseType
-
const server = axios.create({ // axios的config怎么写,这里的配置就怎么写 baseURL:"http://localhost:8888", responseType:'json', headers:{ token:localStorage.getItem('token')||'' } })
-
-
请求响应拦截器
-
请求拦截器(给axios实例添加请求拦截器)
-
erver.interceptors.request.use(function(config){ // 请求发送成功 // config请求的配置项目,是一个对象 // 你对config做一些事情,做完以后给回去 config.headers.token = localStorage.getItem('token')||""; return config; },function(error){ // 请求发送失败 return Promise.reject(error); })
-
-
响应拦截器(给axios实例添加响应拦截
-
-