解決vue刷新頁面以后丟失store的數(shù)據(jù)問題
刷新頁面時vue實例重新加載,store就會被重置,可以把定義刷新前把store存入本地localStorage、sessionStorage、cookie中,localStorage是永久儲存,重新打開頁面時會讀取上一次打開的頁面數(shù)據(jù),sessionStorage是儲存到關(guān)閉為止,cookie不適合存大量數(shù)據(jù)。根據(jù)我的需求,最合適的是sessionStorage。
beforeunload在頁面刷新時觸發(fā),可以監(jiān)聽這個方法,讓頁面在刷新前存store到sessionStorage中。
當然,在頁面刷新時還要讀取sessionStorage中的數(shù)據(jù)到store中,讀取和儲存都寫在app.vue中。
export default {
name: 'app',
created () {
// 在頁面加載時讀取sessionStorage
if (sessionStorage.getItem('store')) {
this.$store.replaceState(Object.assign({}, this.$store.state, JSON.parse(sessionStorage.getItem('store'))))
}
// 在頁面刷新時將store保存到sessionStorage里
window.addEventListener('beforeunload', () => {
sessionStorage.setItem('store', JSON.stringify(this.$store.state))
})
}
}
補充知識:vue項目將token存在(vuex)store和localstorage中
一、準備工作和token
1、準備工作
了解(session,cookie)token
Token的引入:Token是在客戶端頻繁向服務(wù)端請求數(shù)據(jù),服務(wù)端頻繁的去數(shù)據(jù)庫查詢用戶名和密碼并進行對比,判斷用戶名和密碼正確與否,并作出相應(yīng)提示,在這樣的背景下,Token便應(yīng)運而生。
token 是在服務(wù)端產(chǎn)生的一串字符串,以作客戶端進行請求的一個令牌。如果前端使用用戶名/密碼向服務(wù)端請求認證,服務(wù)端認證成功,那么在服務(wù)端會返回 Token 給前端。前端可以在每次請求的時候帶上 Token 證明自己的合法地位。如果這個 Token 在服務(wù)端持久化(比如存入數(shù)據(jù)庫),那它就是一個永久的身份令牌(除非設(shè)置了有效期)。
token 優(yōu)點
Token 完全由應(yīng)用管理,所以它可以避開同源策略
Token 可以避免 CSRF 攻擊
Token 可以是無狀態(tài)的,可以在多個服務(wù)間共享
減輕服務(wù)器的壓力,減少頻繁的查詢數(shù)據(jù)庫,使服務(wù)器更加健壯。
安裝vuex
cnpm install vuex --save
2、介紹token用法
在前后端完全分離的情況下,Vue項目中實現(xiàn)token驗證大致思路如下:
1、第一次登錄的時候,前端調(diào)后端的登陸接口,發(fā)送用戶名和密碼
2、后端收到請求,驗證用戶名和密碼,驗證成功,就給前端返回一個token
3、前端拿到token,將token存儲到localStorage和vuex中,并跳轉(zhuǎn)路由頁面
4、前端每次跳轉(zhuǎn)路由,就判斷 localStroage 中有無 token ,沒有就跳轉(zhuǎn)到登錄頁面,有則跳轉(zhuǎn)到對應(yīng)路由頁面
5、每次調(diào)后端接口,都要在請求頭中加token
6、后端判斷請求頭中有無token,有token,就拿到token并驗證token,驗證成功就返回數(shù)據(jù),驗證失敗(例如:token過期)就返回401,請求頭中沒有token也返回401
7、如果前端拿到狀態(tài)碼為401,就清除token信息并跳轉(zhuǎn)到登錄頁面
二、創(chuàng)建storage,store,request
1、src目錄:

