Vue之状态管理(vuex)与接口调用

一,介绍与需求

1.1,介绍

1,状态管理(vuex)

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

状态管理核心

  1. state里面就是存放项目中需要多组件共享的状态
  2. mutations就是存放更改state里状态的方法
  3. getters就是从state中派生出状态,比如将state中的某个状态进行过滤然后获取新的状态。
  4. actions就是mutation的加强版,它可以通过commit mutations中的方法来改变状态,最重要的是它可以进行异步操作
  5. modules顾名思义,就是当用这个容器来装这些状态还是显得混乱的时候,我们就可以把容器分成几块,把状态和管理规则分类来装。这和我们创建js模块是一个目的,让代码结构更清晰。

2,axios

axios 是一个基于Promise 用于浏览器和 nodejs 的 HTTP 客户端,它本身具有以下特征:

  • 从浏览器中创建 XMLHttpRequest
  • 从 node.js 发出 http 请求
  • 支持 Promise API
  • 拦截请求和响应
  • 转换请求和响应数据
  • 取消请求
  • 自动转换JSON数据
  • 客户端支持防止 CSRF/XSRF

axios并没有install 方法,所以是不能使用vue.use()方法的。

解决方法有很多种:

  • .结合 vue-axios使用
  • axios 改写为 Vue 的原型属性

使用 vue-axios

在主入口文件main.js中引用

 import axios from 'axios'
import VueAxios from 'vue-axios'
Vue.use(VueAxios,axios);

axios 改写为 Vue 的原型属性

在主入口文件main.js中引用

  import axios from 'axios'
Vue.prototype.$axios= axios;

3,vue-resource

vue-resource是Vue.js的一款插件,它可以通过XMLHttpRequest或JSONP发起请求并处理响应。

vue-resource还提供了非常有用的inteceptor功能,使用inteceptor可以在请求前和请求后附加一些行为,比如使用inteceptor在ajax请求时显示loading界面。

vue-resource的请求API是按照REST风格设计的,它提供了7种请求API:

  • get(url, [options])
  • head(url, [options])
  • delete(url, [options])
  • jsonp(url, [options])
  • post(url, [body], [options])
  • put(url, [body], [options])
  • patch(url, [body], [options])

vue-resource不再继续维护,推荐大家使用 axios 。

1.2,需求

如果数据还有其他组件复用,可放在vuex
如果需要跨多级组件传递数据,可放在vuex
需要持久化的数据(如登录后用户的信息),可放在vuex
跟当前业务组件强相关的数据,可以放在组件内

二,状态数据管理

vue项目搭建与部署

第一步:在开发环境下安装vuex

 cnpm install vuex --save-dev

第二步:引用vuex,并实例化vuex状态库

建立一个store文件夹,建立一个index.js。在index.js中引入vue和vuex,日志等

 import Vue from 'vue'
import Vuex from 'vuex' //每次修改state都会在控制台打印log
import createLogger from 'vuex/dist/logger'
Vue.use(Vuex) export default new Vuex.Store({
actions:{},
getters:{},
state:{},
mutations:{},
})

第三步:store文件夹下创建state.js文件

这是我们初始化数据的 ,也是之后我们存数据的地方

 const state = {
userInfo: {},
MenuList: {}
}
export default state;

第四步:store文件夹下创建mutations.js文件

提交 mutations 是更改 vuex中 store 中状态的 唯一方法

 import * as types from './mutation-types'
import roleTokencate from "../caches/roleTokencate"; const mutations = {
/*
* 登录
*/
[types.SET_USERINFO](state, userInfo) {
console.log(types.SET_USERINFO, userInfo)
roleTokencate(userInfo); //存入缓存 state.userInfo = userInfo
},
/*
* 获取菜单列表
*/
[types.SET_MENULIST](state, data={}) {
state.MenuList = data
}
} export default mutations
创建mutation-types.js文件,主要类型区分
 export const SET_USERINFO = "userInfo";//登录返回的用户信息
export const SET_MENULIST = "MenuList";//返回的菜单列表

