1 回调函数
1.1 回调函数定义:
① 函数是不是你定义的 你定义的
② 函数你要有没有调用 你没调用
③ 最终函数有没有被执行 最终执行了
1.2 回调函数分类:
同步的回调:数组api中的回调函数基本都是同步的;promise的执行器是同步的
异步的回调:定时器的回调 dom事件的回调 ajax请求的回调
遇到异步回调函数需注意以下两点:
① 回调什么时候进队列
② 回调什么时候被执行
内存结构:堆 栈 队列
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-2zfZO7rw-1626050796268)(D:\class\vue\02-vue\code\day07(axios)]\01_异步&单线程\定时器\事件循环模型.png)
//最快一秒钟同时输出5个5
for (var i 0; i < 5; i++){
setTimeout(function() {
console.log(i) 55555
},1000)
}
//...耗时的操作
2 变量的查找规则
-
js中的作用域就是函数作用域!
-
变量的查找基于作用域链
变量的查找规则:
① 左查询(变量出现在等号的左边使用左查询的规则进行变量查找)
如果整条作用域链中 都没有对应变量的声明 则v8会默认在顶层作用域声明一份
② 右查询(变量出现在等号的非左边使用右查询的规则进行变量查找)
如果整条作用域链中 都没有对应变量的声明 则v8会报错
function wrap(){
function inner(){
a = 1;
console.log(a,"inner")
}
inner()
}
wrap()
console.log(a,"全局") //1 inner 1 全局
3 Promis实例
3.1 Promise最佳实践
① promise实例是一次性的,一个promise实例实例只能使用一次
② promise构造函数的执行器必须有异步代码,应该在异步对应的回调来合理的调用resolve reject
③ then方法中回调的返回值一般是一个新的promise
3.2 promise重点
① promise的实例对象三种状态是如何切换的 (成功 失败 初始化)
② promise的实例持有值为多少
当通过new Promise创造出来的promise实例; 他状态的改变以及持有的值 都跟执行器有关,new Promise时 执行器先执行 再返回promise实例。
执行器的返回值会被忽略
- 如果执行器中什么都没有发生,创建出来的promise实例则是初始化状态,持有值是undefined
- 如果执行器中resolve被调用,创建出来的实例是成功状态,持有的值是resolve函数第一个参数
- 如果执行器中reject被调用,创建出来的实例是失败状态,持有的值是reject函数第一个参数
- 如果执行器中发生异常,创建出来的promise实例是失败状态,持有的值是失败原因
let promsie = new Promise((resolve, reject)=>{
// 执行器内部的resolve, reject一定要放在一个异步环境下调用!!!
setTimeout(()=>{
resolve()
},2000)
})
promise是一个状态机,在promise状态改变时, 可以决定其then方法中的两个参数什么时候进队列,谁进队列。
then方法有两个参数是两个回调,这两个回调中的一个回调最终是要进队列的,至于是哪一个回调什么时候进队列取决用这个then方法的promise对象的状态什么时候发生改变,改变成什么状态
//此处代表执行时,promise处于初始化状态
let promise = promise.then(() =>{},()=>{})
then方法返回一个新的Promise,而它的行为与then中的回调函数的返回值有关:
-
如果then中的回调函数返回一个值,那么then返回的Promise将会成为成功状态,promise持有的值就是该返回值。
-
如果then中的回调函数返回一个Promise,那么then返回的Promise会继承他的状态和值
-
如果then中的回调函数抛出一个错误,那么then返回的Promise将会成为失败状态,promise持有的值就是错误原因
//此处代表执行时,promise处于初始化状态 promise.then(()=>{},()=>{})
4 promise面试题
// 输出: 1 2 10 5 6 8 9 3
//p2 创建完就是成功状态 持有的值为5
//p1 创建完就是成功状态 持有的值为6
//p3 创建完就是成功状态 持有的值为9
const p1 = () => (new Promise((resolve, reject) => {
console.log(1);
let p2 = new Promise((resolve, reject) => {
console.log(2);
const timeOut1 = setTimeout(() => {
console.log(3);
resolve(4);
}, 0)
resolve(5);
});
resolve(6);
p2.then((arg) => {
console.log(arg);
});
}));
const timeOut2 = setTimeout(() => {
console.log(8);
const p3 = new Promise(reject => {
reject(9);
}).then(res => {
console.log(res)
})
}, 0)
p1().then((arg) => {
console.log(arg);
});
console.log(10);
5 async&await
async函数返回一个新的promise,而他的行为与async函数的代码体有关
- 如果async函数代码体返回一个值,那么async函数返回的promise将会成为成功状态,promise持有的值就是该返回值
- 如果async函数代码体返回一个promise,那么async函数返回的promise与当前代码体返回的promise一致
- 如果async函数代码体抛出一个错误,那么async函数返回的promise将会成为失败状态,promise的值就是错误原因
//await等于一个promise,当等待的promise状态明确之后,async函数才会继续往下执行
(async function(){
try {
const {data:{items}} = await axios.get("https://api.github.com/search/users?q=damu")
let login = items[0].login;
const {data:dataForRepos} = await axios.get(`https://api.github.com/users/${login}/repos`)
let repname = dataForRepos[0].name;
const data = await axios.get(`https://api.github.com/repos/${login}/${repname}/issues`)
console.log(data)
} catch(error) {
console.log((error))
}
})()
总结:
① async函数的执行结果是一个promise
② await通常等待是一个promise
async function getLogin(){
const {data:{items}} = await axios.get("https://api.github.com/search/users?q=damu");
let login = items[0].login;
return login
}
......
(async function (){
const login = await getLogin()
const repname = await getRepname(login)
const data = await getIssues(login,repname)
console.log(data);
})()
6 axios
axios.get("https://api.github.com/search/users?q=damu").then(({data:{items}}) =>{
//console.log(items) //得到所有带有damu名字的用户
let login = items[0].login;
return axios.get(`https://api.github.com/users/${login}/repos`)
}).then(({data}) =>{
//console.log(data) //得到所有的仓库
let login = data[0].owner.login;
let repname = data[0].name;
return axios.get(`https://api.github.com/repos/${login}/${repname}/issues`)
}).then((data) => {
console.log(data) //得到仓库中的issues
}).catch((err) =>{
// 处理全程的异常处理,若有错误,异常穿透,打印错误信息
console.log(err)
})
axios
axios文档: http://www.axios-js.com/zh-cn/docs/
github开发者文档: https://docs.github.com/en
根路径: https://api.github.com
查询用户: https://api.github.com/search/users?q=damu
查询指定用户的仓库:https://api.github.com/users/${login}/repos
查询指定用户仓库的issues: https://api.github.com/repos/${login}/${repname}/issues
安装axios:npm i axios
在项目的入口文件引入axios:import axios from “axios”
使用axios发生基本请求
7 restful 风格 api
7.1 code:
200:请求成功 返回内容
204:请求成功但不返回内容
403:权限不够,拒绝访问
404:接口未定义
412:params数据有问题
422:query数据有问题
500:服务器错误
7.2 method:
① get(查询 获取所有的issues) : /repos/{owner}/{repo}/issues
<div class="hello">
<input type="text" v-model="username">
<button @click="getIssues">获取仓库内容</button>
<ul v-if="issues.length" class="issues">
<li v-for="issue in issues" :key="issue.id">
{{issue.title}}:{{issue.body}}
</li>
</ul>
</div>
import axios from "axios"
export default {
name: 'issues',
data() {
return {
issues:[],
username:'betterDamu' //输入框中的内容即是仓库名字
}
},
methods:{
// 获取仓库中的数据
async getIssues(){
const {data} = await axios({ ///repos/{owner}/{repo}/issues
url:`https://api.github.com/repos/${this.username}/bj_181130/issues`,
method:"get"
})
console.log(data) //得到所有的仓库数据,可以结构赋值只拿到data数据
this.issues = data
},
}
}
② get(查询 获取指定的issues) : /repos/{owner}/{repo}/issues/{issue_number}
<a href="#" @click.prevent="getIssue(issue.number)">{{issue.title}}:{{issue.body}}</a>
// 点击获取仓库中的一个内容并输出
async getIssue(issue_number) {
const {data} = await axios({ //repos/{owner}/{repo}/issues/{issue_number} url:`https://api.github.com/repos/${this.username}/bj_181130/issues/${issue_number}`,
method:"get"
})
console.log(data)
}
③ post(新增) : /repos/{owner}/{repo}/issues
新增接口,登录状态才能提问题,需要权限 github-setting-developer settings- personal access tokens
<button @click="addIssue">给仓库增加一个内容</button>
// 给仓库新增一个内容
async addIssue() {
const {data} = await axios({ //repos/{owner}/{repo}/issues
url:`https://api.github.com/repos/${this.username}/bj_181130/issues`,
method:"post",
data:{
title:"abc",
body:"def",
},
headers:{
Authorization:"token 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",
}
})
//界面要刷新
await this.getIssues()
//.......必须要等界面刷新成功之后才能执行的后续逻辑
}
④ patch(局部修改): /repos/{owner}/{repo}/issues/{issue_number}
<button @click="updateIssue(issue.number)">修改</button>
// 进行局部更新 修改仓库中的一个内容
async updateIssue(issue_number) {
await axios({ ///repos/{owner}/{repo}/issues/{issue_number}
url:`https://api.github.com/repos/${this.username}/bj_181130/issues/${issue_number}`,
method:"patch",
data:{
title:"123456"
},
headers:{
Authorization:"token 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",
}
})
await this.getIssues()
},
⑤ put(锁) : /repos/{owner}/{repo}/issues/{issue_number}/lock
<button @click="suoIssue(issue.number)">锁</button>
async suoIssue(issue_number) {
await axios({ ///repos/{owner}/{repo}/issues/{issue_number}
url:`https://api.github.com/repos/${this.username}/bj_181130/issues/${issue_number}/lock`,
method:"put",
headers:{
Authorization:"token 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",
}
})
await this.getIssues()
},
⑥ delete(解锁) : /repos/{owner}/{repo}/issues/{issue_number}/lock
<button @click="deleteIssue(issue.number)">解锁</button>
async deleteIssue(issue_number) {
await axios({ ///repos/{owner}/{repo}/issues/{issue_number}
url:`https://api.github.com/repos/${this.username}/bj_181130/issues/${issue_number}/lock`,
method:"delete",
headers:{
Authorization:"token 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",
}
})
await this.getIssues()
},
8 使用axios发送请求(实现对issues的CDUR)
8.1 通过axios函数发送请求
整个项目的所有模块使用同一个axios函数
import axios from "axios"
① axios (config)
② axios(url,config) //config要删除URL配置
config:{
url:"请求方式",
method:"请求方式",
data:{} ,//请求体
headers:{authorization},//请求头
params:{},//这是query数据 query=params
timeout:1000,//超时时间:请求发送的时间超过这个这个时间,会取消请求
baseURL:基地址
}
8.2 请求方法的别名
③ axios.method(url,data,config)//config中要剔除url,method,data
async getIssues(){
const {data} = await axios.get(`https://api.github.com/repos/${this.username}/bj_181130/issues`,{
method:"get"
})
}
9 并发请求
axios.all(迭代器)
axios.spread(callback)
promise.all() 返回一个promise 建议用这个
const data = await Promise.all[axios1(),axios2()]
data[0] axios1请求对应的数据
data[1] axios2请求对应的数据
async created() {
const data = await Promise.all([
axios.get(`https://api.github.com/repos/betterdamu/bj_181130/issues`,{
headers:{
Authorization:"token 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"
}
}),
axios.get(`https://api.github.com/search/users?q=damu`,{
headers:{
Authorization:"token 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"
}
})])
console.log(data[0]); // 第一个请求的数据
console.log(data[1]); // 第二个请求的数据
}
10 创建实例
整个项目的所有模块可以使用不同axios实例来发送请求
src下新建一个api文件夹,文件issues.js 以下方法都支持:
① axios (config)
② axios(url,config) //config要删除URL配置
③ axios.method(url,data,config)//config中要剔除url,method,data
const ins = axios.create([config])
import axios from "axios";
const issuesAxios = axios.create({
timeout:1000,
baseURL:"https://api.github.com/",
headers:{
Authorization:"token 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"
}
})
// 添加请求拦截器
issuesAxios.interceptors.request.use(function (config) {
// config.baseURL="https://api.zdy.com"
return config;
});
// 添加响应拦截器
issuesAxios.interceptors.response.use(function (response) {
//响应回来的时 最早可以拿到返回数据的地方
return response.data;
}, function (error) {
//错误处理
return Promise.reject(error);
});
export default issuesAxios
11 axios返回数据的基本格式
{
// `data` 由服务器提供的响应
data: {},
// `status` 来自服务器响应的 HTTP 状态码
status: 200,
// `statusText` 来自服务器响应的 HTTP 状态信息
statusText: 'OK',
// `headers` 服务器响应的头
headers: {},
// `config` 是为请求提供的配置信息
config: {},
// 'request'
// `request` is the request that generated this response
// It is the last ClientRequest instance in node.js (in redirects)
// and an XMLHttpRequest instance the browser
request: {}
}
12 axios配置优先级
优先级依次从低到高
全局默认
axios.defaults.baseURL = 'https://api.example.com';
实例特有
axios.create({
baseURL: 'https://api.example.com'
});
发请求时确认
instance.get('/longRequest', {
baseURL: 'https://api.example.com'
});
请求拦截器的第一个回调是最后可以修改配置的地方
13 拦截器
函数级别和实例级别都可以使用拦截器
// 添加请求拦截器 在app.vue函数级别的拦截器
axios.interceptors.request.use(function (config) {
// 在发送请求之前做些什么
return config;
}, function (error) {
// 对请求错误做些什么
return Promise.reject(error);
});
// 添加响应拦截器
axios.interceptors.response.use(function (response) {
// 响应回来的时候 最早可以拿到返回数据的地方
return response;
}, function (error) {
// 对响应错误做点什么
return Promise.reject(error);
});
14 前端往后台传递数据的形式
url.params
url.query
body:请求头 一般只有post patch put来支持
headers:请求头
15 处理跨域
① 让所有的跨域请求改向webpack=dev-server代理服务器(它不光还具备静态资源管理的功能,还拥有转发请求的功能):一般都是修改请求的URL
② 设计webpack-dev-server的代理规则
如果用的是vue-cli,这个配置在config/index.js文件中(dev-proxyTable)
③ 必要时要重写path
跨单域 congif-index.js
devServer: {
proxy: {
'/api': 'http://localhost:9000',
},
},
修改好后修改baseURL:
baseURL:"/api" //以api开头的
跨多域
devServer: {
proxy: {
'/api': { /api开头的,如/9000/api
target: 'http://localhost:3000',
pathRewrite: { '^/api': '' },
},
},
},
baseURL:"/9000" //以9000开头的
项目上线时候,进行生产环境打包 npm dev build 只是在自己的webpackdevserver中,提醒运维做跨域处理
如果有coreURL,则优先使用coreURL 可以进行判断,若有跨域问题,优先使用coreURL ,否baseURL
16 axios二次封装
在项目中不会使用
src/api 文件夹
contact(代表模块名称)
axios.js :代表发送请求的工具
config.js:代表发送请求的配置
index.js :引入上述两个文件,再引入util中工具类, 生成所有的请求函数
github
api.js :统筹所有模块的index.js,将所有的模块都暴露给ming.js
src/util
createForRequest.js 真正发请求的位置 核心代码
src/main.js
import api from "api/api"
Vue.prototype.$api = api
在项目中如何使用
请求相关的信息(URL methods等)还是不会直接出现在组件中,我们会创建一个叫api的目录,这个目录下会有很多模块发送请求的js文件,每个js文件往外暴露当前模块的请求函数(这些请求函数不是自动生成的,而是手动配置的)
17 mock数据
在没有后接口的情况下,如何造假数据(前后端分离)
17.1 通过webpack-dev-server(静态mock)
//mock数据源 data:当前json的对象
const appData = require("../data.json")
const seller = appData.seller
const goods = appData.goods
const ratings = appData.ratings
devServer:{
before(app){ //before 是个钩子 app:express中的核心对象,可以注册后台路由
//造一些假后台路由,其实没有真正的访问数据库,而是去拿json中数据
//通过Ajax请求get方法来访问/api/seller,可以等得到对应的回调函数返回的数据
app.get("/api/seller",(req,res)=>{
res.json({
code:200,
data:data.seller
})
})
}
}
//发送请求
async mounted() {
axios({
url:"/api/seller",
method:get
})
console.log(data)
}
有后台接口之后,可以把before删掉。
此方法数据是静态的,一成不变的。
17.2 通过mock.js(动态mock)
文档地址:http://mockjs.com/ 生成随机数据,拦截ajax请求
① 安装
npm install mockjs -D
② 基本使用
// 使用 Mock
import Mock from 'mockjs'
var Mock = require('mockjs') //生成随机数据,是以对象的形式出现
var data = Mock.mock({
// 属性 list 的值是一个数组,其中含有 1 到 10 个元素
'list|1-10': [{
// 属性 id 是一个自增数,起始值为 1,每次增 1
'id|+1': 1
}]
})
// 输出结果
console.log(JSON.stringify(data))
以上代码通过node mock.js来执行
③ 拦截ajax请求
在main.js中进行引入 mock.js
Mock.mock( template )
根据数据模板生成模拟数据
Mock.mock( rurl, template )
记录数据模板。当拦截到匹配 rurl 的 Ajax 请求时,
将根据数据模板 template 生成模拟数据,并作为响应数据返回
自行去修改data.json中的数据,即是动态的
const appData = require("../data.json")
const seller = appData.seller
const goods = appData.goods
const ratings = appData.ratings
var Mock = require('mockjs')
var data = Mock.mock({
code:200,
data:{
'list|1-10': [{
'id|+1': 1
}]
}
})
app.vue
//发送请求
async mounted() {
axios({
url:"/api/seller",
method:get
})
console.log(data)
}
使用mock如何实现增删改查接口,mock静态页面模拟分页,可百度查
④ 语言规范
Mock.js 的语法规范包括两部分:
- 数据模板定义规范(Data Template Definition,DTD)
- 数据占位符定义规范(Data Placeholder Definition,DPD)
⑤ DTD
数据模板中的每个属性由 3 部分构成:属性名、生成规则、属性值
// name:属性名 rule:生成规则 value:属性值
'name|rule': value
注意
属性名 和 生成规则 之间用竖线 | 分隔
生成规则 的 含义 需要依赖 属性值的类型 才能确定
属性值 中可以含有 @占位符
属性值 还指定了最终值的初始值和类型
规范地址
https://github.com/nuysoft/Mock/wiki/Syntax-Specification
⑥ DPD
占位符 只是在属性值字符串中占个位置,并不出现在最终的属性值中。
占位符 的格式为:
@占位符 引用Mock.Random的方法
city:“@city” 引用Mock.Random的方法生成随机城市
var Mock = require("mockjs")
console.log(Mock.Random) @表示调用randow方法
18 练习
npm i axios 配别名api和components
拆分组件 search.vue list.vue 在app.vue中引入,为了避免命名冲突,加v-search
在stasic中引入样式,并在index.html中引入当前的样式
在src下新建api,github.js文件 配置axios实例及公共配置,拦截器 并暴露出去
search.vue 中拿输入的内容 v-model,在main.js中注册总线(提供东西为触发事件),
点击search触发总线,把username输送出去,清除输入框,清除错误提示
list.vue中接收总线,随便在哪一个生命周期内都可以created,发送请求获取username对应的所有的GitHub用户 引入githubAxios 配置params
遍历list,拿头像和数据
扩展:当头像地址和名字接口变动的时候,不改模板,用map映射
含义 需要依赖 属性值的类型 才能确定
属性值 中可以含有 @占位符
属性值 还指定了最终值的初始值和类型
规范地址
https://github.com/nuysoft/Mock/wiki/Syntax-Specification