我们来考虑下面的场景(有夸张的成分):
我们需要通过一个url1从服务器加载一个数据data1,data1中包含了下一个请求的url2
我们需要通过data1取出url2,从服务器加载数据data2,data2中包含了下一个请求的url3
我们需要通过data2取出url3,从服务器加载数据data3,data3中包含了下一个请求的url4
发送网络请求url4,获取最终的数据data4
代码:
// 同步
const name = 'yyy';
console.log(name);
const result = 3 + 5;
// 异步
// 1.使用setTimeout 用一个定时器来模拟异步事件
// setTimeout(() => {
// console.log('Hello World');
// }, 1000)
// 什么情况下会用到Promise?
// 一般情况下是有异步操作时,使用Promise对这个*异步操作进行封装*
// new -> 构造函数(1.保存了一些状态信息 2.执行传入的函数)
// 在执行传入的回调函数时, 会传入两个参数, resolve解决, reject拒绝.这两个本身又是函数
// 参数 -> 函数((resolve, reject)
// 以后有什么异步的操作的话都可以封装在Promise里面
new Promise((resolve, reject) => {
setTimeout(() => {
// 如果有代码需要处理不会直接这样放这里 在then里面处理data
// console.log('hello world');
// console.log('hello world');
// console.log('hello world');
// console.log('hello world');
// 成功的话去到then那里
// resolve('Hello World')
// 失败的时候调用reject 会去到catch那里
reject('error message')
}, 1000)
}).then((data) => { // then里面也是一个函数
// 1.100行的处理代码
console.log(data);
console.log(data);
console.log(data);
console.log(data);
console.log(data);
}).catch((err) => {
console.log(err);
})
// 通过回调里的 resolve(data) 将这个 promise 标记为 resolverd,
// 然后进行下一步 then((data)=>{//do something}),resolve 里的参数就是你要传入 then 的数据。
首先, 当我们开发中有异步操作时, 就可以给异步操作包装一个Promise
异步操作之后会有三种状态
new Promise((resolve, reject) => {
setTimeout(() => {
// resolve('Hello Vuejs')
reject('error message')
}, 1000)
// 不想同时写then和catch的话 then里面可以直接传入两个函数 data,err
}).then(data => {
console.log(data);
}, err => {
console.log(err);
})
我们在看Promise的流程图时,发现无论是then还是catch都可以返回一个Promise对象。
所以,我们的代码其实是可以进行链式调用的:
// 参数 -> 函数(resolve, reject)
// resolve, reject本身它们又是函数
// 链式编程
new Promise((resolve, reject) => {
// 第一次网络请求的代码
setTimeout(() => {
resolve()
}, 1000)
}).then(() => {
// 第一次拿到结果的处理代码
console.log('Hello World');
console.log('Hello World');
console.log('Hello World');
console.log('Hello World');
console.log('Hello World');
console.log('Hello World');
// 如果还有回调的话可以在这里写 在第一个.then()的后面继续.then()
return new Promise((resolve, reject) => {
// 第二次网络请求的代码
setTimeout(() => {
resolve()
}, 1000)
})
}).then(() => {
// 第二次处理的代码
console.log('Hello Vuejs');
console.log('Hello Vuejs');
console.log('Hello Vuejs');
console.log('Hello Vuejs');
console.log('Hello Vuejs');
console.log('Hello Vuejs');
return new Promise((resolve, reject) => {
// 第三次网络请求的代码
setTimeout(() => {
resolve()
})
})
}).then(() => {
// 第三处理的代码
console.log('Hello Python');
console.log('Hello Python');
console.log('Hello Python');
console.log('Hello Python');
console.log('Hello Python');
})
这里我们直接通过Promise包装了一下新的数据,将Promise对象返回了
Promise.resovle():将数据包装成Promise对象,并且在内部回调resolve()函数
Promise.reject():将数据包装成Promise对象,并且在内部回调reject()函数
// 简写,直接return Promise.resolve()
new Promise((resolve, reject) => {
setTimeout(() => {
resolve('aaa')
}, 1000)
}).then(res => {
// 1.自己处理10行代码
console.log(res, '第一层的10行处理代码');
// 2.对结果进行第一次处理
return Promise.resolve(res + '111')
}).then(res => {
console.log(res, '第二层的10行处理代码');
return Promise.resolve(res + '222')
}).then(res => {
console.log(res, '第三层的10行处理代码');
})
/* -----省略掉Promise.resolve,直接return res ----- */
new Promise((resolve, reject) => {
setTimeout(() => {
resolve('aaa')
}, 1000)
}).then(res => {
// 1.自己处理10行代码
console.log(res, '第一层的10行处理代码');
// 2.对结果进行第一次处理
return res + '111'
}).then(res => {
console.log(res, '第二层的10行处理代码');
return res + '222'
}).then(res => {
console.log(res, '第三层的10行处理代码');
})
new Promise((resolve, reject) => {
setTimeout(() => {
resolve('aaa')
}, 1000)
}).then(res => {
// 1.自己处理10行代码
console.log(res, '第一层的10行处理代码');
// reject失败的简写
// return Promise.reject('error message')
// 失败error的其他写法 手动抛出异常
throw 'error message'
}).then(res => {
console.log(res, '第二层的10行处理代码');
return Promise.resolve(res + '222')
}).then(res => {
console.log(res, '第三层的10行处理代码');
}).catch(err => {
console.log(err);
})
// 需求本身依赖两个请求 不确定是哪个先请求回来,所以两个都得处理handleResult
// 请求一:
// let isResult1 = false
// let isResult2 = false
// $ajax({
// url: '',
// success: function () {
// console.log('结果1');
// isResult1 = true
// handleResult()
// }
// })
// // 请求二:
// $ajax({
// url: '',
// success: function () {
// console.log('结果2');
// isResult2 = true
// handleResult()
// }
// })
//
// function handleResult() {
// if (isResult1 && isResult2) {
// //
// }
// }
// Promise.all可以将多个Promise实例包装成一个新的Promise实例。统一进行回调
// 同时,成功和失败的返回值是不同的,成功的时候返回的是一个结果数组,而失败的时候则返回最先被reject失败状态的值。
Promise.all([
// new Promise((resolve, reject) => {
// $.ajax({
// url: 'url1',
// success: function (data) {
// resolve(data)
// }
// })
// }),
// new Promise((resolve, reject) => {
// $.ajax({
// url: 'url2',
// success: function (data) {
// resolve(data)
// }
// })
// })
new Promise((resolve, reject) => {
setTimeout(() => {
resolve({
name: 'yyy',
age: 18
})
}, 2000)
}),
new Promise((resolve, reject) => {
setTimeout(() => {
resolve({
name: 'kobe',
age: 19
})
}, 1000)
})
]).then(results => {
console.log(results);
})
但是,有什么状态时需要我们在多个组件间共享的呢?
如果你做过大型开放,你一定遇到过多个状态,在多个界面间的共享问题。
比如用户的登录状态、用户名称、头像、地理位置信息等等。
比如商品的收藏、购物车中的物品等等。
这些状态信息,我们都可以放在统一的地方,对它进行保存和管理,而且它们还是响应式的(待会儿我们就可以看到代码了,莫着急)。
App.vue代码
{{message}}
{{counter}}
// HelloVuex.vue
{{counter}}
// App.vue
{{message}}
{{counter}}
store是通过单例模式实现的
// 安装vuex
npm install vuex --save
代码
import Vue from 'vue'
import Vuex from 'vuex'
// 1.安装插件
Vue.use(Vuex)
// 2.创建对象 new Vuex.Store
const store = new Vuex.Store({
// 这5个对象一般都是固定的
state: { //保存状态
counter: 1000
},
mutations: { // 方法 修改state唯一途径 同步操作
increment(state) { // 默认就有个state参数,不用通过this.state
state.counter++
},
decrement(state) {
state.counter--
}
},
actions: { // 如果有异步操作在这里写 比如网络请求
},
getters: {
},
modules: {
}
})
// 3.导出store对象
export default store
main.js代码:
import Vue from 'vue'
import App from './App'
import store from './store'
Vue.config.productionTip = false
// 其他组件就能通过 $store 获取到store
new Vue({
el: '#app',
store,
render: h => h(App)
})
代码:
-----App.vue内容-----
{{ $store.state.counter }}
-----HelloVuex内容-----
Vuex有几个比较核心的概念:
State
Getters
Mutation
Action
Module
store下index.js代码:
const store = new Vuex.Store({
// 这五个一般都是固定的,是对象
state: { //保存状态
counter: 1000,
students: [{
id: 110,
name: 'why',
age: 18
},
{
id: 111,
name: 'kobe',
age: 24
},
{
id: 112,
name: 'james',
age: 30
},
{
id: 113,
name: 'curry',
age: 10
}
],
},
// ...
})
App.vue代码
// App.vue
computed: {
// 用计算属性的话 代码不好复用
// 找出多于20岁的学生
more20stu() {
return this.$store.state.students.filter(s => s.age > 20)
}
},
store下index.js代码:
const store = new Vuex.Store({
// 这五个一般都是固定的,是对象
state: { //保存状态
counter: 1000,
students: [{
id: 110,
name: 'why',
age: 18
},
{
id: 111,
name: 'kobe',
age: 24
},
{
id: 112,
name: 'james',
age: 30
},
{
id: 113,
name: 'curry',
age: 10
}
],
},
getters: { // 可以认为是 store 的计算属性
// getters里面的方法 也会有state参数
powerCounter(state) {
return state.counter * state.counter
},
// 找出多于20岁的学生
// filter(回调函数(当前元素的值))
more20stu(state) {
return state.students.filter(s => s.age > 20)
},
}
})
注意,getter 在通过属性访问时是作为 Vue 的响应式系统的一部分缓存其中的
const store = new Vuex.Store({
// 这五个一般都是固定的,是对象
state: { //保存状态
counter: 1000,
students: [{
id: 110,
name: 'why',
age: 18
},
{
id: 111,
name: 'kobe',
age: 24
},
{
id: 112,
name: 'james',
age: 30
},
{
id: 113,
name: 'curry',
age: 10
}
],
},
getters: { // 可以认为是 store 的计算属性
// getters里面的方法 也会有state参数
powerCounter(state) {
return state.counter * state.counter
},
// 找出多于20岁的学生
// filter(回调函数(当前元素的值))
more20stu(state) {
return state.students.filter(s => s.age > 20)
},
// 找出大于20岁学生的个数
more20stuLength(state, getters) { // Getters 也可以接受其他 getters 作为第二个参数:
// return state.students.filter(s => s.age > 20).length
return getters.more20stu.length
},
},
})
store下index.js代码:
// ..
getter:{
// 找出年龄大于参数age的学生
moreAgeStu(state) { // getters传递参数 只能让getters本身返回另一个函数.
// return function (age) {
// return state.students.filter(s => s.age > age)
// }
return age => {
return state.students.filter(s => s.age > age)
}
}
}
// ...
App.vue
----------App内容: getters相关信息----------
{{ $store.getters.powerCounter }}
{{ $store.getters.more20stu }}
{{ $store.getters.more20stuLength }}
{{ $store.getters.moreAgeStu(12) }}
// ...
mutations: { // 方法 修改state唯一途径 同步操作
// mutations传递参数
// 单个参数
incrementCount(state, count) {
console.log(count);
state.counter +=count
},
},
// ...
// template...
// ...
// methods
addCount(count) {
// payload: 负载
// 1.普通的提交封装 这样写的 mutations里的 incrementCount(state, count) 的count就是count
this.$store.commit('incrementCount', count) // 单个参数
},
// ...
mutations: { // 方法 修改state唯一途径 同步操作
// 参数是对象
addStudent(state, stu) {
state.students.push(stu)
},
},
// ...
// App.vue的methods
addStudent() {
// 提交对象
const stu = { id: 114, name: "alan", age: 35 };
this.$store.commit("addStudent", stu);
},
// ...
mutations:{
// 特殊的提交封装
incrementCount(state, payload) {
// console.log(count);
state.counter += payload.count
},
}
addCount(count) {
// payload: 负载
// 1.普通的提交封装 这样写的 mutations里的 incrementCount(state, count) 的count 就是count
// this.$store.commit('incrementCount', count) // 单个参数
// 2.特殊的提交封装 mutations里的 incrementCount(state, count) 的count 是一个对象 写成payload比较合适,通过payload.count取
this.$store.commit({
type: "incrementCount",
count,
});
},
const store = new Vuex.Store({
state: { //保存状态
// state里面都属性初始化后 每个属性对应一个dep 监听属性变化 dep观察者模式
info: {
name: 'kobe', // Dep -> [Watcher]
age: 40, // Dep -> [Watcher]
height: 1.98 // Dep -> [Watcher]
}
},
mutations: {
updateInfo(state) {
// 可以响应式
state.info.name = 'coderwhy'
// 不能响应式
// state.info['address'] = '洛杉矶' // 我的可以响应???视频中的不可以
Vue.set(state.info, 'address', '洛杉矶')
// delete state.info.age // “delete+某个属性”该方式做不到响应式
// Vue.delete(state.info, 'age') // Vue.delete() 响应式
}
}
})
// template
// ...
----------App内容: info对象的内容是否是响应式----------
{{ $store.state.info }}
// ...
// methods
updateInfo() {
this.$store.commit('updateInfo')
}
es5风格方法写法
export const INCREMENT = 'increment'
// import INCREMENT from './mutations-types'// 不能这样导入,只能是export default
// export导出,导入需要加 {}
import {
INCREMENT
} from './mutations-types'
const store = new Vuex.Store({
state: { ... },
mutations: {
// 我们可以使用 ES2015 风格的计算属性命名功能来使用一个常量作为函数名
[INCREMENT](state) {
state.counter++
},
}
})
mutations: {
updateInfo(state) {
// 错误的代码: 不能在这里进行异步操作
setTimeout(() => {
state.info.name = 'coderwhy'
}, 1000)
}
}
// ...
updateInfo() {
// this.$store.commit('updateInfo') // 这样没有经过actions
// 异步修改信息 actions
// this.$store.dispatch('aUpdateInfo',"我是携带的信息");// 携带一个参数
// 通知外面已经改成功了 -> commit调用之后就算成功,利用对象的方法回调
this.$store.dispatch('aUpdateInfo', {
message: '我是携带的信息',
success: () => {
console.log('里面已经完成了');
}
})
},
// ...
// ...
const store = new Vuex.Store({
mutations:{
updateInfo(state) {
state.info.name = 'coderwhy'
}
},
actions:{
// 默认参数 context: 上下文 现在先理解成store
aUpdateInfo(context, payload) {
setTimeout(() => {
context.commit('updateInfo')
console.log(payload.message);
payload.success() // 调用回调 告诉外面已经成功
}, 1000)
},
}
})
// ...
// ...
actions:{
// Action返回的Promise
aUpdateInfo(context, payload) {
return new Promise((resolve, reject) => {
setTimeout(() => {
context.commit('updateInfo');
console.log(payload);
resolve('1111111')
}, 1000)
}) // then()在App.vue里面写
}
}
// ...
// ...
updateInfo() {
// 之前写的不够优雅 回调消息与携带信息混在一起,现在用Promise封装再用resolve调用
this.$store.dispatch("aUpdateInfo", "我是携带的信息").then((res) => {
console.log("里面完成了提交");
console.log(res);
});
},
// ...
const moduleA = {
// 组件里面通过$store.state.a.name获取属性
state: {
name: 'zhangsan'
},
// 官网state写法
// state: () => ({
// }),
// 使用 this.$store.commit() 提交
mutations: {
updateName(state, payload) {
state.name = payload
}
},
// $store.getters.fullname调用
getters: {
fullname(state) {
return state.name + '11111'
},
fullname2(state, getters) { // 使用其他getters
return getters.fullname + '2222'
},
// rootState 可以使用大的state里面的值
fullname3(state, getters, rootState) {
return getters.fullname2 + rootState.counter
}
},
// this.$store.dispatch() 提交
actions: {
// 这里的 context 不是store对象了 而是这个module对应的mutations
aUpdateName(context) {
console.log(context);
setTimeout(() => {
context.commit('updateName', 'wangwu')
}, 1000)
}
}
}
关于state:()=>({})写法
这样写是为了给箭头函数返回JSON对象,如果不加(),那么{}中的内容将会被当做代码,这样js语法错误。
例如下面这个,moudeA.state()返回{a:1,b:2}
const moduleA = { state: () => ({ a:1,b:2}) }
要是不加括号,{a:1,b:2}中a:1,b:2为js代码会出错
const moduleA = { state: () => { a:1,b:2} }
// ...
----------App内容: modules中的内容----------
{{ $store.state.a.name }}
{{ $store.getters.fullname }}
{{ $store.getters.fullname2 }}
{{ $store.getters.fullname3 }}
// ...
// methods
updateName() {
this.$store.commit("updateName", "lisi");
},
asyncUpdateName() {
this.$store.dispatch("aUpdateName");
},
当我们的Vuex帮助我们管理过多的内容时, 好的项目结构可以让我们的代码更加清晰.
import Vue from 'vue'
import Vuex from 'vuex'
import mutations from './mutations'
import actions from './actions'
import getters from './getters'
import moduleA from './modules/moduleA'
// 1.安装插件
Vue.use(Vuex)
// 2.创建对象 new Vuex.Store
// state 一般不会抽出去 而是写在这个文件
const state = {
counter: 1000,
students: [{
id: 110,
name: 'why',
age: 18
},
{
id: 111,
name: 'kobe',
age: 24
},
{
id: 112,
name: 'james',
age: 30
},
{
id: 113,
name: 'curry',
age: 10
}
],
info: {
name: 'kobe', // Dep -> [Watcher]
age: 40, // Dep -> [Watcher]
height: 1.98 // Dep -> [Watcher]
}
}
const store = new Vuex.Store({
// 这五个一般都是固定的,是对象
state, //保存状态
mutations, // 方法 修改state唯一途径 同步操作
actions, // 如果有异步操作在这里写 比如网络请求
getters,
modules: {
a: moduleA
}
})
// 3.导出store对象
export default store
import {
INCREMENT
} from "./mutations-types";
export default {
// 定义方法也可以这样写 ['text'](){}
[INCREMENT](state) {
// 不需要this
state.counter++
},
decrement(state) {
state.counter--
},
// Mutation传递参数
incrementCount(state, payload) { // 多个参数 对象
// console.log(count);
state.counter += payload.count
},
addStudent(state, stu) {
state.students.push(stu)
},
updateInfo(state) {
state.info.name = 'coderwhy'
}
}
export default {
// 也会有state参数
powerCounter(state) {
return state.counter * state.counter
},
more20stu(state) {
return state.students.filter(s => s.age > 20)
},
more20stuLength(state, getters) { // 再多加一个getters参数
// return state.students.filter(s => s.age > 20).length
return getters.more20stu.length
},
moreAgeStu(state) {
return age => {
return state.students.filter(s => s.age > age)
}
}
}
export default {
aUpdateInfo(context, payload) {
return new Promise((resolve, reject) => {
setTimeout(() => {
context.commit('updateInfo');
console.log(payload);
resolve('1111111')
}, 1000)
})
}
}
export default {
state: {
name: 'zhangsan'
},
mutations: {
updateName(state, payload) {
state.name = payload
}
},
getters: {
fullname(state) {
return state.name + '11111'
},
fullname2(state, getters) {
return getters.fullname + '2222'
},
fullname3(state, getters, rootState) {
return getters.fullname2 + rootState.counter
}
},
actions: {
aUpdateName(context) {
console.log(context);
setTimeout(() => {
context.commit('updateName', 'wangwu')
}, 1000)
}
}
}
- 常见的网络请求模块,以及优缺点对比。
- JSONP的原理和封装
- JSONP原理回顾
- JSONP请求封装
- axios的内容详解
- 认识axios网络模块
- 发送基本请求
- axios创建实例
- axios拦截器的使用
HelloWorld.vue
{{categories}}
这种开发思路不好,每个组件对第三方框架依赖太强了,假如这个框架不再维护,或者有bug,更换框架得一个个文件查找修改
要有这种开发意识:只要引用第三方的东西,千万不要在多个组件对它有依赖
那要怎么做?
单独建一个文件,对它进行封装
// request.js
import axios from 'axios'
// request封装v1.0 使用回调,返回数据
export function request(config, success, failure) {
// 1.创建axios的实例
const instance = axios.create({
baseURL: 'http://123.207.32.32:8000',
timeout: 5000
})
// 发送真正的网络请求
instance(config)
.then(res => {
// console.log(res);
success(res);
})
.catch(err => {
// console.log(err);
failure(err)
})
}
// main.js
// 5.封装request模块
import {
request
} from "./network/request";
// request封装v1.0 的调用
request({
url: '/home/multidata'
}, res => {
console.log(res);
}, err => {
console.log(err);
})
// request.js
// request封装v2.0 直接传一个config 再从里面取success,failure
export function request(config) {
// 1.创建axios的实例
const instance = axios.create({
baseURL: 'http://123.207.32.32:8000',
timeout: 5000
})
// 发送真正的网络请求
instance(config.baseConfig)
.then(res => {
// console.log(res);
config.success(res);
})
.catch(err => {
// console.log(err);
config.failure(err)
})
}
// main.js
// request封装v2.0 的调用
request({
baseConfig: {
url:'/home/multidata'
},
success: function (res) {
console.log(res);
},
failure: function (err) {
console.log(res);
}
})
request封装v3.0 使用Promise
// request.js
// request封装v3.0 使用Promise
export function request(config) {
return new Promise((resolve, reject) => {
// 1.创建axios的实例
const instance = axios.create({
baseURL: 'http://123.207.32.32:8000',
timeout: 5000
})
// 发送真正的网络请求
instance(config)
.then(res => {
resolve(res)
})
.catch(err => {
reject(err)
})
})
}
// main.js
request({
url: '/home/multidata'
}).then(res => {
console.log(res);
}).catch(err => {
console.log(err);
})
request封装v4.0 直接return instance(config) 因为它这个本身就是一个Promise
// request.js
// request封装v4.0 直接return instance(config) 因为它这个本身就是一个Promise
export function request(config) {
// 1.创建axios的实例
const instance = axios.create({
baseURL: 'http://123.207.32.32:8000',
timeout: 5000
})
// 2.发送真正的网络请求
return instance(config) // 本身的返回值就是个promise
}
// main.js
// request封装v3.0 / v4.0的调用
request({
url: '/home/multidata'
}).then(res => {
console.log(res);
}).catch(err => {
// console.log(err);
})
// request.js
export function request(config) {
// 1.创建axios的实例
const instance = axios.create({
baseURL: 'http://123.207.32.32:8000',
timeout: 5000
})
// 2.axios的拦截器
// 2.1.请求拦截的作用 成功/失败
instance.interceptors.request.use(config => {
// console.log(config);
// 1.比如config中的一些信息不符合服务器的要求
// 2.比如每次发送网络请求时, 都希望在界面中显示一个请求的图标
// 3.某些网络请求(比如登录(token)), 必须携带一些特殊的信息
// 得把config再返回
return config
}, err => {
// console.log(err);
})
// 2.2.响应拦截 成功/失败
instance.interceptors.response.use(res => {
// console.log(res);
return res.data
}, err => {
console.log(err);
})
// 3.发送真正的网络请求
return instance(config) // 本身的返回值就是个promise
}
// main.js
// request封装v3.0 / v4.0的调用
request({
url: '/home/multidata'
}).then(res => {
console.log(res);
}).catch(err => {
// console.log(err);
})
你们的支持是我最大的动力
(项目不知道要不要也写成笔记)