第五步:store文件夹下创建getters.js文件

vue 的计算属性

 export const userInfo = state => state.userInfo
export const MenuList = state => state.MenuList

第六步:store文件夹下创建actions.js文件

Action 提交的是 mutation,而不是直接变更状态。 Action 可以包含任意异步操作。

 import * as types from './mutation-types'
import { getCurrUserMenu } from "./../services/auth"; /**
* 登录 获取用户信息
* @param context:与 store 实例具有相同方法和属性的 context 对象
* @param Object:需管理的数据
*/
export function getUserInfoSync (context,Object) {//2.接受dispatch传递过来的方法和参数
//处理异步操作
setTimeout(()=>{
//3.通过commit提交一个名为getParam的mutation
//action 函数接受一个与 store 实例具有相同方法和属性的 context 对象,因此你可以调用 context.commit 提交一个 mutation
context.commit(types.SET_USERINFO,Object)
},1000)
}; /**
* 获取菜单列表
* @param context:与 store 实例具有相同方法和属性的 context 对象
* @param Object:需管理的数据
*/
export function getMenuListSync (context,Object) {
context.commit(types.SET_MENULIST,Object)
}

第七步:store文件夹下创建modules文件夹

对应模块js文件中,这里我使用的home.js文件中编写state、actions和mutations ,getters 等

vuex自带模块化方法,为namespaced:true。通过对模块进行命名空间设置,就能分模块进行管理。

 const state = {
initInfo: 'hello jackson'
}
const getters = {
initInfo(state, getters) {
return state.initInfo
}
}
const actions = {
getInfo({commit, state},data) {
console.log('getInfo==',data)
commit('updateInitInfo', 'getInfo')
}
}
const mutations = {
updateInitInfo(state, string) {
state.initInfo = string
console.log('home update', string)
}
} export default {
namespaced: true,
state,
getters,
actions,
mutations
}

第八步:在开发环境下,开启严格模式

引入日志打印:

  //每次修改state都会在控制台打印log
import createLogger from 'vuex/dist/logger'

判断是否是开发环境

 const debug = process.env.NODE_ENV !== 'production'

添加到Vuex.Store库中

 export default new Vuex.Store({

  ...

     strict: debug, // 当debug=true时开启严格模式(性能有损耗)
plugins: debug ? [createLogger()] : []
})

第九步:将上面创建的文件与方法,引入的第二步创建的store入口文件index.js中

 import Vue from 'vue'
import Vuex from 'vuex'
import * as actions from './actions'
import * as getters from './getters'
import state from './state'
import mutations from './mutations'
import home from './modules/home' //每次修改state都会在控制台打印log
import createLogger from 'vuex/dist/logger'
Vue.use(Vuex) const debug = process.env.NODE_ENV !== 'production' export default new Vuex.Store({
actions,
getters,
state,
mutations,
modules: {
home,
},
strict: debug, // 当debug=true时开启严格模式(性能有损耗)
plugins: debug ? [createLogger()] : []
})

第十步:在项目的入口文件main.js中注册使用

 import 'amfe-flexible'
// The Vue build version to load with the `import` command
// (runtime-only or standalone) has been set in webpack.base.conf with an alias.
import Vue from 'vue'
// By default we import all the components.
// Only reserve the components on demand and remove the rest.
// Style is always required.
import VueResource from 'vue-resource' import App from './App'
import router from '../router'
import store from '../store'
Vue.config.productionTip = false
Vue.use(VueResource)
/* eslint-disable no-new */
new Vue({
el: '#app',
router,
store,
template: '<App/>',
components: { App }
})

第十一步:在xxx.vue中使用

1,设置数据

(1),调用actions中的方法

  this.$store.dispatch('getUserInfoSync',res.data.data[0])

(2),调用mutations中的方法

引入mapMutations

 import { mapMutations } from "vuex";

使用

 import { mapMutations } from "vuex";
