Vue router, axios interceptors, Vuex Store.

在Vue2.0中的路由钩子主要是用来拦截导航,让它完成跳转或前取消,可以理解为路由守卫。
分为全局导航钩子,单个路由独享的钩子,组件内钩子。
三种 类型的钩子只是用的地方不一样,都接受一个函数作为参数,函数传入三个参数,分别为to,from,next。
其中next有三个方法
(1)next(); //默认路由
(2)next(false); //阻止路由跳转
(3)next({path:’/’}); //阻止默认路由,跳转到指定路径

这里我使用了组件内钩子进行判断token过期后跳转到登录页,其他两种钩子可以去官网查看。
//路由前验证
beforeRouteEnter(to, from, next)
实现方法很简单,在路由前向服务器发送请求,如果返回的数据表明token过期则阻止默认跳转,否则就正常跳转。

注意:我们前端一般部署在另外的服务器上,会跨域,后端要处理跨域的问题,在PHP中可以写上如下代码:
//指定允许其他域名访问
header(“Access-Control-Allow-Origin: *”);
header(“Access-Control-Allow-Methods: GET,POST”);
header(‘Access-Control-Allow-Headers: X-Requested-With,content-type,if-modified-since’);

/*从localStorage中取出token过期时间*/
let expiredTime = new Date(JSON.parse(localStorage.auth).expired_at).getTime() / 1000
/*获取本地时间*/
let nowTime = new Date().getTime() / 1000

//request拦截器
instance.interceptors.request.use(
config => {
//每次发送请求之前检测都vuex存有token,那么都要放在请求头发送给服务器
if(store.state.token){
config.headers.Authorization = `token ${store.state.token}`;
}
return config;
},
err => {
return Promise.reject(err);
}
);
//respone拦截器
instance.interceptors.response.use(
response => {
return response;
},
error => { //默认除了2XX之外的都是错误的,就会走这里
if(error.response){
switch(error.response.status){
case 401:
store.dispatch(‘UserLogout’); //可能是token过期,清除它
router.replace({ //跳转到登录页面
path: ‘login’,
query: { redirect: router.currentRoute.fullPath } // 将跳转的路由path作为参数,登录成功后跳转到该路由
});
}
}
return Promise.reject(error.response);
}
);

export default instance;

getters: {
personInfo(state) {
return `My name is ${state.name}, I am ${state.age}`;
}
}
computed:
return this.$store.getters.personInfo;
Note: Vuex的函数要通过组件中的computed or methods才能使用,或者map到本地函数名。

axios client:
‘Authorization’: `token ${store.getToken}`,
=>
‘Authorization’: `token ${store.getters.getToken}`,

