Promise/Vuex/网络模块封装
1、Promise
ES6中一个非常重要和好用的特性就是Promise。
Promise是异步编程的一种解决方案。
避免当网络请求非常复杂时,就会出现回调问题。
1.1、Promise基本使用
new Promise((resolve,reject)=>{
setTimeout(function(){
resolve('xxx')
reject('Error Data')
},1000)
}).then(data=>{console.log(data);}).catch(error=>{console.log(error);})
new Promise很明显是创建一个Promise对象,小括号中((resolve, reject) => {})也很明显就是一个函数。成功:调用resolve(message),这个时候,我们后续的then会被回调。失败:调用reject(error),这个时候,我们后续的catch会被回调。
异步操作之后会有三种状态
pending:等待状态,比如正在进行网络请求,或者定时器没有到时间。
fulfill:满足状态,当我们主动回调了resolve时,就处于该状态,并且会回调.then()
reject:拒绝状态,当我们主动回调了reject时,就处于该状态,并且会回调.catch()
1.2、Promise链式调用
Promise.resovle():将数据包装成Promise对象,并且在内部回调resolve()函数
Promise.reject():将数据包装成Promise对象,并且在内部回调reject()函数
...
}).then(data=>{console.log(data);}).catch(error=>{console.log(error);})
.then(data=>{console.log(data);}).catch(error=>{console.log(error);})
2、Vuex
Vuex 是一个专为 Vue.js 应用程序开发的状态管理模式。
状态管理:把需要多个组件共享的变量全部存储在一个对象里面。然后,将这个对象放在顶层的Vue实例中,让其他组件可以使用。VueJS带给我们最大的便利是,所以要保证属性做到响应式,自己封装可能稍微麻烦一些,所以Vuex就是为了提供这样一个在多个组件间共享状态的插件。
2.1、Vuex的使用
创建一个文件夹store,并且在其中创建一个index.js文件
在index.js文件中写入如下代码
import Vuex from 'vuex'
import Vue from 'vue'
Vue.use(Vuex)
const store = new Vuex.store({
state:{
count:0
},
//Vuex中store数据改变的唯一方法就是mutation!
mutations:{
increment(state){state.count--}
}
})
export default store
然后挂载到Vue实例中,在main.js里面中
import Vue from 'vue'
import App from './App'
import store from './store'
new Vue({
el:'#app',
store,
render:h=>h(App)
})
使用Vuex的count
<template中><button @click="increment">+1</buttton>
<script>
export default {
name:'App',
components:{},
computed:{
count:function(){return this.$store.state.count}
},
methods:{
increment:function(){this.$store.commit('increment')}
}
}
小总结
1、提取出一个公共的store对象,用于保存在多个组件中共享的状态
2、将store对象放置在new Vue对象中,这样可以保证在所有的组件中都可以使用到
3、在其他组件中使用store对象中保存的状态即可
通过this.s t o r e . s t a t e . 属 性 的 方 式 来 访 问 状 态 通 过 t h i s . store.state.属性的方式来访问状态 通过this.store.state.属性的方式来访问状态通过this.store.commit(‘mutation中方法’)来修改状态
注意事项:
1、我们通过提交mutation的方式,而非直接改变store.state.count。
2、这是因为Vuex可以更明确的追踪状态的变化,所以不要直接改变store.state.count的值。
2.2、Vuex核心概念
State、Getters、Mutation、Action、Module
2.2.1、State
单一状态树。实际开发中,如果你的状态信息是保存到多个Store对象中的,那么之后的管理和维护等等都会变得特别困难。所以Vuex也使用了单一状态树来管理应用层级的全部状态。单一状态树能够让我们最直接的方式找到某个状态的片段,而且在之后的维护和调试过程中,也可以非常方便的管理和维护。
2.2.2、Getters
场景:需要从store中获取一些state变异后的状态
getters:{
xxx:state=>{return state.students.filter(s=>s.age<-10).length}
}
Getters作为参数和传递参数
getters默认是不能传递参数的, 如果希望传递参数, 那么只能让getters本身返回另一个函数。
2.2.3、Mutation
Vuex的store状态的更新唯一方式:提交Mutation
Mutation主要包括两部分:
字符串的事件类型(type)
一个回调函数(handler),该回调函数的第一个参数就是state。
mutation的定义方式:
mutation:{incre(state){state.count++}}
通过mutation更新
incre:function(){this.$store.commit('incre')}
<Mutation传递参数>
通过mutation更新数据的时候携带一些额外的参数,参数被称为是mutation的载荷(Payload)
decrement(state,n){state.count -=n}
decrement:function(){this.$store.commint('decrement',2)}
如果参数是多个, 我们通常会以对象的形式传递,也就是payload是一个对象。然后再从对象中取出相关的信息。
decrement2(state,payload){state.count =payload.count}
decrement2:function(){this.$store.commint('decrement2',{count:0})}
<Mutation提交风格>
上面的通过commit进行提交是一种普通的方式
Vue还提供了另外一种风格, 它是一个包含type属性的对象
this.$store.commit({
type:'changeCount',
count:100
})
Mutation中的处理方式是将整个commit的对象作为payload使用, 所以代码没有改变
changeCount(state,payload){state.count =payload.count}
<Mutation响应规则>
Vuex的store中的state是响应式的, 当state中的数据发生改变时, Vue组件会自动更新。
(1)提前在store中初始化好所需的属性.
(2)当给state中的对象添加新属性时, 使用下面的方式:
方式一: 使用Vue.set(obj, ‘newProp’, 123)
方式二: 用新对象给旧对象重新赋值
mytations:{
updateInfo(state,payload){
//方法一
Vue.set(state.info,'height',payload.height)
//方法二
state.info={...state.info,'height':payload.geight}
}
}
<Mutation常量类型>
可以将这些常量放在一个单独的文件中, 方便管理。
mutations:{
[types.UPDATE_INFO](state,payload){
state.info={...state.info,'height':payload.height}
}
}
然后在vue文件中直接引用。
<Mutation同步函数>
通常情况下, Vuex要求我们Mutation中的方法必须是同步方法。
(1)主要的原因是当我们使用devtools时, 可以devtools可以帮助我们捕捉mutation的快照。
(2)但是如果是异步操作, 那么devtools将不能很好的追踪这个操作什么时候会被完成。
通常情况下, 不要再mutation中进行异步的操作
2.2.4、Action
比如网络请求, 必然是异步的。这个时候Action类似于Mutation, 但是是用来代替Mutation进行异步操作的。
context:通过context去进行commit相关的操作, 也可以获取context.state等。但是注意, 这里它们并不是同一个对象。
const store = new Vuex.Store({
state:{count:0},
mutations:{inc(state){state.count--}},
actions:{inc(context){context.commit('inc')}
})
<Action的分发>
在Vue组件中, 如果我们调用action中的方法, 那么就需要使用dispatch
methods:{
inc(){this.$store.dispatch('inc')}
}
<Action返回的Promise>
ES6语法中Promise经常用于异步操作。在Action中, 我们可以将异步操作放在一个Promise中, 并且在成功或者失败后, 调用对应的resolve或reject。
actions:{
inc(context){
retrun new Promise((resolve)=>
setTimeout((=>{
context.commit('inc')
resolve()
},1000))
)}}
调用
methods:{
inc(){
this.$store.dispatch('inc').then(res=>{
console.log('完成了更新');
})
}
}
2.2.5、Module
当应用变得非常复杂时,store对象就有可能变得相当复杂。所以我们将store分割成模块(Module), 而每个模块拥有自己的state、mutations、actions、getters等。
const moduleA={...}
const store = new Vuex.store({
modules:{a:moduleA}
})
store.state.a //-->moduleA的状态
<modele中写Actions与getters>
const moduleA={
actions:{...}
getters:{...}
}
2.3、项目结构
api:抽出API请求
store/index.js:组装模块并导出store的地方
store/actions.js:根级别的action
store/mutations.js:根级别的mutation
store/modules:写自己项目的模块
3、网络模块封装
常见的网络请求模块JSONP、axios
3.1、模块的选择
<1>不用Ajax
传统的Ajax是基于XMLHttpRequest(XHR),不使用的原因(1)配置和调用方式等非常混乱。(2)编码起来看起来繁琐,真实开发用的少,而是jQuery-Ajax。
<2>不用jQuery-Ajax
在Vue的整个开发中都是不需要使用jQuery了。如果用就相对于特意去使用了。
<3>不用Vue-resource(官方在Vue1.x的时候推荐)
因为Vue2.0之后,去掉了且不会再更新,不利于以后的维护。
<4>使用axios
3.1.1、jsonp封装
使用JSONP最主要的原因往往是为了解决跨域访问的问题。
JSONP的核心在于通过
3.1.2、axios
功能特点:在浏览器中发送 XMLHttpRequests 请求、在 node.js 中发送 http请求、支持 Promise API、拦截请求和响应、转换请求和响应数据等等
支持多种请求方式
axios(config)、axios.request(config)、axios.get(url[, config])、axios.delete(url[, config])、axios.head(url[, config])、axios.post(url[, data[, config]])、axios.put(url[, data[, config]])、axios.patch(url[, data[, config]])
<发送get请求>
import axios from 'axios'
export default{
name:'app',
created(){
//1、没有请求参数
axios.get('http://xxx').then(res=>{xxx}).catch(err=>{xxx})
}
//2、有请求参数
axios.get('http://xxx',params:{type:'sell',age:1}).then(res=>{xxx}).catch(err=>{xxx})
}
}
<发送并发请求>
有时候可能需求同时发送两个请求,使用axios.all, 可以放入多个请求的数组。axios.all([]) 返回的结果是一个数组,使用axios.spread 可将数组 [res1,res2] 展开为 res1, res2。
import axios from 'axios'
export default{
name:'app',
created(){
//2、发送并发请求
axios.all([axios.get('http://xxx'),axios.get('http://xxx'),{params:{type:'sell',age:1}}]).then(axios.spread((res1,res2)=>{
console.log(res1);
console.log(res2);
}))
}
}
<全局配置>
抽取一些公共固定的参数,如:
axios.defaults.baseURL = ‘IP’
axios.defaults.headers.post[‘Content-Type’] = ‘application/x-www-form-urlencoded’;
<常见的配置选项>
请求地址
url: ‘/user’,
请求类型
method: ‘get’,
请根路径
baseURL: ‘http://www.xx.com/api’,
请求前的数据处理
transformRequest:[function(data){}],
请求后的数据处理
transformResponse: [function(data){}],
自定义的请求头
headers:{‘x-Requested-With’:‘XMLHttpRequest’},
URL查询对象
params:{ id: 12 },
查询对象序列化函数
paramsSerializer: function(params){ }
request body
data: { key: ‘aa’},
超时设置s
timeout: 1000,
跨域是否带Token
withCredentials: false,
自定义请求处理
adapter: function(resolve, reject, config){},
身份验证信息
auth: { uname: ‘’, pwd: ‘12’},
响应的数据格式 json / blob /document /arraybuffer / text / stream
responseType: ‘json’,
3.1.3、实例
//创建新的实例
const axiosTns=axios.create({
baseURL:'xxx',
timeout:5000,
headers:{'Content-Type':'app;ication/x-www-from-x'}
})
//发送网络请求
axiosIns({
url:'/xx',
method:'get'
}).then(res=>{
console.log(xxx);
}).catch(err=>{xxx})
axios.js中封装
将实例和发送网络请求写一起
import originAxios from 'axios'
export default function axios(option){
return new Promise((resolve,reject)=>{
//创建实例与传入对象进行网络请求
})
}
3.2、拦截器
//配置请求和响应拦截
instance.interceptors.request.use(config=>{
console.log('来到了request拦截成功中')
return config
},err=>{console.log('来到了request拦截失败中');
return config})
instance.interceptors.response.use(response=>{
console.log('来到了response拦截成功中')
return response.data
},err=>{console.log('来到了response拦截失败中');
return err})
1、当发送网络请求时,添加动画组件
2、判断用户是否有token
3、对参数进行序列化
响应里面主要是对数据的过滤,再根据失败的不同状态跳不同页面。