vuex应用状态管理和axios网络请求响应

知识为进步之母,而进步又为富强之源泉。这篇文章主要讲述vuex应用状态管理和axios网络请求响应相关的知识,希望能为你提供帮助。
vuex应用状态管理和axios网络请求响应Vuex插件的安装在vue项目目录下执行如下命令:

npm install vuex@3.6.2 --save

安装完成后,在??package.json???的项目配置文件中显示出??vuex??安装版本.手动在项目的src目录下创建store文件夹和index.js文件,index.js文件内容如下。该文件内容就是Vuex进行状态集中管理的仓库。
import Vue from vue
import Vuex from vuex

//使用Vuex插件
Vue.use(Vuex)

//注意这里创建的是store对象,不是vuex对象
const store = new Vuex.Store(
state:
,
mutations:,
actions:,
getters:,
modules:
)
//导出对象
export default store

从上图中可以看到,在store对象中我们定义了一系列的子对象state、mutations、actions、getters、modules,这就是我们学习Vuex的主要内容。填空题,我们一点点来做。我们建好了“仓库”,还得把它引入到项目里面来。在main.js中加入如下代码:
import Vue from vue
import App from ./App
import router from ./router
import store from ./store
Vue.config.productionTip = false
/* eslint-disable no-new */
new Vue(
el: #app,
store,//加这里
router,
components:App ,
template: < App/>
)

计数器组件基础代码
定义两个组件,一个组件name为VuexCpn1,另一个组件name为VuexCpn2。其他代码完全相同,如下:
< template>
< div>
< div> 计数值:count< /div>
< button @click="count++"> +1< /button>
< button @click="count--"> -1< /button>
< /div>
< /template>

< script>
export default
name:VuexCpn1,
data()
return
count: 0

,

< /script>

如上图所示,我们定义的两个组件,除了名字不同,其他的代码完全一致。代码中定义了一个数据变量count。然后我们将这两个组件引入到同一个父组件里面,也就是说这两个组件是兄弟组件。下图是父组件的代码:
  • CPN.vue
< template>
< div>
< h1> CPN< /h1>
< vuex-cpn-1> < /vuex-cpn-1>
< vuex-cpn-2> < /vuex-cpn-2>
< /div>
< /template>
< script>
import VuexCpn1 from ./VuexCpn1.vue
import VuexCpn2 from ./VuexCpn2.vue
export default
name:CPN,
data()
return
count: 0

,
components:
VuexCpn2,
VuexCpn1


< /script>

实现效果如下:
vuex应用状态管理和axios网络请求响应

文章图片

两个组件的count的值不同步,因为count是分别定义在两个组件里面的,从程序运行的角度来讲,count是一个局部变量。如果我们希望点击组件VuexCpn1的按钮,同步影响组件VuexCpn2的count值;点击组件VuexCpn2的按钮,同步影响组件VuexCpn1的count值。该怎么做?比较麻烦的做法就是:点击VuexCpn1按钮,向父组件传递点击事件,父组件向VuexCpn2传递counter数据(参考组件化开发章节内容进行回顾)。还有一种简单的做法就是将count变成一个全局变量。
Vuex集中化存储
那么我们怎么让counter变成一个全局变量?答案就是使用vuex,将count定义在store/index.js中的store对象的state状态对象里面。
state:
count:0
,

然后修改VuexCpn1组件和VuexCpn2组件的代码如下:
< template>
< div>
< h1> CPN1< /h1>
< div> 计数值:$store.state.count< /div>
< button @click="$store.state.count++"> +1< /button>
< button @click="$store.state.count--"> -1< /button>
< /div>
< /template>
< script>

export default
name:VuexCpn1

< /script>

  • $store代表项目全局的集中存储的store对象,即store/index.js中定义的store对象
  • store对象中的state属性,用于定义全局变量,多个组件都可以使用该变量
实现效果如下:无论我们点击哪一个组件的加1减1按钮,count的值在两个组件里面的显示都是同步的一致的。由此我们可以知道$store.state.count状态是响应式的,即:我们点击组件VuexCpn1的按钮,组件VuexCpn2中的count也发生变化
【vuex应用状态管理和axios网络请求响应】
vuex应用状态管理和axios网络请求响应

文章图片

