Vue.js学习笔记第四部分Vuex状态管理和axios

因为hexo会试图渲染页面的mastache语法 所以每个mastache语法间加入空格

vue版本:v2.6.12

一. Promise知识回顾

  • Promise是异步编程的一种解决方案。

  • 常见场景:网络请求

  • 解决问题:回调地狱

    • $.ajax('url1',function(data1){
        $.ajax(data1['url2'],function(data2){
          $.ajax(data1['url3'],function(data3){
            $.ajax(data1['url4'],function(data4){
              console.log(data4);
            })
          })
        })
      })
      <!--0-->
      

转树形为链式

Promise的链式调用

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
new Promise((resolve,reject)=>{
setTimeout(()=>{
resolve('aaa')
},1000)
}).then(res=>{
//自己处理
console.log(res,'第一层10行处理代码');

//2
// return Promise.reject('err')
throw 'err'
}).then(res =>{
console.log(res, '第二层10行代码处理');

return res + '222'
}).then(res => {
console.log(res,'第三层10行处理代码');
}).catch(err=>{
console.log(err);
})

Promise.all

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
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('result1')
},2000)
}),
new Promise((resolve, reject)=>{
setTimeout(()=>{
resolve('result2')
},1000)
})
]).then(results=>{
console.log(results[0]);
console.log(results[1]);
})

Promise语法糖await

1
2
3
4
5
6
7
8
async function callAjax(){
var a = await myAjax('xxx')
console.log(a)
var b = await myAjax('xxx')
console.log(b)
var c = await myAjax('xxx')
console.log(c)
}

二. Vuex

官方解释:Vuex是一个专为Vue.js应用程序开发的状态管理模式

  • 它采用 集中式存储管理 应用的所有组件的状态,并以相应的规则保证状态以一种可预测的方式发生变化。
  • Vuex也集成到Vue的官方调试工具devtools extension,提供了诸如零配置的time-travel调试、状态快照导入导出等高级调试功能

状态管理是什么?

  • 我们可以简单的将其看成需要多个组件共享的变量全部存储在一个对象里面。
  • 然后将这个对象放在顶层的Vue实例中,实现多个组件变量的共享

哪些状态需要共享?

  • 比如用户登录状态、用户名称、头像、地理位置
  • 商品收藏、购物车物品

文件:Vuex存储文件位于src/store/index.js

state基本使用

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
import Vue from 'vue'
import Vuex from 'vuex'

Vue.use(Vuex)

export default new Vuex.Store({
state: {
counter: 1000
},
mutations: {
},
actions: {
},
modules: {
}
})

组件中访问state

{ {$store.state.counter} }

提交mutations修改state

  • Vuex的store状态更新唯一方式:提交Mutation

store/index.js

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
export default new Vuex.Store({
state: {
counter: 1000
},
mutations: {
//添加对应修改函数 increment是事件类型 后面的为回调函数
//回调函数后第一个参数就是state
increment(state) {
state.counter++
},
decrement(state) {
state.counter--
}
},
actions: {
},
modules: {
}
})

组件User.vue

1
2
3
4
5
6
7
8
9
10
11
12
13
<script>
export default {
//组件内调用对应修改函数
methods: {
addition() {
this.$store.commit('increment')
},
subtraction() {
this.$store.commit('decrement')
}
}
}
</script>

带参数提交mutations修改state

组件:

1
2
3
4
5
6
7
8
9
10
11
12
13
...
<button @click="addCount(5)">+5</button>
<button @click="addCount(10)">+10</button>
...
<script>
export default {
methods: {
addCount(count) {
this.$store.commit('incrementCount',count)
}
}
}
</script>

store/index.js

1
2
3
4
5
6
7
8
9
10
...
state: {
counter: 1000,
},
mutations: {
incrementCount(state,count) {
state.counter += count
}
}
...
  • 提交mutation携带的额外参数被称为载荷(Payload)
    • 可以是一个对象

带对象参数提交mutations修改state

组件:

1
2
3
4
5
6
7
8
9
10
11
12
...
addCount(count) {
//1.普通的提交封装
// this.$store.commit('incrementCount',count)

//2.特殊的提交封装(提交对象)
this.$store.commit({
type: 'incrementCount',
count
})
},
...

store/index.js:

1
2
3
4
5
6
7
8
9
10
...
state: {
counter: 1000,
},
mutations: {
incrementCount(state,payload) {
state.counter += payload.count
},
},
...

Getters基本使用

