MK
摩柯社区 - 一个极简的技术知识社区
AI 面试

Vue Vuex 如何实现持久化存储与离线支持

2022-10-231.3k 阅读

前端开发 Vue:Vuex 持久化存储

Vuex 基础回顾

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

在一个典型的 Vuex 应用中,有以下几个核心概念:

  1. State:存储应用状态的地方,就像是一个大的容器,包含了所有组件可能会用到的数据。例如,在一个电商应用中,购物车的商品列表就可以存储在 State 中。
  2. Mutations:唯一能改变 State 的方法,它必须是同步函数。当需要改变 State 中的数据时,就要通过提交 Mutation 来实现。比如,当用户往购物车中添加商品,就需要提交一个添加商品到购物车的 Mutation。
  3. Actions:可以包含异步操作,例如向服务器发起 AJAX 请求。Actions 通常会调用 Mutations 来间接改变 State。例如,在获取用户信息时,先通过 Action 发起网络请求,获取到数据后再提交 Mutation 将用户信息存储到 State 中。
  4. Getters:从 State 派生出来的状态,类似于计算属性。比如,购物车商品列表中有商品的单价和数量,Getters 可以计算出购物车商品的总价。

Vuex 持久化存储的需求

在实际应用中,我们常常希望在页面刷新或者用户关闭浏览器后,Vuex 中的状态数据仍然能够保持。例如,用户在购物车中添加了商品,刷新页面后,购物车中的商品不应该消失。这就是 Vuex 持久化存储的需求。

默认情况下,Vuex 的状态存储在内存中,页面刷新或者浏览器关闭后,状态数据就会丢失。为了解决这个问题,我们需要将 Vuex 的状态数据存储在一些持久化的存储介质中,如浏览器的本地存储(Local Storage)、会话存储(Session Storage)或者 Cookie 等。

使用 Local Storage 实现 Vuex 持久化存储

Local Storage 是浏览器提供的一种本地存储机制,它可以在浏览器中存储键值对数据,并且数据会一直保留,直到被手动清除或者存储容量达到上限。

  1. 安装依赖 首先,我们可以使用 vuex-persistedstate 这个插件来实现 Vuex 状态的持久化存储。通过 npm 安装:
npm install vuex-persistedstate --save
  1. 配置 Vuexstore.js 文件中引入并配置 vuex-persistedstate
import Vue from 'vue';
import Vuex from 'vuex';
import createPersistedState from 'vuex-persistedstate';

Vue.use(Vuex);

const store = new Vuex.Store({
  state: {
    // 假设这里有一个用户信息的状态
    userInfo: null,
    // 购物车商品列表
    cartList: []
  },
  mutations: {
    SET_USER_INFO(state, userInfo) {
      state.userInfo = userInfo;
    },
    ADD_TO_CART(state, product) {
      state.cartList.push(product);
    }
  },
  actions: {
    // 异步获取用户信息的 Action
    async fetchUserInfo({ commit }) {
      try {
        const response = await axios.get('/api/userInfo');
        commit('SET_USER_INFO', response.data);
      } catch (error) {
        console.error('获取用户信息失败', error);
      }
    },
    // 向购物车添加商品的 Action
    addProductToCart({ commit }, product) {
      commit('ADD_TO_CART', product);
    }
  },
  getters: {
    getCartTotalPrice: state => {
      return state.cartList.reduce((total, product) => {
        return total + product.price * product.quantity;
      }, 0);
    }
  },
  plugins: [createPersistedState()]
});

export default store;

在上述代码中,通过 plugins: [createPersistedState()]vuex-persistedstate 插件添加到 Vuex 中。默认情况下,该插件会将 Vuex 的 State 存储到 Local Storage 中,键名为 vuex

  1. 自定义存储键名和存储路径 如果想要自定义存储在 Local Storage 中的键名,或者只持久化存储部分 State,可以这样配置:
plugins: [
  createPersistedState({
    key: 'my-custom-vuex-key',
    paths: ['userInfo', 'cartList']
  })
]

上述配置中,key 定义了存储在 Local Storage 中的键名,paths 数组指定了只持久化存储 userInfocartList 这两个 State 数据。

使用 Session Storage 实现 Vuex 持久化存储

Session Storage 与 Local Storage 类似,但它的生命周期是在浏览器会话期间,即页面关闭后数据就会被清除。

  1. 安装和配置 同样先安装 vuex-persistedstate 插件,然后在 store.js 中配置:
import Vue from 'vue';
import Vuex from 'vuex';
import createPersistedState from 'vuex-persistedstate';

Vue.use(Vuex);

const store = new Vuex.Store({
  state: {
    // 临时用户会话数据
    sessionData: null
  },
  mutations: {
    SET_SESSION_DATA(state, data) {
      state.sessionData = data;
    }
  },
  actions: {
    // 异步获取会话数据的 Action
    async fetchSessionData({ commit }) {
      try {
        const response = await axios.get('/api/sessionData');
        commit('SET_SESSION_DATA', response.data);
      } catch (error) {
        console.error('获取会话数据失败', error);
      }
    }
  },
  plugins: [
    createPersistedState({
      storage: window.sessionStorage,
      key: 'vuex-session',
      paths: ['sessionData']
    })
  ]
});