mutations的作用
  • 当我们点击任何一个组件中的按钮,实际上调用了“和store.state.count--”,就修改了公共状态count计数的值,从而影响另一个组件的显示内容同步。
  • 但是我在上一节中也说到了直接对$store.state中的状态进行赋值操作,是可以实现响应式的,即:state的变化影响所有引用它的视图的变化。
但是存在一个问题:我们没有办法进行状态跟踪,也就是我们只能看到状态的结果,无法知道状态改变的过程。如果我们很多个组件引用很多的公共状态state,该如何跟踪每一次点击按钮操作之后的state变化?如何留下痕迹?
直接对$store.state中的状态进行赋值操作是无法留痕的,我们需要使用mutation(改变)。mutation是实际上就是对state状态进行操作的自定义方法,通过触发mutations方法修改的state可以留痕,可以被vue devtools调试工具跟踪。
但是注意不要在mutation自定义方法里面进行异步操作,比如发送网络请求。因为异步回调无法有效的被mutation跟踪,所以mutation自定义方法里面必须是同步操作
mutations的基本使用
VuexCpn1和VuexCpn2的例子的基础上进行代码修改,首先定义mutations:
mutations:
add(state)
state.count++
,
sub(state)
state.count--


,

mutations自定义方法的第一个参数是就是state,我们可以通过state参数修改状态。然后在组件中使用**$store.commit()方法触发mutation& **,commit的第一个参数是mutation的方法名。如下:
< button @click="$store.commit(add)"> +1< /button>
< button @click="$store.commit(sub)"> -1< /button>

视图效果和使用“store.state.counter--”是一样的。但是使用mutation改变的state会留痕,可以被跟踪。
mutations方法携带参数
希望每点击一次按钮加5减5或加n减n,不再是加1减1。这样我们就希望mutation自定义的方法能够传参,这样我们就能够针对state状态做更灵活的操作,适应更广泛的需求。当然,这个功能是一定有的,语法如下:
mutations:
add(state)
state.count++
,
sub(state)
state.coun--
,
add_num(state,num)
state.count= state.count+num
,
sub_num(state,num)
state.count= state.count-num


,

以双组件计数器的例子,我们触发mutation的时候,是这样的代码:
< button @click="$store.commit(add_num,5)"> +< /button>
< button @click="$store.commit(sub_num,5)"> -< /button>

mutations常量
  • mutation函数一次定义,在多个组件内多次commit调用。
  • 触发mutation的方法commit的第一个参数,就是mutation函数的名称。
如果有开发人员修改了mutation函数的方法名,那么我们如何保证对应的commit方法的参数一也对应的进行修改?比较笨的方式就是字符串查找,然后一个一个改。还有一种情况就是:通过查找的方式一个一个改,漏掉了怎么办?这种可能性很大。所以我们在一开始就要避免这个问题:将mutation函数名称定义为常量。新建一个文件叫做store/mutation-types.js定义常量
let COUNTER_ADD =add1
let COUNTER_SUB =sub1
export
COUNTER_ADD,
COUNTER_SUB

在mutation函数定义的时候,先导入mutation-types,再使用[]引用常量
import
COUNTER_ADD ,
COUNTER_SUB
from ./mutation-types


mutations:
add(state)
state.count++
,
sub(state)
state.count--
,
add_num(state,num)
state.count= state.count+num
,
sub_num(state,num)
state.count= state.count-num
,
[COUNTER_ADD] (state,payload)
state.count = state.count + payload.num * payload.multiple
,
[COUNTER_SUB] (state,payload)
state.count = state.count - payload.num * payload.multiple
,
handleFirstNameVal(state,payload)
state.firstName = payload


,

在调用commit触发mutation的时候,同样先导入mutation-types,再使用常量。注意通过js模块导入的COUNTER_ADD 不能再html里面被使用,所以我们需要单独定义method,在method中使用常量。
< template>
< div>
< h1> CPN1< /h1>
< div> 计数值:$store.state.count< /div>
< button @click="addCounter()"> +1< /button>
< button @click="subCounter()"> -1< /button>
< /div>
< /template>

< script>
import
COUNTER_ADD ,
COUNTER_SUB
from ../store/mutation-types

export default
name:VuexCpn1,
data()
return
counter: 0