注:創(chuàng)建storage是可選的,因為我把localstorage(緩存)封裝到了storage.js(本文后續(xù)代碼均是用自己封裝的js);創(chuàng)建store是必須的!
2、創(chuàng)建storage(可選)
// 封裝操作localstorage本地存儲的方法 模塊化
var storage = {
set(key, value) {
localStorage.setItem(key, JSON.stringify(value));
// localStorage.key = value;
// localStorage[key] = value;
},
get(key) {
return JSON.parse(localStorage.getItem(key));
},
getForIndex(index) {
return localStorage.key(index);
},
getKeys(){
let items = this.getAll();
let keys = [];
for (let index=0;index<items.length;index++){
keys.push(items[index].key);
}
return keys;
},
getLength() {
return localStorage.length;
},
getSupport() {
return (typeof (Storage) !== "undefined") ? true : false;
},
remove(key) {
localStorage.removeItem(key);
},
removeAll() {
localStorage.clear();
},
getAll() {
let len = localStorage.length; // 獲取長度
let arr = new Array(); // 定義數(shù)據(jù)集
for (var i = 0; i < len; i++) {
// 獲取key 索引從0開始
var getKey = localStorage.key(i);
// 獲取key對應(yīng)的值
var getVal = localStorage.getItem(getKey);
// 放進數(shù)組
arr[i] = {
'key': getKey,
'val': getVal,
}
}
return arr;
}
}
export default storage;
3、創(chuàng)建store
import Vue from 'vue'
import Vuex from 'vuex'
import storage from '@/model/storage'
Vue.use(Vuex);
// 用Vuex.Store對象用來記錄token
const store = new Vuex.Store({
state: {
// 存儲token
// token: storage.get('token') ? storage.get('token') : '',
token:"",
userName:"" // 可選
},
actions: {
// removeToken: () => {
// context.commit('set_token')
// }
},
// 計算屬性
mutations: {
// 修改token,并將token存入localStorage
set_token(state,token) {
state.token = token;
storage.set('token', token);
console.log('store、localstorage保存token成功!');
},
del_token(state) {
state.token = "";
storage.remove("token");
},
// 可選
setUserInfo(state, userName) {
state.userName = userName;
}
}
});
export default store;
4、創(chuàng)建request
import axios from 'axios'
import store from '@/store'
import router from '@/router'
// create an axios instance
const service = axios.create({
// index.js設(shè)置了代理(解決跨域) invoice = http://58.246.79.142:25005
baseURL: "/invoice", // url = base url + request url
timeout: 5000, // request timeout
}
})
//添加請求攔截器,若token存在則在請求頭中加token,不存在也繼續(xù)請求
service.interceptors.request.use(
config => {
// 每次發(fā)送請求之前檢測都vuex存有token,那么都要放在請求頭發(fā)送給服務(wù)器,沒有則不帶token
// Authorization是必須的
if (store.state.token) {
config.headers.Authorization = store.state.token;
}
return config;
},
error => {
console.log("在request攔截器顯示錯誤:", error.response)
return Promise.reject(error);
}
);
//respone攔截器
service.interceptors.response.use(
response => {
// 在status正確的情況下,code不正確則返回對應(yīng)的錯誤信息(后臺自定義為200是正確,并且將錯誤信息寫在message),正確則返回響應(yīng)
return response.data.code == 200 ? response : Promise.reject(response.data.message);
},
error => {
// 在status不正確的情況下,判別status狀態(tài)碼給出對應(yīng)響應(yīng)
if (error.response) {
console.log("在respone攔截器顯示錯誤:", error.response)
switch (error.response.status) {
case 401:
//可能是token過期,清除它
// this.$store.commit("del_token");
store.commit("del_token");
router.replace({ //跳轉(zhuǎn)到登錄頁面
path: '/login',
// 將跳轉(zhuǎn)的路由path作為參數(shù),登錄成功后跳轉(zhuǎn)到該路由
query: { redirect: router.currentRoute.fullPath }
});
}
}
return Promise.reject(error.response.data);
}
);
export default service
三、配置代理,封裝路由router、設(shè)置路由守衛(wèi),在main.js中引入router
一、配置代理