export default {
name: "Login",
data() {
return {
loginCode: undefined,
password: undefined,
isEye: true,
isChecked: true
};
},
mounted() { },
methods: { // 登陆
loginAction() {
let loginData = {
loginCode: this.$data.loginCode,
password: this.$data.password
};
if (this.$data.isChecked) {
loginRememberCate(loginData);
}
//不简写login
this.$http(login(loginData))
//es6写法 .then()部分
.then(res => {
console.log(res.data);
if (res.data.httpCode === 200) {
if (res.data.data && res.data.data.length > 0) {
this.setUserInfo(res.data.data[0]);
}
}
})
.catch(err => {
console.log("错误信息==", err.data);
});
},
...mapMutations({
setUserInfo: "SET_USERINFO"
})
}
};

2,获取数据

引入mapGetters

 import {mapGetters} from 'vuex'

使用

  computed:{
...mapGetters([
'userInfo','MenuList'
])
},
mounted() {
console.log('this.userInfo==',this.userInfo);
console.log('this.MenuList==',this.MenuList);
},

三,接口调用

3.1,axios访问接口

第一步:安装axios

 cnpm install axios --save

第二步:引入axios并封装

QS是axios库中带的,不需要我们再npm安装一个

 import axios from 'axios'
import QueryString from 'qs'; function checkHttpStatus(response) {
if (response.status >= 200 && response.status < 300) {
return response;
}
const error = new Error(response.statusText);
error.response = response;
error.code = response.status;
throw error;
} function getResult(json) {
if (json.status === 200) {
let result = { result: json.data.data };
return result;
}
}
/**
* 通用配置
* @param url:接口地址
* @param options:配置
* @param return{*}
*/
function request(url = '', options = {}, cache) {
// debugger
console.info('request ' + url);
let data;
let contentType;
if (typeof cache === 'function') {
data = cache();
if (data) {
return Promise.resolve(data);
}
}
data = options.data;
delete options.data;
contentType = options.contentType;
delete options.contentType;
const opts = {
method: 'POST',
url,
...options
};
opts.headers = {
...opts.headers,
};
if (opts.method === 'GET') {
url = url.split('?');
url = url[0] + '?' + QueryString.stringify(url[1] ? { ...QueryString.parse(url[1]), ...data } : data);
opts.headers['content-type'] = contentType ? contentType : 'application/json'; //
} else {
opts.headers['content-type'] = contentType ? contentType : 'application/json'; //
opts.data= contentType === 'application/x-www-form-urlencoded' ? serialize(data) : JSON.stringify(data);
}
// 支持处理缓存
const handleCache = data => {
typeof cache === 'function' && cache(data.result);
return data;
}; return axios(opts)
.then(checkHttpStatus)
.then(getResult)
.then(handleCache)
.catch(err => ({ err }));
}
export default request;

第三步:使用axios

 import requestAxios from './requestAxios';
import { POST, PUT } from '../utils/const'; /*
***获取可访问菜单***
*/
export function getCurrUserMenu(data) {
return requestAxios('/api/v1/yingqi/user/getCurrUserMenu', { data, method: PUT });
}

在store中actions中调用接口

 import { getCurrUserMenu } from "./../services/auth";

 export function getMenuListAxiosSync (context,payload) {
getCurrUserMenu(payload.data)
.then(action => {
// alert('action中调用封装后的axios成功');
payload.getResult(action.result)
console.log('action中调用封装后的axios成功',action.result)
context.commit(types.SET_MENULIST, action.result)
})
}

3.2,vue-resource访问接口

第一步:安装vue-resource

 cnpm install vue-resource --save

第二步:在入口文件中引入使用

 import VueResource from 'vue-resource'
Vue.use(VueResource)