,
methods:
addCounter()
this.$store.commit(COUNTER_ADD,num:5,multiple:2)
,
subCounter()
this.$store.commit(COUNTER_SUB,num:5,multiple:2)



< /script>

全局计算属性getters
  • store.state的数据定义如下:
state:
count:0,
firstName:"",
lastName:""
,

  • allName1.vue
< template>
< div>
< label for="firstName"> firstName:< /label>
< input type="text" v-model="$store.state.firstName" id="firstName">
< label for="lastName"> lastName:< /label>
< input type="text" v-model="$store.state.lastName" id="lastName">

< div> VuexCpn1组件< /div>
< div> fullName:$store.state.firstName -$store.state.lastName < /div>
< /div>
< /template>

  • allName2.vue
< template>
< div>
< div> VuexCpn2组件< /div>
< div> fullName:$store.state.firstName -$store.state.lastName < /div>
< /div>
< /template>

  • AllName.vue
< template>
< div>
< h1> ALLNAME< /h1>
< all-name-1> < /all-name-1>
< all-name-2> < /all-name-2>
< /div>
< /template>

< script>
import allName1 from ./allName1.vue
import allName2 from ./allName2.vue
export default
name:ALLNAME,
data()
return
counter: 0

,
components:
allName1,
allName2


< /script>

最后的显示结果如下:
vuex应用状态管理和axios网络请求响应

文章图片

  • 当我们在第一个组件里面输入firstName和lastName的时候,另一个组件fullName也随之发生变化
  • 使用v-model指令绑定了store.state.lastName状态数据。
  • 我们看到上面代码中的fullName是通过$store.state.firstName -$store.state.lastName 拼接而成的,在两个组件里面分别进行计算得出fullName。
v-model绑定state状态数据的标准做法
上文中我们使用v-model绑定了$store.state状态数据,实现了输入框与state状态数据之间的绑定,但是不推荐这种做法。因为这种直接的绑定方式,状态无法被devtools跟踪
vuex应用状态管理和axios网络请求响应

文章图片

从devtools调试工具中可以看到:浏览器显示上已经为kobe-byrant,但是调试工具中的firstName和lastName仍然是空串。比较正规的做法是:v-model绑定计算属性。
< template>
< div>
< h1> AllName1< /h1>
< label for="firstName"> firstName:< /label>
< input type="text" v-model="firstName" id="firstName">
< label for="lastName"> lastName:< /label>
< input type="text" v-model="$store.state.lastName" id="lastName">

< div> AllName1组件< /div>
< div> fullName:$store.state.firstName -$store.state.lastName < /div>
< !-- < div> fullName:fullName< /div> -->

< /div>
< /template>

然后在computed计算属性的get和set方法里面进行state变量的状态管理。
export default
name:AllName1,
computed:
firstName:
get()
return this.$store.state.firstName
,
set(newVal)
this.$store.commit(handleFirstNameVal, newVal)


,

< /script>

在mutation是里面定义handleFirstNameVal,对firstName状态赋值。这种方式虽然较上一小节的实现麻烦了很多,但确实是标准的做法。这样做完之后firstName状态数据就可以正确的被devtools所跟踪。
mutations:
add(state)
state.count++
,
sub(state)
state.count--
,
add_num(state,num)
state.count= state.count+num
,
sub_num(state,num)
state.count= state.count-num
,
[COUNTER_ADD] (state,payload)
state.count = state.count + payload.num * payload.multiple
,
[COUNTER_SUB] (state,payload)
state.count = state.count - payload.num * payload.multiple
,
handleFirstNameVal(state,payload)
state.firstName = payload


,

vuex的getters的定义与使用
在上面的实现中,我们看到全名fullName的值是通过$store.state.firstName -$store.state.lastName 拼接而成的,在两个组件里面分别使用。还有一种方式就是将firstName 和 lastName先计算出fullName,然后在组件里面使用fullName,这种方法就是getters,在stroe/index.js中添加
getters:
fullName(state)

return state.firstName + "-" + state.lastName

,

下文代码显示效果和使用$store.state.firstName -$store.state.lastName 是一样的。
< template>
< div>
< h1> AllName1< /h1>
< label for="firstName"> firstName:< /label>
< input type="text" v-model="firstName" id="firstName">
< label for="lastName"> lastName:< /label>
< input type="text" v-model="$store.state.lastName" id="lastName">