export default store;

在上述代码中,通过 storage: window.sessionStorage 指定使用 Session Storage 来存储 Vuex 状态数据,key 定义了存储在 Session Storage 中的键名,paths 数组指定了只持久化存储 sessionData 这个 State 数据。

使用 Cookie 实现 Vuex 持久化存储

Cookie 也是一种在客户端存储数据的机制,不过它与 Local Storage 和 Session Storage 有一些不同。Cookie 的大小有限制,并且每次 HTTP 请求都会携带 Cookie,所以不太适合存储大量数据。但在某些场景下,如存储用户认证信息等少量关键数据时,Cookie 还是很有用的。

  1. 安装依赖 我们可以使用 js-cookie 库来操作 Cookie。通过 npm 安装:
npm install js-cookie --save
  1. 自定义持久化插件store.js 中,我们需要自定义一个 Vuex 插件来实现使用 Cookie 进行持久化存储。
import Vue from 'vue';
import Vuex from 'vuex';
import Cookies from 'js-cookie';

Vue.use(Vuex);

// 自定义 Cookie 持久化插件
const cookiePlugin = store => {
  const savedState = Cookies.get('vuex-cookie');
  if (savedState) {
    try {
      store.replaceState({
        ...store.state,
        ...JSON.parse(savedState)
      });
    } catch (error) {
      console.error('解析 Cookie 中的 Vuex 数据失败', error);
    }
  }
  store.subscribe((mutation, state) => {
    Cookies.set('vuex-cookie', JSON.stringify(state), {
      expires: 7, // Cookie 有效期 7 天
      path: '/'
    });
  });
};

const store = new Vuex.Store({
  state: {
    // 用户认证令牌
    token: null
  },
  mutations: {
    SET_TOKEN(state, token) {
      state.token = token;
    }
  },
  actions: {
    // 登录 Action,获取并设置令牌
    async login({ commit }, credentials) {
      try {
        const response = await axios.post('/api/login', credentials);
        commit('SET_TOKEN', response.data.token);
      } catch (error) {
        console.error('登录失败', error);
      }
    }
  },
  plugins: [cookiePlugin]
});

export default store;

在上述代码中,自定义的 cookiePlugin 插件在 Vuex 初始化时,从 Cookie 中读取数据并替换 State。然后,在每次 Vuex 状态发生变化时,将最新的 State 存储到 Cookie 中。Cookies.set 方法设置了 Cookie 的有效期为 7 天,路径为根路径。

前端开发 Vue:Vuex 离线支持

离线应用的背景和需求

随着移动互联网的发展,用户对于应用在离线状态下的使用体验有了更高的要求。在前端开发中,实现离线支持可以让用户在没有网络连接的情况下,仍然能够访问和操作部分应用功能。

对于 Vue 应用来说,结合 Vuex 实现离线支持主要有以下几个需求:

  1. 数据缓存:在有网络时,将需要的数据缓存到本地,以便离线时使用。这些数据可以存储在 Vuex 的 State 中,并通过持久化存储机制保存到本地存储介质。
  2. 离线状态下的操作:例如,在离线状态下,用户添加商品到购物车,应该能够正常操作,并且在恢复网络连接后,这些操作能够同步到服务器。
  3. 网络状态检测:实时检测网络状态,当网络变化时,能够做出相应的处理,如提示用户网络已连接或已断开,以及触发数据同步等操作。

数据缓存与离线使用

  1. 使用 Service Worker 进行数据缓存 Service Worker 是一种在后台运行的脚本,它可以拦截网络请求,缓存资源,实现离线访问。

首先,在项目根目录下创建 service - worker.js 文件。

self.addEventListener('install', event => {
  event.waitUntil(
    caches.open('my - app - cache')
     .then(cache => cache.addAll([
        // 缓存 HTML 文件
        '/index.html',
        // 缓存 CSS 文件
        '/styles.css',
        // 缓存 JavaScript 文件
        '/app.js',
        // 缓存图片等其他资源
        '/logo.png'
      ]))
  );
});

self.addEventListener('fetch', event => {
  event.respondWith(
    caches.match(event.request)
     .then(response => {
        if (response) {
          return response;
        }
        return fetch(event.request);
      })
  );
});

然后,在 Vue 应用的入口文件(通常是 main.js)中注册 Service Worker。

if ('serviceWorker' in navigator) {
  window.addEventListener('load', () => {
    navigator.serviceWorker.register('/service - worker.js')
     .then(registration => {
        console.log('ServiceWorker 注册成功', registration);
      })
     .catch(error => {
        console.log('ServiceWorker 注册失败', error);
      });
  });
}