二、封裝路由router,并設(shè)置路由守衛(wèi)
/* eslint-disable */
import Vue from 'vue'
import Router from 'vue-router'
import Main from '@/main/index'
import store from '@/store'
import storage from '@/model/storage'
Vue.use(Router)
const routes = [
{
path: '/',
name: 'Main',
redirect: '/login',
// 某些頁面規(guī)定必須登錄后才能查看 ,可以在router中配置meta,將需要登錄的requireAuth設(shè)為true,
meta: {
requireAuth: true,
}
},
{
path: '/login',
component: () => import('@/views/login'),
},
{
path: '/invoice',
redirect: '/invoice',
component: Main,
},
]
},
{
path: '*',
component: Main
}
]
const router = new Router({ routes: routes })
// 設(shè)置路由守衛(wèi),在進頁面之前,判斷有token,才進入頁面,否則返回登錄頁面
if (storage.get("token")) {
store.commit("set_token", storage.get("token"));
}
router.beforeEach((to, from, next) => {
// 判斷要去的路由有沒有requiresAuth
// to.matched.some(r => r.meta.requireAuth) or to.meta.requiresAuth
if (to.matched.some(r => r.meta.requireAuth)) {
if (store.state.token) {
next(); //有token,進行request請求,后臺還會驗證token
} else {
next({
path: "/login",
// 將剛剛要去的路由path(卻無權(quán)限)作為參數(shù),方便登錄成功后直接跳轉(zhuǎn)到該路由,這要進一步在登陸頁面判斷
query: { redirect: to.fullPath }
});
}
} else {
next(); //如果無需token,那么隨它去吧
}
});
export default router
三、在main.js中引入router
import Vue from 'vue'
// import Vuex from 'vuex'
import App from './App'
import router from './router'
import ElementUI from 'element-ui';
import 'element-ui/lib/theme-chalk/index.css'
// import VueResource from 'vue-resource'
// import axios from 'axios'
// 兼容ie
// import 'babel-polyfill';
Vue.use(ElementUI);
// Vue.use(Vuex);
// Vue.use(VueResource);
// Vue.use(axios);
Vue.config.productionTip = false
Vue.component(CollapseTransition.name, CollapseTransition)
// this.$ajax 全局使用axios
// Vue.prototype.$ajax=axios;
new Vue({
el: '#app',
router:router,
components: { App },
template: '<App/>'
})
四、登錄頁面實際使用
只給出表單提交示例函數(shù)和依賴js
import { postLogin } from "@/api/login";
submitForm(formName) {
this.$refs[formName].validate(valid => {
if (valid) {
let that = this;
// console.log('username',this.loginForm.username)
// 通過校驗規(guī)則后進入校驗用戶名密碼是否正確
postLogin(this.loginForm.username, this.loginForm.password)
.then(res => {
console.log(res);
that.$store.commit("set_token", res.data.token);
that.$store.commit("setUserInfo", res.data.account);
this.$notify({
title: "登錄成功",
type: "success",
showClose: false,
duration: 1000
});
setTimeout(() => {
// 此時要判斷/login后面的參數(shù),若無參數(shù),進入主頁;
this.$router.push("/index");
// 若有參數(shù)則參數(shù)為未有權(quán)限的那個路由,跳轉(zhuǎn)到那個路由
// this.$router.push(***); -- 具體要自己在這實現(xiàn)
}, 1000);
})
.catch(error => {
// 錯誤分為 status-請求錯誤 和 code-賬號密碼錯誤
this.$message.error(error);
console.log(error);
});
} else {
// 不符合前端校驗
this.$message.error('format error:'+error);
console.log('format error:',error);
return false;
}
});
}
上面依賴的@/api/login:
import request from '@/utils/request.js'
export function postLogin(account,password) {
console.log(account,password)
return request({
url: '/login',
method: 'post',
params:{
// 具體傳參(鍵)要看后臺要求
account:account,
password:password
}
})
}
以上這篇解決vue刷新頁面以后丟失store的數(shù)據(jù)問題就是小編分享給大家的全部內(nèi)容了,希望能給大家一個參考,也希望大家多多支持腳本之家。
相關(guān)文章
一文掌握Pinia使用及數(shù)據(jù)持久化存儲超詳細教程
這篇文章主要介紹了Pinia安裝使用及數(shù)據(jù)持久化存儲的超詳細教程,本文通過實例代碼給大家介紹的非常詳細,對大家的學(xué)習(xí)或工作具有一定的參考借鑒價值,需要的朋友可以參考下2023-07-07
element-ui中如何給el-table的某一行或某一列加樣式
本文主要介紹了element-ui中怎么給el-table的某一行或某一列加樣式,文中通過示例代碼介紹的非常詳細,對大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價值,需要的朋友們下面隨著小編來一起學(xué)習(xí)學(xué)習(xí)吧2023-01-01
基于Vue2實現(xiàn)簡易的省市區(qū)縣三級聯(lián)動組件效果
這是一個基于Vue2的簡易省市區(qū)縣三級聯(lián)動組件,可以控制只顯示省級或只顯示省市兩級,可設(shè)置默認值等。提供原始省市縣代碼和名稱數(shù)據(jù),適用于各種有關(guān)城市區(qū)縣的應(yīng)用。需要的朋友可以參考下2018-11-11
關(guān)于vue3+echart5?遇到的坑?Cannot?read?properties?of?undefine
這篇文章主要介紹了vue3+echart5?遇到的坑?Cannot?read?properties?of?undefined?(reading?'type'),本文給大家介紹的非常詳細,對大家的學(xué)習(xí)或工作具有一定的參考借鑒價值,需要的朋友可以參考下2023-04-04
Element-Plus實現(xiàn)動態(tài)渲染圖標的示例代碼
在Element-Plus中,我們可以使用component標簽來動態(tài)渲染組件,本文主要介紹了Element-Plus?實現(xiàn)動態(tài)渲染圖標教程,具有一定的參考價值,感興趣的可以了解一下2024-03-03