< div> AllName1组件< /div>
< div> fullName:fullName< /div>
< !-- < div> fullName:fullName< /div> -->

< /div>
< /template>
< script>


export default
name:AllName1,
computed:
firstName:
get()
return this.$store.state.firstName
,
set(newVal)
this.$store.commit(handleFirstNameVal, newVal)

,
fullName()
return this.$store.getters.fullName

,

< /script>

vuex状态异步操作
Mutation中只能定义同步操作,异步操作交给Action。
Action基本用法
vuex应用状态管理和axios网络请求响应

文章图片

  • 在组件中使用dispatch触发异步操作Action(如网络请求backend API,后端服务API)
  • 在异步操作Action中对Mutation操作进行commit。
mutations:
add(state)
state.count++
,
sub(state)
state.count--
,
add_num(state,num)
state.count= state.count+num
,
sub_num(state,num)
state.count= state.count-num
,
[COUNTER_ADD] (state,payload)
state.count = state.count + payload.num * payload.multiple
,
[COUNTER_SUB] (state,payload)
state.count = state.count - payload.num * payload.multiple
,
handleFirstNameVal(state,payload)
state.firstName = payload
,

actionTest(state)
state.firstName = "actionTest"


,
actions:
submitAction(context)
setTimeout(() => //异步操作
//state数据的修改还是由mutation执行
context.commit(actionTest);
, 1000);


,

  • action的方法的参数context意为上下文,在我们还没有学习vuex的module之前可以暂且认为它是$store对象。我们后文再做详解。
  • 最后我们可以在组件中通过dispatch方法触发Action。这样的操作结果就是:state.firstName在异步操作中也可以被devtools跟踪状态。
< template>
< div>
< h1> AllName1< /h1>
< label for="firstName"> firstName:< /label>
< input type="text" v-model="firstName" id="firstName">
< label for="lastName"> lastName:< /label>
< input type="text" v-model="$store.state.lastName" id="lastName">

< button @click="$store.dispatch(submitAction)"> 触发异步操作< /button>

< div> AllName1组件< /div>
< div> fullName:fullName< /div>
< !-- < div> fullName:fullName< /div> -->

< /div>
< /template>
< script>


export default
name:AllName1,
computed:
firstName:
get()
return this.$store.state.firstName
,
set(newVal)
this.$store.commit(handleFirstNameVal, newVal)

,
fullName()
return this.$store.getters.fullName

,

< /script>

axios网络请求响应axios的简介及优势
基于Promise的,用在浏览器和nodeJS环境下的HTTP客户端。(Promise based HTTP client for the browser and node.js)
  • 支持从浏览器中创建XMLHttpRequests
  • 支持从 node.js 创建http请求(这个是其他框架不具备的)
  • 支持PromiseAPI(最重要特点)
  • 支持拦截器,拦截请求和响应(核心功能)
  • 支持转换请求数据和响应数据(核心功能)
  • 自动转换 JSON 数据(核心功能)
  • 客户端支持防御XSRF攻击
支持多种请求方法
  • request(config)
  • get(url[, config])
  • delete(url[, config])
  • head(url[, config])
  • post(url[, data[, config]])
  • put(url[, data[, config]])
  • patch(url[, data[, config]])
安装使用axios
npm安装方式:
npm install axios --save

axios发送GET请求
在使用axios发送HTTP请求之前,先向大家介绍一个网站:https://jsonplaceholder.typicode.com/,这个网站提供了免费的HTTP请求及相应服务,我们可以利用这个服务测试我们的axios。
< template>
< div>
< h1> ALLNAME< /h1>
< button @click="testaxios()"> testaxios< /button>
< all-name-1> < /all-name-1>
< all-name-2> < /all-name-2>
< /div>
< /template>

< script>
import allName1 from ./allName1.vue
import allName2 from ./allName2.vue
import axios from axios
export default
name:ALLNAME,
components:
allName1,
allName2
,
methods:
testaxios()
axios(
method:get,//http请求方法
url:"https://jsonplaceholder.typicode.com/posts/1"//http服务地址
)
.then( (response) =>
console.log(response);
).catch(function (error)
//请求异常响应结果
console.log(error);
);



< /script>

