• Stars
    star
    1,655
  • Rank 28,254 (Top 0.6 %)
  • Language
    TypeScript
  • License
    MIT License
  • Created over 7 years ago
  • Updated over 1 year ago

Reviews

There are no reviews yet. Be the first to send feedback to the community and the maintainers!

Repository Details

A Vuex plugin to persist the store. (Fully Typescript enabled)

vuex-persist

A Typescript-ready Vuex plugin that enables you to save the state of your app to a persisted storage like Cookies or localStorage.

Paypal Donate

Info : GitHub stars npm npm license

Status : Build Status codebeat badge Codacy Badge Code Climate codecov

Sizes : npm:size:gzip umd:min:gzip umd:min:brotli

Table of Contents

Table of contents generated with markdown-toc

Features

  • πŸ“¦ NEW in v1.5
    • distributed as esm and cjs both (via module field of package.json)
    • better tree shaking as a result of esm
  • πŸŽ— NEW IN V1.0.0
    • Support localForage and other Promise based stores
    • Fix late restore of state for localStorage
  • Automatically save store on mutation.
  • Choose which mutations trigger store save, and which don't, using filter function
  • Works perfectly with modules in store
  • Ability to save partial store, using a reducer function
  • Automatically restores store when app loads
  • You can create mulitple VuexPersistence instances if you want to -
    • Save some parts of the store to localStorage, some to sessionStorage
    • Trigger saving to localStorage on data download, saving to cookies on authentication result

Compatibility

  • VueJS - v2.0 and above
  • Vuex - v2.1 and above

Installation

Vue CLI Build Setup (using Webpack or some bundler)

npm install --save vuex-persist

or

yarn add vuex-persist

Transpile for target: es5

This module is distributed in 3 formats

  • umd build /dist/umd/index.js in es5 format
  • commonjs build /dist/cjs/index.js in es2015 format
  • esm build /dist/esm/index.js in es2015 format

When using with Webpack (or Vue CLI 3), the esm file gets used by default. If your project has a es6 or es2015 target, you're good, but if for backwards compatibility, you are compiling your project to es5 then this module also needs to be transpiled.

To enable transpilation of this module

// in your vue.config.js
module.exports = {
  /* ... other config ... */
  transpileDependencies: ['vuex-persist']
}

Directly in Browser

<!-- We need lodash.merge so get lodash first -->
<script src="https://cdnjs.cloudflare.com/ajax/libs/lodash.js/4.17.10/lodash.min.js"></script>
<script src="https://cdn.jsdelivr.net/npm/vuex-persist"></script>

Tips for NUXT

This is a plugin that works only on the client side. So we'll register it as a ssr-free plugin.

// Inside - nuxt.config.js
export default {
   plugins: [{ src: '~/plugins/vuex-persist.js', mode: 'client' }],
}
// ~/plugins/vuex-persist.js
import VuexPersistence from 'vuex-persist'

export default ({ store }) => {
  new VuexPersistence({
  /* your options */
  }).plugin(store);
}

Usage

Steps

Import it

import VuexPersistence from 'vuex-persist'

NOTE: In browsers, you can directly use window.VuexPersistence

Create an object

const vuexLocal = new VuexPersistence({
  storage: window.localStorage
})

// or in Typescript

const vuexLocal = new VuexPersistence<RootState>({
  storage: window.localStorage
})

Use it as Vue plugin. (in typescript)

const store = new Vuex.Store<State>({
  state: { ... },
  mutations: { ... },
  actions: { ... },
  plugins: [vuexLocal.plugin]
})

(or in Javascript)

const store = new Vuex.Store({
  state: { ... },
  mutations: { ... },
  actions: { ... },
  plugins: [vuexLocal.plugin]
})

Constructor Parameters -

When creating the VuexPersistence object, we pass an options object of type PersistOptions. Here are the properties, and what they mean -

Property Type Description
key string The key to store the state in the storage
Default: 'vuex'
storage Storage (Web API) localStorage, sessionStorage, localforage or your custom Storage object.
Must implement getItem, setItem, clear etc.
Default: window.localStorage
saveState function
(key, state[, storage])
If not using storage, this custom function handles
saving state to persistence
restoreState function
(key[, storage]) => state
If not using storage, this custom function handles
retrieving state from storage
reducer function
(state) => object
State reducer. reduces state to only those values you want to save.
By default, saves entire state
filter function
(mutation) => boolean
Mutation filter. Look at mutation.type and return true
for only those ones which you want a persistence write to be triggered for.
Default returns true for all mutations
modules string[] List of modules you want to persist. (Do not write your own reducer if you want to use this)
asyncStorage boolean Denotes if the store uses Promises (like localforage) or not (you must set this to true when using something like localforage)
Default: false
supportCircular boolean Denotes if the state has any circular references to itself (state.x === state)
Default: false