封装共同访问参数

 /**
* 通用配置
* @param url:接口地址
* @param options:配置
* @param return{*}
*/
export function request(url, options) {
// // post 传参数 需要加 {emulateJSON:true}
// this.$http.post('in.php',{a:1,b:2},{emulateJSON:true}).then( (res) => {
// console.log(res.data)
// } ) // // get传参数 需要 {params: {你传的值}}
// this.$http.get('getin.php',{params: {a:1,b:2}}).then( (res) => {
// console.log(res.data)
// }) // // jsonp 传参数
// this.$http.jsonp("https://sug.so.360.cn/suggest",{params:{word:'a'}}).then( (res)=>{
// console.log(res.data.s)
// })
let data;
let contentType;
data = options.data;
delete options.data;
contentType = options.contentType;
delete options.contentType;
const opts = {
method: 'POST',
url,
emulateJSON: true,
...options
};
opts.headers = {
...opts.headers,
};
opts.headers['content-type'] = contentType ? contentType : 'application/json'; //
opts.body = contentType === 'application/x-www-form-urlencoded' ? serialize(data) : JSON.stringify(data); return opts;
}
export default request;

第三步:使用

 import request from './request';//request
import { POST, PUT } from '../utils/const';
/*
***登陆***
*/
export function login(data) {
return request('/api/v1/yingqi/user/login', { data, method: POST });
}

在xxx.vue中调用接口

 import { login } from "../../services/auth";
... this.$http(login(loginData))
//es6写法 .then()部分
.then(res => {
console.log(res.data);
if (res.data.httpCode === 200) {
if (res.data.data && res.data.data.length > 0) {
console.log("res.data.data", res.data.data[0]);
}
}
})
.catch(err => {
console.log("错误信息==", err.data);
}); ...

3.3,axios拦截器interceptors访问接口

第一步:创建axios实例

 const service = axios.create({
baseURL: process.env.VUE_APP_BASE_API, // url = base url + request url
withCredentials: true, // send cookies when cross-domain requests
timeout: 50000 // request timeout
})

第二步:请求拦截器

 service.interceptors.request.use(
config => {
// 发送请求之前做配置 一般配置token
//config.headers['X-Token'] = getToken()
return config
},
error => {
// 请求异常
console.log(error) // for debug
return Promise.reject(error)
}
)

第三步:返回拦截器

 service.interceptors.response.use(
/**
* 如果您想获得诸如头信息或状态信息
* Please return response => response
*/ response => {
const res = response.data
if (res.code !== 0) {//状态码错误/异常时处理
return Promise.reject(res.message || 'error')
} else {//请求成功返回
return res
}
},
error => {//接口返回异常
console.log('err' + error) // for debug
return Promise.reject(error)
}
)

第四步:调用封装

 export function login(data) {
return request({
url: '/api/v1/yingqi/user/login',
method: 'post',
data: {
loginCode: data.loginCode,
password: data.password
}
})
}

第五步:在状态管理里调用

 const actions = {
// user login
login({ commit }, userInfo) {
const { loginCode, password } = userInfo
return new Promise((resolve, reject) => {
login({ loginCode: loginCode.trim(), password: password }).then(response => {
const { data } = response
commit('SET_TOKEN', data.token)
resolve()
}).catch(error => {
reject(error)
})
})
},
}

第六步:在页面调用actions

  handleLogin() {
// this.loginForm= {loginCode: 'loginCode', password: '123456'},
this.$refs.loginForm.validate(valid => {
if (valid) {
this.loading = true
this.$store.dispatch('user/login', this.loginForm)
.then(() => {
//路由跳转 this.loading = false
})
.catch(() => {
this.loading = false
})
} else {
console.log('error submit!!')
return false
}
})
}

四,常见问题

1,刷新后,在vuex中的数据会丢失

vuex 刷新 数据丢失问题

解决思路: localStorage 本地存储

解决办法:

mutations.js 里面存数据,不用每个组件都存一次

 import * as types from './mutation-types'
import roleTokencate from "../caches/roleTokencate";
import commonCache from "../caches/commonCache"; const mutations = {
/*
* 登录
*/
[types.SET_USERINFO](state, userInfo) {
console.log(types.SET_USERINFO, userInfo)
roleTokencate(userInfo); //存入缓存
commonCache(types.SET_USERINFO,userInfo); //存入缓存 防止数据丢失
state.userInfo = userInfo
},
} export default mutations