请求成功响应之后,浏览器打印结果如下:
vuex应用状态管理和axios网络请求响应

文章图片

  • 使用axios(config)发送http请求,config为该请求的配置信息对象
  • axios.get等同于axios使用method:get。
  • axios是基于Promise的HTTP客户端,所以可以使用then、catch对请求结果进行处理。
    axios发送POST请求
axios发送HTTP post请求的基本方法如下:
postaxios()

axios(
method:post,//http请求方法
url:"http://httpbin.org/post" , //http服务地址
data:
firstName: kobe,
lastName: byrant

)
.then( (response) =>
console.log(response);
).catch(function (error)
//请求异常响应结果
console.log(error);
);

在模板中添加一个按钮
< button @click="postaxios()"> postaxios< /button>

发送POST请求成功之后,浏览器的打印结果如下:
vuex应用状态管理和axios网络请求响应

文章图片

axios的配置详解
常用数据格式在实际的开发过程中,服务端接收数据的方式可能是多种数据格式的,比如:JSON、QueryString表单数据等形式。
JSON数据格式当data参数是JSON对象的时候,默认的Content-Type是application/json。所以下面代码中的headers的content-type设置可以省略。
postaxios()

axios(
method:post,//http请求方法
url:"http://httpbin.org/post" , //http服务地址
data:
firstName: kobe,
lastName: byrant
,

headers:"Content-Type":"application/json"

)
.then( (response) =>
console.log(response);
).catch(function (error)
//请求异常响应结果
console.log(error);
);


vuex应用状态管理和axios网络请求响应

文章图片

表单数据格式如果服务端不接收JSON对象数据,而是接收表单数据,那么我们该如何调整上面的代码?
  • 首先我们需要将JSON对象转换为QueryString字符串,使用qs.stringify()函数
  • 在项目中使用命令行工具输入:??npm install qs??
  • 安装完成后在需要用到的代码中:??import qs from qs??
  • qs.parse()是将URL解析成对象的形式,qs.stringify()是将对象 序列化成URL的形式,以& 进行拼接
  • 当data参数是字符串的时候,默认的Content-Type是application/x-www-form-urlencoded。所以下面代码中的headers的content-type设置可以省略。
postaxios()

axios.post(http://httpbin.org/post,
qs.stringify(
firstName: kobe,
lastName: byrant
),

headers:"content-type":"application/x-www-form-urlencoded"

).then(function (response)
console.log(response);
);

// axios(
//method:post,//http请求方法
//url:"http://httpbin.org/post" , //http服务地址
//data:
//firstName: kobe,
//lastName: byrant
//,

//headers:"Content-Type":"application/json"

// )
// .then( (response) =>
//console.log(response);
// ).catch(function (error)
////请求异常响应结果
//console.log(error);
// );

结果展示
vuex应用状态管理和axios网络请求响应

文章图片

数据的转换除了常用的表单数据格式、JSON数据格式,实际开发中还有很多的数据格式、内容需要进行处理。比如我们希望对发送出去的数据做特殊的自定义处理,axios也为我们提供了方法。
  • transfromRequest用于在向服务端发送请求之前,将数据做处理修改请求数据。该方法只对PUT、POST、PATCH请求生效。而且,此方法最后必须返回一个string、ArrayBuffer或者Stream。
  • transformResponse对响应结果数据,进行转换处理。即在收到响应之后,then/catch之前做数据转换处理工作。

transformRequest: [function (data)
// 在这里对 data 进行任意转换处理

return data;
],

// `transformResponse` 在传递给 then/catch 前,允许修改响应数据
transformResponse: [function (data)
// 在这里对 data 进行任意转换处理

return data;
]

axios请求配置axios最基本的请求方法GET、POST的使用,PUT、DELETE请求的方法和POST请求使用方法是基本一致的。如果没有指定??method???,请求将默认使用??get???方法。此外,??url??是必需的请求参数。下面为大家介绍其他的请求参数:

// `url` 是用于请求的服务器 URL(必填)
url: /user,

// `method` 是HTTP请求时使用的方法, 默认是 get
method: get,

// `baseURL` 将自动加在 `url` 前面,除非 `url` 是一个绝对 URL。
// 它可以通过设置一个 `baseURL` 便于为 axios 实例的方法传递相对 URL
baseURL: https://some-domain.com/api/,