Usage Notes

Reducer

Your reducer should not change the shape of the state.

const persist = new VuexPersistence({
  reducer: (state) => state.products,
  ...
})

Above code is wrong You intend to do this instead

const persist = new VuexPersistence({
  reducer: (state) => ({products: state.products}),
  ...
})

Circular States

If you have circular structures in your state

let x = { a: 10 }
x.x = x
x.x === x.x.x // true
x.x.x.a === x.x.x.x.a //true

JSON.parse() and JSON.stringify() will not work. You'll need to install flatted

npm install flatted

And when constructing the store, add supportCircular flag

new VuexPersistence({
  supportCircular: true,
  ...
})

Examples

Simple

Quick example -

import Vue from 'vue'
import Vuex from 'vuex'
import VuexPersistence from 'vuex-persist'

Vue.use(Vuex)

const store = new Vuex.Store<State>({
  state: {
    user: { name: 'Arnav' },
    navigation: { path: '/home' }
  },
  plugins: [new VuexPersistence().plugin]
})

export default store

Detailed

Here is an example store that has 2 modules, user and navigation We are going to save user details into a Cookie (using js-cookie) And, we will save the navigation state into localStorage whenever a new item is added to nav items. So you can use multiple VuexPersistence instances to store different parts of your Vuex store into different storage providers.

Warning: when working with modules these should be registered in the Vuex constructor. When using store.registerModule you risk the (restored) persisted state being overwritten with the default state defined in the module itself.

import Vue from 'vue'
import Vuex, { Payload, Store } from 'vuex'
import VuexPersistence from 'vuex-persist'
import Cookies from 'js-cookie'
import { module as userModule, UserState } from './user'
import navModule, { NavigationState } from './navigation'

export interface State {
  user: UserState
  navigation: NavigationState
}

Vue.use(Vuex)

const vuexCookie = new VuexPersistence<State, Payload>({
  restoreState: (key, storage) => Cookies.getJSON(key),
  saveState: (key, state, storage) =>
    Cookies.set(key, state, {
      expires: 3
    }),
  modules: ['user'], //only save user module
  filter: (mutation) => mutation.type == 'logIn' || mutation.type == 'logOut'
})
const vuexLocal = new VuexPersistence<State, Payload>({
  storage: window.localStorage,
  reducer: (state) => ({ navigation: state.navigation }), //only save navigation module
  filter: (mutation) => mutation.type == 'addNavItem'
})

const store = new Vuex.Store<State>({
  modules: {
    user: userModule,
    navigation: navModule
  },
  plugins: [vuexCookie.plugin, vuexLocal.plugin]
})

export default store

Support Strict Mode

This now supports Vuex strict mode (Keep in mind, NOT to use strict mode in production) In strict mode, we cannot use store.replaceState so instead we use a mutation

You'll need to keep in mind to add the RESTORE_MUTATION to your mutations See example below

To configure with strict mode support -

import Vue from 'vue'
import Vuex, { Payload, Store } from 'vuex'
import VuexPersistence from 'vuex-persist'

const vuexPersist = new VuexPersistence<any, any>({
  strictMode: true, // This **MUST** be set to true
  storage: localStorage,
  reducer: (state) => ({ dog: state.dog }),
  filter: (mutation) => mutation.type === 'dogBark'
})

const store = new Vuex.Store<State>({
  strict: true, // This makes the Vuex store strict
  state: {
    user: {
      name: 'Arnav'
    },
    foo: {
      bar: 'baz'
    }
  },
  mutations: {
    RESTORE_MUTATION: vuexPersist.RESTORE_MUTATION // this mutation **MUST** be named "RESTORE_MUTATION"
  },
  plugins: [vuexPersist.plugin]
})

Some of the most popular ways to persist your store would be -

  • js-cookie to use browser Cookies
  • window.localStorage (remains, across PC reboots, untill you clear browser data)
  • window.sessionStorage (vanishes when you close browser tab)
  • localForage Uses IndexedDB from the browser

Note on LocalForage and async stores

There is Window.Storage API as defined by HTML5 DOM specs, which implements the following -

interface Storage {
  readonly length: number
  clear(): void
  getItem(key: string): string | null
  key(index: number): string | null
  removeItem(key: string): void
  setItem(key: string, data: string): void
  [key: string]: any
  [index: number]: string
}