上述代码中,service - worker.jsinstall 事件中缓存了一些关键的应用资源。在 fetch 事件中,优先从缓存中获取资源,如果缓存中没有,则发起网络请求。

  1. 结合 Vuex 缓存数据 在 Vuex 中,我们可以在获取数据的 Action 中,将数据存储到 State 并进行持久化存储。例如:
actions: {
  async fetchProducts({ commit }) {
    try {
      const response = await axios.get('/api/products');
      commit('SET_PRODUCTS', response.data);
      // 假设使用 Local Storage 持久化存储
      localStorage.setItem('products', JSON.stringify(response.data));
    } catch (error) {
      // 尝试从 Local Storage 读取数据
      const storedProducts = localStorage.getItem('products');
      if (storedProducts) {
        commit('SET_PRODUCTS', JSON.parse(storedProducts));
      }
      console.error('获取产品数据失败', error);
    }
  }
}

在上述代码中,当网络正常时,从服务器获取产品数据并存储到 Vuex State 和 Local Storage。当网络请求失败时,尝试从 Local Storage 读取数据并设置到 Vuex State,以提供离线使用。

离线状态下的操作与同步

  1. 模拟离线操作 假设我们有一个添加商品到购物车的功能,在离线状态下,用户仍然可以点击添加按钮将商品添加到购物车。
<template>
  <div>
    <button @click="addProductToCart(product)">添加到购物车</button>
  </div>
</template>

<script>
export default {
  data() {
    return {
      product: {
        id: 1,
        name: '示例商品',
        price: 100,
        quantity: 1
      }
    };
  },
  methods: {
    addProductToCart(product) {
      this.$store.dispatch('addProductToCart', product);
    }
  }
};
</script>

在 Vuex 的 store.js 中:

mutations: {
  ADD_TO_CART(state, product) {
    state.cartList.push(product);
  }
},
actions: {
  addProductToCart({ commit }, product) {
    commit('ADD_TO_CART', product);
    // 这里可以添加逻辑,如在离线时将操作记录下来
  }
}
  1. 同步离线操作 当网络恢复时,我们需要将离线时的操作同步到服务器。可以通过监听网络状态变化来实现。
// 在 main.js 中监听网络状态变化
window.addEventListener('online', () => {
  // 这里触发同步操作
  const store = new Vuex.Store({... });
  // 假设购物车添加商品操作需要同步到服务器
  const cartList = store.state.cartList;
  cartList.forEach(product => {
    // 发起网络请求将商品添加到服务器购物车
    axios.post('/api/addToCart', product)
     .then(response => {
        console.log('商品成功添加到服务器购物车', response.data);
      })
     .catch(error => {
        console.log('同步商品到服务器购物车失败', error);
      });
  });
});

上述代码中,当网络状态变为在线时,获取 Vuex 中购物车的商品列表,并将每个商品添加到服务器购物车的操作同步到服务器。

网络状态检测与处理

  1. 使用 navigator.onLine 属性 navigator.onLine 是浏览器提供的一个属性,用于检测当前网络状态。
// 在 main.js 中检测网络状态
if ('onLine' in navigator) {
  const onlineHandler = () => {
    console.log('网络已连接');
    // 可以在这里触发数据同步等操作
  };
  const offlineHandler = () => {
    console.log('网络已断开');
    // 可以在这里提示用户网络断开
  };
  window.addEventListener('online', onlineHandler);
  window.addEventListener('offline', offlineHandler);
}
  1. 结合 Vuex 管理网络状态 我们可以在 Vuex 中定义一个 State 来表示网络状态,并通过 mutations 和 actions 来更新和处理。
state: {
  isOnline: navigator.onLine
},
mutations: {
  SET_ONLINE_STATUS(state, isOnline) {
    state.isOnline = isOnline;
  }
},
actions: {
  updateOnlineStatus({ commit }) {
    const onlineHandler = () => {
      commit('SET_ONLINE_STATUS', true);
    };
    const offlineHandler = () => {
      commit('SET_ONLINE_STATUS', false);
    };
    window.addEventListener('online', onlineHandler);
    window.addEventListener('offline', offlineHandler);
  }
}

main.js 中调用 Action 来初始化网络状态检测:

const store = new Vuex.Store({... });
store.dispatch('updateOnlineStatus');

通过上述方式,我们可以在 Vuex 中管理网络状态,并且在网络状态变化时做出相应的处理,如在组件中根据网络状态显示不同的提示信息等。

通过以上对 Vuex 持久化存储和离线支持的详细介绍和代码示例,希望能帮助开发者更好地实现 Vue 应用在不同场景下的高效运行和良好用户体验。无论是持久化存储以保持状态的连续性,还是离线支持以应对网络不稳定的情况,都为构建现代化的前端应用提供了重要的技术手段。在实际项目中,开发者可以根据具体需求选择合适的持久化存储方式和离线支持策略,以满足用户的多样化需求。