// `headers` 是即将被发送的自定义请求头
headers: X-Requested-With: XMLHttpRequest,

// `params` 是即将与请求一起发送的 URL 参数
// 必须是一个无格式对象(plain object)或 URLSearchParams 对象
params:
ID: 12345
,

// `paramsSerializer` 是一个负责 `params` 序列化的函数。
//针对params的参数序列化通常axios自动完成,所以并不常用
paramsSerializer: function(params)
return qs.stringify(params, arrayFormat: brackets)
,

// `data` 是作为请求body被发送的数据,只适用于这些请求方法 PUT, POST, 和 PATCH
// 在没有设置 `transformRequest` 时,data数据必须是以下类型之一:
// - string, plain object, ArrayBuffer, ArrayBufferView, URLSearchParams
// - 浏览器专属:FormData, File, Blob
// - Node 专属:Stream
data:
firstName: Fred
,

// `timeout` 指定请求超时的毫秒数(0 表示无超时时间)
// 如果请求话费了超过 `timeout` 的时间,请求将被中断
timeout: 1000,

// `withCredentials` 表示跨域请求时是否需要使用凭证
withCredentials: false, // 默认的

// `adapter` 允许自定义处理请求,使用mock方式方便测试
// 返回一个 promise 并应用一个有效的响应
adapter: function (config)
/* ... */
,

// `auth` 表示应该使用 HTTP 基础验证,并提供凭据
// 这将设置一个 `Authorization` 头,可以对应Http Basic基础认证模式。
//如果是java开发人员,可以简单的学一下Spring Security的Http Basic基础认证模式,就会理解。
auth:
username: janedoe,
password: s00pers3cret
,

// `responseType` 表示服务器响应的数据类型,可以是 arraybuffer, blob, document, json, text, stream
responseType: json, // 默认的

//以下两个配置与跨站攻击伪造相关的防御,须与服务端名称保持一致。
// `xsrfCookieName` 是用作 xsrf token 的值的cookie的名称
xsrfCookieName: XSRF-TOKEN, // default

// `xsrfHeaderName` 是承载 xsrf token 的值的 HTTP 头的名称
xsrfHeaderName: X-XSRF-TOKEN, // 默认的

// `maxContentLength` 定义允许的响应内容的最大尺寸
maxContentLength: 2000,

// `validateStatus` 定义对于给定的HTTP 响应状态码是 resolve 或 rejectpromise 。如果 `validateStatus` 返回 `true` (或者设置为 `null` 或 `undefined`),promise 将被 resolve; 否则,promise 将被 rejecte
validateStatus: function (status)
return status > = 200 & & status < 300; // 默认的
,

// `maxRedirects` 定义在 node.js 中 follow 的最大重定向数目
// 如果设置为0,将不会 follow 任何重定向
maxRedirects: 5, // 默认的

// `httpAgent` 和 `httpsAgent` 分别在 node.js 中用于定义在执行 http 和 https 时使用的自定义代理。允许像这样配置选项:
// `keepAlive` 默认没有启用
httpAgent: new http.Agent( keepAlive: true ),
httpsAgent: new https.Agent( keepAlive: true ),

// proxy 定义代理服务器的主机名称和端口
// `auth` 表示 HTTP 基础验证应当用于连接代理,并提供凭据
// 这将会设置一个 `Proxy-Authorization` 头,覆写掉已有的通过使用 `header` 设置的自定义 `Proxy-Authorization` 头。
proxy:
host: 127.0.0.1,
port: 9000,
auth: :
username: mikeymike,
password: rapunz3l

,

// `cancelToken` 指定用于取消请求的 cancel token
cancelToken: new CancelToken(function (cancel)
)

axios响应数据结构某个请求的响应包含以下信息

// `data` 由服务器提供的响应
data: ,

// `status` 来自服务器响应的 HTTP 状态码
status: 200,

// `statusText` 来自服务器响应的 HTTP 状态信息
statusText: OK,

// `headers` 服务器响应的头
headers: ,

// `config` 是为请求提供的配置信息
config:



如果您觉得本文不错,欢迎关注,点赞,收藏支持,您的关注是我坚持的动力!
原创不易,转载请注明出处,感谢支持!如果本文对您有用,欢迎转发分享!



    推荐阅读