As you can see it is an entirely synchronous storage. Also note that it saves only string values. Thus objects are stringified and stored.

Now note the representative interface of Local Forage -

export interface LocalForage {
  getItem<T>(key: string): Promise<T>
  setItem<T>(key: string, data: T): Promise<T>
  removeItem(key: string): Promise<void>
  clear(): Promise<void>
  length(): Promise<number>
  key(keyIndex: number): Promise<string>
  _config?: {
    name: string
  }
}

You can note 2 differences here -

  1. All functions are asynchronous with Promises (because WebSQL and IndexedDB are async)
  2. It works on objects too (not just strings)

I have made vuex-persist compatible with both types of storages, but this comes at a slight cost. When using asynchronous (promise-based) storages, your state will not be immediately restored into vuex from localForage. It will go into the event loop and will finish when the JS thread is empty. This can invoke a delay of few seconds.

How to know when async store has been replaced

As noted above, the store is not immediately restored from async stores like localForage. This can have the unfortunate side effect of overwriting mutations to the store that happen before vuex-persist has a chance to do its thing. In strict mode, you can create a plugin to subscribe to RESTORE_MUTATION so that you tell your app to wait until the state has been restored before committing any further mutations. (Issue #15 demonstrates how to write such a plugin.) However, since you should turn strict mode off in production, and since vuex doesn't currently provide any kind of notification when replaceState() has been called, starting with v2.1.0 vuex-persist will add a restored property to the store object to let you know the state has been restored and that it is now safe to commit any mutations that modify the stored state. store.restored will contain the Promise returned by calling the async version of restoreState().

Here's an example of a beforeEach() hook in vuex-router that will cause your app to wait for vuex-persist to restore the state before taking any further actions:

// in src/router.js
import Vue from 'vue'
import Router from 'vue-router'
import { store } from '@/store' // ...or wherever your `vuex` store is defined

Vue.use(Router)

const router = new Router({
  // define your router as you normally would
})

const waitForStorageToBeReady = async (to, from, next) => {
  await store.restored
  next()
}
router.beforeEach(waitForStorageToBeReady)

export default router

Note that on the 2nd and subsequent router requests to your app, the Promise in store.restored should already be in a "resolved" state, so the hook will not force your app to wait for additional calls to restoreState().

Unit Testing

Jest

When testing with Jest, you might find this error -

TypeError: Cannot read property 'getItem' of undefined

This is because there is no localStorage in Jest. You can add the following Jest plugins to solve this https://www.npmjs.com/package/jest-localstorage-mock

More Repositories

1

vuex-module-decorators

TypeScript/ES7 Decorators to create Vuex modules declaratively
TypeScript
1,796
star
2

SimpleFingerGestures_Android_Library

Android Library to implement simple touch/tap/swipe gestures
Java
315
star
3

android-locales

194
star
4

Android-SocialButtons

A library for easily implementing social login/share buttons
Java
150
star
5

awesome-jetbrains

A collection of awesome Fonts and Color Schemes to be used in Jetbrains IDEs.
88
star
6

awesome-devs

Everytime I come across good Github profiles I index them here
81
star
7

kernel-tools

tools to tinker with kernels and ramdisks and bootsplash images
C
78
star
8

NFC-host-card-emulation-Android

Testing bidirectional data transfer using NFC HCE on Android
Java
71
star
9

low-level-design-problem

Case studies (with solution codes) for Low Level System Design problems
Java
48
star
10

SPORK

Kotlin
48
star
11

GithubTrendingNow_Android

Kotlin
34
star
12

Navi_Imgur_App

Kotlin
34
star
13

InstaClone

Kotlin
21
star
14

yt_dlp_gui

Dart
20
star
15

vue-plugin-timers

TypeScript
16
star
16

Lifelog-Android-Library

An Android Library/API to access Sony Lifelog palatform.
Java
16
star
17

oh-my-zsh

Shell
14
star
18

ParkingLot-LLD-Kotlin-MPP

Kotlin
14
star
19

sharetime.in

Share your time, to anyone on any timezone
TypeScript
13
star
20

faangshaadi.com

HTML
12
star
21

hasgeek_mobile_nsvue

CSS
12
star
22

nativescript-vue-typescript-starter

Starter project for https://nativescript-vue.org using Typescript
JavaScript
12
star
23

express-ga-middleware

TypeScript
11
star
24

imguram

Kotlin
10
star
25

NFC-Bidirectional-over-HCE

Implementing bidirectional data transfer (half-duplex) using NFC HCE on Android
CSS
9
star
26

vscode-theme-quieter-dark

8
star
27

Conduit_SpringBoot_Postgres

Java
8
star
28

sharetime.zone

Share time in different timezones
Vue
7
star
29

authentication-authorization-20240717

6
star
30

SchoolTabApp

Java
6
star
31

LeetCodeByADev.js

Solving LeetCode using Dev-friendly JS Snippets
JavaScript
5
star
32

nativescript-awesome-webview

TypeScript
5
star
33

GoogleDrive_as_Datastore_Example

This project shows how you can use Google Drive as a datastore for your Android and Web apps
Java
5
star
34

Android-Tests-Example

An example repo showing how to setup unit tests and instrumentation tests on Android
Java
5
star
35

championswimmer

Github Profile Yay!
5
star
36

twitter-threads

5
star
37

GenericRecyclerAdapter

A RecyclerView Adapter that works with any type of Model(POJO)
Java
4
star
38

BadooTransactionApp

Java
4
star
39

Conduit_Flutter

Dart
4
star
40

parse-heroku-postgres

JavaScript
4
star
41

Kocktail_KMP_App

Kotlin
3
star
42

JavaConcurrencyExamples

Java
3
star
43

AlgorithmPractice

Java
3
star
44

guberx.io

HTML
3
star
45

qBounty-api-server

JavaScript
3
star
46

championswimmer.github.io

Championswimmer's About Page
CSS
3
star
47

express-universal-analytics

TypeScript
3
star
48

Xperia_FOTAKernel_Manager_Android_App

Java
3
star
49

wakanda-backend

TypeScript
3
star
50

spring-boot-taskmanager-demo

Java
3
star
51

Pyrrit

Simple python client for gerrit. To enable quick patchset listings, pushing and pulling.
Python
3
star
52

RealWorld-Spring-Kotlin

Kotlin
3
star
53

spring-boot-sockets-music

Java
3
star
54

GadiMeter

A live meter for many Indian cab and auto providers. Also provides fare estimates between two destinations.
Java
3
star
55

ns-vue-ts-sample

JavaScript
3
star
56

Learn_2_readwrite_files_Cpp

C++
2
star
57

design-patterns-sample-repo-java-tanmaykacker

Java
2
star
58

ember-parse-lib

JavaScript
2
star
59

oneauth

JavaScript
2
star
60

Flutter-PlatformWidgets-Sample

Dart
2
star
61

url-parser-workshop-typescript

TypeScript
2
star
62

InterComGeoDist

Kotlin
2
star
63

uri_parser_workshop_go

Go
2
star
64

InterviewQuestionsCpp

C++
2
star
65

vue-vuex-typescript-sample

CSS
2
star
66

conduit_flutter_riverpod

Dart
2
star
67

dukaan-backend

TypeScript
2
star
68

PCBuilderKT

Kotlin
2
star
69

realworld-vue3-pinia

Vue
2
star
70

AndroidWear_ComplicationProvider_Sample

Kotlin
2
star
71

AppDesk-NodeJS-Session-01

JavaScript
2
star
72

ember-parse-adapter-two

JavaScript
2
star
73

shitposting-bot

TypeScript
2
star
74

REST_API_Design_2024-03-16

2
star
75

Bookd_Android_App

Java
2
star
76

android_kernel_sony_msm8930

Sony Xperia L kernel source
C
2
star
77

nest-typeorm-sample

TypeScript
1
star
78

smolugger_rs

Rust
1
star
79

onepixel_backend

Go
1
star
80

words

words
1
star
81

Phonytale

Java
1
star
82

OverlayService

Java
1
star
83

URL_Shortner_Discord_Session

TypeScript
1
star
84

CollegeFests_Android_App

Java
1
star
85

new-vue-project

JavaScript
1
star
86

LeetCode-VSCode

TypeScript
1
star
87

india-aggregator-list

PHP
1
star
88

hashnode-backups

1
star
89

uses.page

TypeScript
1
star
90

ETHNYC2023-MonaWallpapers

Kotlin
1
star
91

fake-score-netlify-edge

TypeScript
1
star
92

ngbc-sample-march19-2

JavaScript
1
star
93

convert-radix64

JavaScript
1
star
94

cb-sample-heroku-app-02

JavaScript
1
star
95

ReFuel

Android App to keep track of fuel and mileage of your car
Java
1
star
96

IITD_Tryst_Android_App

Android App for Tryst
Java
1
star
97

android-drawable-converter

Automatically exported from code.google.com/p/android-drawable-converter
Java
1
star
98

simavr

C
1
star
99

sample-express-universal-analytics

TypeScript
1
star
100

WebNode2016Fall

HTML
1
star