类似于计算属性

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
export default new Vuex.Store({
state: {
counter: 1000,
students:[
{id:101,name:'111',age:14},
{id:102,name:'112',age:15},
{id:103,name:'113',age:16}
]
},
getters: {
powerCounter(state) {
return state.counter * state.counter
},
more20stu(state){
return state.students.filter(s => s.age > 20)
}
//需求:获取age>20的人数 设置成getters属性
more20stuLength(state){
return state.students.filter(s => s.age > 20).length
},
//简写,使用第二个getters属性,方便我们拿取其他getters
more20stuLength2(state, getters){
return getters.more20stu.length
}
},
})

调动

1
<h2>{ {$store.getters.powerCounter} }</h2>

getters参数传递

1
2
3
4
5
6
7
8
9
10
11
12
moreAgeStu(state) {
return function(age) {
return state.students.filter(s => s.age > age).length
}
//可以改成箭头函数===============
return age => {
return state.students.filter(s => s.age > age).length
}

}
//再简写,可读性差,不喜欢
moreAgeStu:(state=>age=>state.students.filter(s=>s.age>age))

调用

1
<h2>{{$store.getters.moreAgeStu(18)}}</h2>

mutations的类型常量

  • 我们可以发现mutations里面的属性名和组件中commit提交的参数名是一致的,当属性少时还不会有问题,但是当有几百个这样的变量时要改就非常麻烦了

  • 我们是否可以将其抽离出来?

  • 在store目录下新建一个mutations-types.js文件

    • 这个文件专门用来定义变量,例如:

    • export const INCREMENT = 'increment'
      <!--15-->
  • 改写组件中的调用

    • ...
      methods: {
        addition() {
            this.$store.commit(INCREMENT)
        },
      ...
      <!--16-->
  • 这样如果等多次用到常量,并且当常量名字拼错后马上会报undefine,然后根据填错的常量名很快就能定位到是哪个常量

Mutation同步函数

  • 通常情况下,Vuex要求我们Mutation中的方法必须是同步方法
    • 主要的原因是当我们使用devtools时,可以帮助我们捕捉到mutation的快照
    • 如果是异步操作,那么devtools将不能很好的追踪这个操作什么时候会完成

Action基本定义

  • 有些情况我们确实希望在Vuex中进行一些异步操作,比如网络请求必然是异步的,此时如何处理呢

  • Action类似于Mutation,用来代替Mutation进行异步操作

  • store/index.js

    • state: {
        students:[
          {id:101,name:'111',age:14},
          {id:102,name:'112',age:15},
          {id:103,name:'113',age:16}
        ]
      },
      mutations: {
        updateInfo(state){
          state.students.push({id:103,name:'113',age:16})
        }
      },
      actions: {
        //上下文 现在是store对象
        aUpdateInfo(context) {
          setTimeout(() => {
            context.commit('updateInfo')
          },1000)
        }
      },
      <!--17-->
      

actions结合promise

  • store/index.js

    • actions: {
          //上下文 现在是store对象
          aUpdateInfo(context, payload) {
              return new Promise((resolve, reject) => {
                  setTimeout(() => {
                      context.commit('updateInfo')
                      console.log(payload);
                      resolve('1111')
                  },1000)
              })
          }
      },
      <!--18-->
  • 返回promise对象, 这样的话就能对执行结果成功或者失败都进行处理了

modules基本使用

  • Modeule是模块的意思

    • Vue使用单一状态树,意味着很多状态交给Vuex管理
    • 当应用变得非常复杂时,store对象就有可能变成非常臃肿
    • 为了解决这个问题,Vuex允许我们将store分割成模块,每个模块有自己的state、mutation、action、getters等
  • 组织方式

    • const moduleA = {
          state: {...},
          mutations: {...},
          actions: {...},
          getters: {...}
      }
      
      const moduleB = {
          state: {...},
          mutations: {...},
          actions: {...}
      }
      
      const store = new Vuex.Stroe({
          modules: {
              a: moduleA,
              b: moduleB
          }
      })
      
      store.state.a //moduleA的状态     
      store.state.b //moduleB的状态  
      <!--19-->
      

三. 网络请求封装

1.网络请求模块选择-axios

  • Vue中发送网络请求方式非常多
  • 选择一:传统的Ajax是基于XMLHttpRequest(XHR)
  • 为什么不用它:
    • 配置和调用方式等非常混乱
    • 编码可读性差
    • 真实开发中很少直接用,而是使用jQuery-Ajax
  • 选择二:jQuery-Ajax
    • 相对于传统Ajax非常好用
  • 为什么不用它:
    • 在Vue整个开发过程中不需要使用jQuery
    • jQuery代码1w+行,而Vue代码也1w+行
    • 没有必要为了网络请求一个功能就引用jQuery
  • 选择三:官方在Vue1.x时推出了Vue-resource
    • 相对jQuery小很多
  • 为什么不用它:
    • 不更新了
  • 选择四:作者在说明不更新和维护Vue-resource的同时,推荐了一个框架:axios
    • axios有非常多的优先,用起来也方便
  • 功能特点:
    • 在浏览器中发送XMLHttpRequests请求
    • 在node.js中发送http请求
    • 支持 Promise API
    • 拦截请求和响应
    • 转换请求和响应数据
    • 等等

2.axios框架基本使用

axios请求方式
  • 支持多种请求方式:
    • 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.patc(url, [data,[config]])

安装npm install axios -save

引用import axios from 'axios'

使用:

  • 简单使用:

    • axios({
          url: 'http://123.207.32.32:8000/home/multidata',
          method: 'post'
      }).then(res => {
          console.log(res);
      })
      <!--20-->
      

3.axios发送并发请求

  • axios.all([
    axios({
        url: 'http://123.207.32.32:8000/home/data',
        params: {
            type: 'pop',
            page: 1
        }
    }), 
    axios({
        url: 'http://123.207.32.32:8000/home/multidata'
    })])
    .then(axios.spread((res1,res2) => {
        console.log(res1)
        console.log(res2)
    }))
    <!--21-->
  • 常见配置选项

    • 请求地址:url: '/user'
    • 请求类型:method: 'get'
    • 请求根路径:baseURL: 'http://www.mt.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'}
    • 超时设置:timeout: 1000
    • 跨域是否带Token:withCredentials: false
    • 自定义请求处理:adapter: function(resolve, reject, config){}
    • 身份验证信息:auth: {uname: '', pwd: '12'}
    • 响应的数据格式json/blob/document/arraybuffer/text/stream:``responseType: ‘json’`

5.axios实例和模块封装

  • axios实例解决分布式服务器的问题

    • const instance1 = axios.create({
          baseURL: 'http://123.207.32.32:8000',
          timeout: 5000
      })
      
      instance1({
          url: '/home/multidata'
      }).then(res => {
          console.log(res);
      })
      
      instance1({
          url: '/home/data',
          params: {
              type: 'pop',
              page: 1
          }
      }).then(res => {
          console.log(res);
      })
      
      const instance2 = axios.create({
          baseURL: 'http://222.111.33.33:8080/',
          timeout: 1000,
      })
      
      instance2({
          url: '/home/data',
          params: {
              type: 'pop',
              page: 1
          }
      }).then(res => {
          console.log(res);
      })
      <!--22-->
    • 使用:

    • import {request} from './network/request.js'
      request({
          url: '/home/multidata',
      }, res => {
          console.log(res);
      }, err => {
          console.log(err);
      })
      <!--23-->
    • 使用:

    • request({
          //对象解构
          baseConfig: {
      
          },
          success: function(res) {
      
          },
          failure: function (err) {
      
          }
      })
      <!--24-->
    • 使用:

    • request({
          url: '/home/multidata'
      }).then(res => {
          console.log(res);
      }).catch(err => {
          console.log(err);
      })
      <!--25-->
    • 使用:

    • ```javascript
      request({

      url: '/home/multidata'

      }).then(res => {

      console.log(res);

      }).catch(err => {

      console.log(err);

      })

      1
      2
      3
      4
      5
      6
      7
      8
      9
      10
      11
      12
      13
      14
      15
      16
      17
      18
      19
      20
      21
      22
      23
      24
      25
      26
      27
      28
      29
      30
      31
      32
      33
      34
      35
      36
      37
      38
      39
      40
      41
      42
      43
      44

      #### 6.axios拦截器的使用

      - axios提供了拦截器,用于发送每次请求或得到响应后,进行对应处理

      - 作用:请求之前想要做什么事情就需要拦截器,比如可以在请求前加一个loading,请求成功后关闭loading

      - 使用:

      - ```javascript
      import axios from 'axios'
      export function request(config){
      //1.创建axios实例
      const instance = axios.create({
      baseURL: 'http://123.207.32.32:8000',
      timeout: 5000
      })
      //2.axios拦截器
      //拦截请求
      instance.interceptors.request.use(config => {
      //发送请求成功
      console.log(config);
      // 1.比如config中一些信息不符合服务器要求
      // 2.比如每次发送网络请求,都希望在界面中显示一个请求图标
      // 3.某些网络请求(比如登陆),必须携带特殊信息(token)
      return config
      },err => {
      //发送请求失败
      console.log(err);
      })
      //拦截响应
      instance.interceptors.response.use(res => {
      // 响应成功
      console.log(res);
      // 比如只拿出res.data数据
      return res
      }, err => {
      // 响应失败
      console.log(err);
      })

      //3.发送真正的网络请求
      return instance(config)
      }

评论