在state.js 里面 加入以下代码 :

 import commonCache from "../caches/commonCache";

 ...

   for (var item in state) {
6 let getCacheData = commonCache(item);//从缓存中获取数据
7 getCacheData ? (state[item] = typeof getCacheData ==='string'?JSON.parse(getCacheData):getCacheData) : false;//防止页面刷新vuex中的数据丢失
8 }

2,axios请求https后台接口时,总是走error

axios开发环境配置代理请求https后台接口时,如果是ip地址,例如thinkjs后台接口地址https:127.0.0.1:8080,就会总是走error,无法正常获取后台接口的返回值;但是如果是域名的话,则无这种情况。

解决思路:target默认情况下,不接受运行在HTTPS上,且使用了无效证书的后端服务器。如果你想要接受, 则需设置secure为false;

解决办法:在配置代理proxyTable里添加如下属性

  // 是否验证SSL证书
secure: false,

完整的配置如下:

  proxyTable: {
"/api": {
// 传递给http(s)请求的对象
target: "http://127.0.0.1:8880",
// 是否将主机头的源更改为目标URL
changeOrigin: true,
// 是否代理websocket
ws: true,
// 是否验证SSL证书
secure: false,
// 重写set-cookie标头的域,删除域名
cookieDomainRewrite: '',
// 代理响应事件
//onProxyRes: onProxyRes,
// 重写目标的url路径
pathRewrite: {
'^/api' : '/api'
}
}
代理响应事件
 /**
* 过滤cookie path,解决同域下不同path,cookie无法访问问题
* (实际上不同域的cookie也共享了)
* @param proxyRes
* @param req
* @param res
*/
function onProxyRes (proxyRes, req, res) {
let cookies = proxyRes.headers['set-cookie']
// 目标路径
let originalUrl = req.originalUrl
// 代理路径名
let proxyName = originalUrl.split('/')[1] || ''
// 开发服url
let server = configDev.servers[proxyName]
// 后台工程名
let projectName = server.substring(server.lastIndexOf('/') + 1)
// 修改cookie Path
if (cookies) {
let newCookie = cookies.map(function (cookie) {
if (cookie.indexOf(`Path=/${projectName}`) >= 0) {
cookie = cookie.replace(`Path=/${projectName}`, 'Path=/')
return cookie.replace(`Path=//`, 'Path=/')
}
return cookie
})
// 修改cookie path
delete proxyRes.headers['set-cookie']
proxyRes.headers['set-cookie'] = newCookie
}
}
 secure: false,  // 如果是https接口,需要配置这个参数

如需完整代码,请先留言评论加关注

Vue之状态管理(vuex)与接口调用的更多相关文章

  1. vue创建状态管理(vuex的store机制)

    1:为什么说要是永远状态管理 在使用 Vue 框架做单页面应用时,我们时常会遇到传值,组件公用状态的问题.(子父间传值文章传送门) ,如果是简单的应用,兄弟组件之间通信还能使用 eventBus 来作 ...

  2. vue中状态管理vuex的使用分享

    一.main.js中引入 store import store from './store' window.HMO_APP = new Vue({ router, store, render: h = ...

  3. Vue状态管理vuex

    前面的话 由于多个状态分散的跨越在许多组件和交互间各个角落,大型应用复杂度也经常逐渐增长.为了解决这个问题,Vue提供了vuex.本文将详细介绍Vue状态管理vuex 引入 当访问数据对象时,一个 V ...

  4. 状态管理Vuex

    路由Router 配置 {path:'/login',component:Login} 路由出口 router-view 传参 {path:'/login/:id',component:Login} ...

  5. Vue.js 2.x笔记:状态管理Vuex(7)

    1. Vuex简介与安装 1.1 Vuex简介 Vuex是为vue.js应用程序开发的状态管理模式,解决的问题: ◊ 组件之间的传参,多层嵌套组件之间的传参以及各组件之间耦合度过高问题 ◊ 不同状态中 ...

  6. 转 理解vuex -- vue的状态管理模式

    转自:https://segmentfault.com/a/1190000012015742 vuex是什么? 先引用vuex官网的话: Vuex 是一个专为 Vue.js 应用程序开发的状态管理模式 ...

  7. vuex -- vue的状态管理模式

    Vuex 是一个专为 Vue.js 应用程序开发的状态管理模式.它采用集中式存储管理应用的所有组件的状态,并以相应的规则保证状态以一种可预测的方式发生变化. 状态管理模式.集中式存储管理 一听就很高大 ...

  8. Vue.js状态管理模式 Vuex

    vuex 是一个专为 Vue.js 应用程序开发的状态管理模式.它采用集中式存储管理应用的所有组件的状态,并以相应的规则保证状态以一种可预测的方式发生变化. 安装.使用 vuex 首先我们在 vue. ...

  9. vue状态管理vuex从浅入深详细讲解

    1.vuex简介以及创建一个简单的仓库 vuex是专门为vue框架而设计出的一个公共数据管理框架,任何组件都可以通过状态管理仓库数据沟通,也可以统一从仓库获取数据,在比较大型的应用中,数据交互庞大的情 ...

随机推荐

  1. layui_表格数据查询按钮

    2018-4月末入职,开始接触layui,到2018-8对这个前端框架有了一个基础的了解. 文档地址:https://www.layui.com/. 按钮,是任何网页中的重要组成部分,以下内容是我在制 ...

  2. 微服务定义及.Net Core中用的技术

    微服务 定义: 它是一种架构模式,提倡将大的单体系统,按业务拆分成一个个较小且独立的服务,服务与服务之前进行相互协作和配合. 历史: 针对互联网行业的蓬勃发展,需要支撑的业务越来越多,越来越大,单体程 ...

  3. 由AbstractQueuedSynchronizer和ReentrantLock来看模版方法模式

    在学完volatile和CAS之后,近几天在撸AbstractQueuedSynchronizer(AQS)的源代码,很多并发工具都是基于AQS来实现的,这也是并发专家Doug Lea的初衷,通过写一 ...

  4. DBA思考系列——凛冬将至,丧钟为谁而鸣!

    诸多迹象昭示着凛冬将至,大萧条终于正式在全国各地拉开了序幕,很多80后的国人没有经历过苦日子,也没有经历过真正的金融危机.这场经济危机必将摧毁一些无视经济能力,盲目购房,盲目消费的家庭或个人.个人对经 ...

  5. 获取spring security用户相关信息

    在JSP中获得 使用spring security的标签库 在页面中引入标签 <%@ taglib prefix="sec" uri="http://www.spr ...

  6. phpstorm ftp主动模式能连接上,但获取不到目录;

    前面一直都在使用ST做开发,但是也想试试传说中的phpstorm神器.一切都弄好了,想使用它的远程开发功能,省去我本地开发然后再ftp上传做法. 但是却遇到了这个问题,困扰了我三四天!!!我各种百度都 ...

  7. 在Linux系统中同步更新我们的Github博客

    原理介绍 类似于版本管理,我们把我们的hexo博客文件系统在Github上建立一个分支,通过管理分支提交最新的博客文件系统,保证我们博客框架的更新.然后我们基于最新的博客框架,撰写文章,进行Githu ...

  8. 2017 百度杯丶二月场第一周WP

    1.祸起北荒 题目: 亿万年前 天子之子华夜,被父神之神末渊上神告知六荒十海之北荒西二旗即将发生一场"百度杯"的诸神之战 他作为天族的太子必须参与到此次诸神之战定六荒十海 华夜临危 ...

  9. 面向对象_内置函数 property

    property 将方法伪装成为属性,可以不用加上()就可以调出其属性. 但是用__dict__,不能调出此属性 from math import pi class Circle: def __ini ...

  10. Redis操作hash

    来自:http://www.cnblogs.com/alex3714/articles/6217453.html Hash操作 hash表现形式上有些像pyhton中的dict,可以存储一组关联性较强 ...