router server:
if(req.header[‘Authorization’]){
let token = req.header[‘Authorization’].split(‘ ‘)[1];
=>
req.header[‘Authorization’] => req.headers[‘authorization’]
Note: html大小写不敏感,js中操作取小写。

//检查post的信息或者url查询参数或者头信息
var token = req.body.token || req.query.token || req.headers[‘x-access-token’];

vue绑定值与字符串拼接两种写法
<cell :title=”`当前门店:${item.Storename}`” link=”/component/radio” :inline-desc=”‘门店地址:’ + item.Storeaddess”></cell>
第一种:title=”`字符串${xx}`”
第二种:title=”‘字符串’ + xx”

methods: {
…mapMutations([
‘increment’ // 映射 this.increment() 为 this.$store.commit(‘increment’)
]),
…mapMutations({
add: ‘increment’ // 映射 this.add() 为 this.$store.commit(‘increment’)
})
}

**使用 this.$store.commit()时** 两种格式都能提交附加的数据
方法一:
store.commit(‘increment’, {
amount: 10
})
方法二:
store.commit({
type: ‘increment’,
amount: 10
})

axios中的console是在浏览器里面,node的console是命令终端。

The Route Object
A route object represents the state of the current active route. It contains parsed information of the current URL
and the route records matched by the URL.
The route object is immutable. Every successful navigation will result in a fresh route object.
The route object can be found in multiple places:
Inside components as this.$route
Inside $route watcher callbacks
As the return value of calling router.match(location)
Inside navigation guards as the first two arguments:
router.beforeEach((to, from, next) => {
// `to` and `from` are both route objects
})
Inside the scrollBehavior function as the first two arguments:
const router = new VueRouter({
scrollBehavior (to, from, savedPosition) {
// `to` and `from` are both route objects
}
})

router vs route:
if (this.$route.query.redirect) {
this.$router.push({
name: this.$route.query.redirect
})
} else {
this.$router.push({
name: ‘NewPage’
})
}

‘Authorization’: `token ${store.getters.getToken}`,
==
‘Authorization’: ‘token ‘+ store.getters.getToken,

utils.checkTokenOnRouter = function (req, res, next) {
//console.log(‘Time: ‘, Date.now())
if(req.headers[‘authorization’]){
let token = req.headers[‘authorization’].split(‘ ‘)[1];
console.log(token);
if(token && false == utils.isTokenExpired(token)){
next();
}else{
res.send({ result: false, code: 402, message: ‘Invalid token!’});
}
}else{
res.send({ result: false, code: 402, message: ‘Invalid token!’});
}
}

Vue router:
router.beforeEach((to, from, next) => {
iView.LoadingBar.start();
Util.title(to.meta.title);
if (to.meta.requireAuth) {
if (store.getters.getToken != “”) {
next();
}
else {
next({
name: ‘login’,
query: { redirect: to.name }
})
}
}
else {
next();
}
})

AJAX use axios interceptors request/response:
axios.interceptors.request.use(function(config) {
if (store.getters.getToken) {
config.headers.Authorization = ‘token ‘+ store.getters.getToken;
}
return config;
}, function(error){
return Promise.reject(error);
});

axios.interceptors.response.use(function(res) {
//console.log(res.data);
return res;
}, function(err){
if (err.response.status) {
//console.log(“code ” + err.response.status);
switch(err.response.status){
case 401:
console.log(‘redirect to login page’);
//store.commit(‘SET_TOKEN’, ”);
store.dispatch(‘setToken’, ”);
//console.log(store.getters.getToken);
router.replace({
name: ‘login’,
query: { redirect: router.currentRoute.name }
});
}
}
return Promise.reject(err);
}
);

const moduleA = {
state: {
maState: ‘A’
}
};

const moduleB = {
state: {
mbState: ‘B’
}
};

const store = new Vuex.Store({
modules: {
a: moduleA,
b: moduleB
},
state: {
rtState: ‘Root’
}
});

console.log(store.state.a.maState); // A
console.log(store.state.b.mbState); // B
console.log(store.state.rtState); // Root

在 types.js 内定义 常量 并导出,默认全部大写
// 定义类型常量,默认全部大写
const INCREMENT = ‘INCREMENT’
const DECREMENT = ‘DECREMENT’
export default {
INCREMENT,
DECREMENT
}

// 导入 types.js 文件
import types from “./../types”;

const mutations ={
// 此处的事件为上方 actions 中的 commit(types.INCREMENT)
[types.INCREMENT](state){
state.count++
},
// 此处的事件为上方 actions 中的 commit(types.DECREMENT)
[types.DECREMENT](state){
state.count–
}
}
// 最后统一导出
export default {
state,
getters,
actions,
mutations
}
注意:上方 mutations 中的 [types.INCREMENT] 写法,因为 types.INCREMENT 是一个对象,所以不能直接当做一个函数名来写,需要用到 ES2015 风格的计算属性命名功能来使用一个常量作为函数名,方能正常使用,原来的写法为:
const mutations ={
increment(state){
state.count ++;
}
}

// 异步操作中需要用到 increment 方法,所以需要导入 types.js 文件
import types from ‘./types’

const actions= {
incrementAsync({ commit, state }) {
// 异步操作
var p = new Promise((resolve, reject) => {
setTimeout(() => {
resolve()
}, 3000);
});
p.then(() => {
commit(types.INCREMENT);
}).catch(() => {
console.log(‘异步操作’);
})
}
}
// 最后导出
export default actions;

Getters
与 state 不同的是,不同模块的 getters 会直接合并在 store.getters 下
console.log(store.getters.maGetter); // 3
console.log(store.getters.mbGetter); // Hello Vuex
getters 的回调函数所接收的前两个参数,模块化后需要用到第三个参数——rootState。参数: 1. state,模块中的 state 仅为模块自身中的 state;2. getters,等同于 store.getters;3. rootState,全局 state。
通过 rootState,模块中的 getters 就可以引用别的模块中的 state 了,十分方便。
注意:由于 getters 不区分模块,所以不同模块中的 getters 如果重名,Vuex 会报出 ‘duplicate getter key: [重复的getter名]’ 错误。
Mutations
mutations 与 getters 类似,不同模块的 mutation 均可以通过 store.commit 直接触发。
store.commit(‘sayCountA’); // Module A count: 1
store.commit(‘sayCountB’); // Module B count: 2
mutation 的回调函数中只接收唯一的参数——当前模块的 state。如果不同模块中有同名的 mutation,Vuex 不会报错,通过 store.commit 调用,会依次触发所有同名 mutation。
Actions
与 mutations 类似,不同模块的 actions 均可以通过 store.dispatch 直接触发。
store.dispatch(‘maAction’); // Module A count: 1、Module B count: 3
action 的回调函数接收一个 context 上下文参数,context 包含:1. state、2. rootState、3. getters、4. mutations、5. actions 五个属性,为了简便可以在参数中解构。
在 action 中可以通过 context.commit 跨模块调用 mutation,同时一个模块的 action 也可以调用其他模块的 action。
同样的,当不同模块中有同名 action 时,通过 store.dispatch 调用,会依次触发所有同名 actions。

你也可以使用 vm.$set 实例方法,该方法是全局方法 Vue.set 的一个别名:
=>
Vue.set(sdata[0].data, len-1, outT);
Vue.set(sdata[1].data, len-1, inT);
=> Error: Vue undefined.
this.$set(sdata[0].data, len-1, outT);
this.$set(sdata[1].data, len-1, inT);

iview表单验证的步骤:
第一步:给 Form 设置属性 rules :rules
第二步:同时给需要验证的每个 FormItem 设置属性 prop 指向对应字段即可 prop=”“
第三步:Form标签里面是 :model
第四步:在Form标签里面必须添加 ref,相当于id,在此范围内的表单验证有效
第五步:在操作保存按钮时,添加方法,对整个表单进行校验,参数为检验完的回调,会返回一个 Boolean 表示成功与失败。

http://v2.iviewui.com/components/form-en Form Validation

iView Input type. Optional value: text, password, textarea, url, email, date

<Form ref=’settings’ :model=’settings’ :rules=”ruleValidate”>
<FormItem label=’user’ prop=”user”>
<Input placeholder=”username” v-model=’settings.user’></Input>
</FormItem>
<FormItem>
<Button type=”primary” @click=”handleSubmit(‘formCustom’)”>Submit</Button>
<Button type=”ghost” @click=”handleReset(‘formCustom’)” style=”margin-left: 8px”>Reset</Button>
</FormItem>
</Form>
var vue= new Vue({
el: ‘#addModule’,
data(){
var validateuser = function(rule, value, callback){
if(!value){
return callback(new Error(“请输入用户名”));
}else if(!/^[a-zA-Z\d]+$/.test(value)){
return callback(new Error(“请正确输入用户名”))
}else{
callback();
}
};
var validatePass = function(rule, value, callback) {
if (value === ”) {
callback(new Error(‘请输入密码’));
} else {
callback();
}
};
var validatePassCheck = function(rule, value, callback){
if (value === ”) {
return callback(new Error(‘请再次输入密码’));
} else if (value !== vue.settings.password) {
return callback(new Error(‘两次密码不一致’));
} else {
callback();
}
};
return{
group:[],
settings:{
‘username’:””,
‘password’:””,
‘qq’:””,
‘group’:””
},
ruleValidate:{
username : [{validator: validateuser,trigger: ‘blur’}],
password : [{validator: validatePass, trigger: ‘blur’ },{min:6,message:’请输入最少6位’}],
rpassword : [{validator: validatePassCheck, trigger: ‘blur’ },{min:6,message:’请输入最少6位’}],
group:[{required: true, type: ‘string’, message: ‘请选择分组’, trigger: ‘change’}],
}
}
},
}
methods: {
handleSubmit (name) {
this.$refs[name].validate((valid) => {
if (valid) {
this.$Message.success(‘Success!’);
} else {
this.$Message.error(‘Fail!’);
}
})
},
handleReset (name) {
this.$refs[name].resetFields();
}
}

分类: vueweb

发表评论

电子邮件地址不会被公开。