vuex应用状态管理和axios网络请求响应
Posted springboot葵花宝典
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了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>
实现效果如下:
两个组件的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也发生变化
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>
最后的显示结果如下:
- 当我们在第一个组件里面输入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跟踪
从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基本用法
- 在组件中使用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>
请求成功响应之后,浏览器打印结果如下:
- 使用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请求成功之后,浏览器的打印结果如下:
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);
);
表单数据格式
如果服务端不接收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);
// );
结果展示
数据的转换
除了常用的表单数据格式、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 或 reject promise 。如果 `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:
如果您觉得本文不错,欢迎关注,点赞,收藏支持,您的关注是我坚持的动力!
原创不易,转载请注明出处,感谢支持!如果本文对您有用,欢迎转发分享!
以上是关于vuex应用状态管理和axios网络请求响应的主要内容,如果未能解决你的问题,请参考以下文章