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. c# API接收Base64转图片

    /// <summary> /// API接收Base64转图片 /// </summary> /// <param name="Img">图片 ...

  2. [PHP] 工厂模式的日常使用

    负责生成其他对象的类或方法,这就是工厂模式,下面是一个经常见到的用法 <?php class test{ public $x=1; public $setting; //负责生成其他对象的类或方 ...

  3. json 按照字段分类

    let arr = [ { Category:'A', Amount:, },{ Category:'B', Amount:, },{ Category:'A', Amount:, },{ Categ ...

  4. 前端js 实现文件下载

    https://www.zhangxinxu.com/wordpress/2017/07/js-text-string-download-as-html-json-file/ 侵删 1.H5 down ...

  5. 浅析 JavaScript 中的 函数 uncurrying 反柯里化

    柯里化 柯里化又称部分求值,其含义是给函数分步传递参数,每次传递参数后部分应用参数,并返回一个更具体的函数接受剩下的参数,这中间可嵌套多层这样的接受部分参数函数,直至返回最后结果. 因此柯里化的过程是 ...

  6. splay详解(二)

    前言 在上一节中,我们讲述了Splay的核心操作rotate与splay 本节我会教大家如何用这两个函数实现各种强大的功能 为了方便讲解,我们拿这道题做例题来慢慢分析 利用splay实现各种功能 首先 ...

  7. 在Dynamics 365中使用SURVEYJS代替对话(Dialog)制作话术

    本人微信和易信公众号: 微软动态CRM专家罗勇 ,回复269或者20180318可方便获取本文,同时可以在第一间得到我发布的最新的博文信息,follow me!我的网站是 www.luoyong.me ...

  8. CentOS7 设置yum源

    1.关闭防火墙 临时关闭防火墙 systemctl stop firewalld 永久防火墙开机自关闭 systemctl disable firewalld 临时打开防火墙 systemctl st ...

  9. Ubuntu 18.04 安装博通(Broadcom)无线网卡驱动

    目录 Ubuntu 18.04 安装博通(Broadcom)无线网卡驱动 Package gcc is not configured yet. 解决办法 history history | grep ...

  10. Linux LVM学习总结——Insufficient Free Extents for a Logical Volume

    如下所示,在创建LV的时候,偶尔会遇到"Volume group "xxxx" has insufficient free space (xxxx extents): x ...