瀏覽代碼

Merge pull request #55 from ryangjchandler/feature/persistance

feature(persistence)
Ryan Chandler 4 年之前
父節點
當前提交
2ac7d5df66
共有 10 個文件被更改,包括 114 次插入9 次删除
  1. 0 1
      dist/spruce.js
  2. 0 0
      dist/spruce.js.map
  3. 0 1
      dist/spruce.module.js
  4. 0 0
      dist/spruce.module.js.map
  5. 0 1
      dist/spruce.umd.js
  6. 0 0
      dist/spruce.umd.js.map
  7. 21 2
      examples/index.html
  8. 35 3
      src/index.js
  9. 9 1
      src/utils.js
  10. 49 0
      tests/persistence.spec.js

文件差異過大導致無法顯示
+ 0 - 1
dist/spruce.js


文件差異過大導致無法顯示
+ 0 - 0
dist/spruce.js.map


文件差異過大導致無法顯示
+ 0 - 1
dist/spruce.module.js


文件差異過大導致無法顯示
+ 0 - 0
dist/spruce.module.js.map


文件差異過大導致無法顯示
+ 0 - 1
dist/spruce.umd.js


文件差異過大導致無法顯示
+ 0 - 0
dist/spruce.umd.js.map


+ 21 - 2
examples/index.html

@@ -6,12 +6,31 @@
     <body>
         <div x-data>
             <span x-text="$store.application.name"></span>
-            <button @click="$store.application.name = 'Example'">Click</button>
+            <button @click="$store.application.toggle()">Click</button>
         </div>
+
+        <div x-data>
+            <code>Persisted</code>
+            <input type="text" x-model="$store.persisted.example">
+            <button type="button" @click="$store.persisted.method()">
+                Call method on persisted store
+            </button>
+        </div>
+
         <script>
             Spruce.store('application', {
-                name: 'Application'
+                name: 'Application',
+                toggle() {
+                    this.name = ['Application', 'Example'][Math.floor(Math.random() * 2)]
+                }
             })
+
+            Spruce.store('persisted', {
+                example: 'Hello',
+                method() {
+                    console.log('test')
+                }
+            }, true)
         </script>
     </body>
 </html>

+ 35 - 3
src/index.js

@@ -1,11 +1,15 @@
-import { domReady } from './utils'
+import { domReady, getMethods } from './utils'
 import { createObservable } from './observable'
 
 const Spruce = {
     stores: {},
 
+    persisted: [],
+
     subscribers: [],
 
+    disableReactivity: false,
+
     async start() {
         await domReady()
         
@@ -14,7 +18,17 @@ const Spruce = {
         document.addEventListener('turbolinks:render', this.attach)
 
         this.stores = createObservable(this.stores, {
-            set: this.updateSubscribers.bind(this)
+            set: () => {
+                if (this.disableReactivity) {
+                    return
+                }
+
+                this.updateSubscribers()
+
+                this.disableReactivity = true
+                this.persisted.forEach(this.updateLocalStorage.bind(this))
+                this.disableReactivity = false
+            }
         })
     },
 
@@ -32,7 +46,15 @@ const Spruce = {
         })
     },
 
-    store(name, state) {
+    store(name, state, persist = false) {
+        if (persist) {
+            this.stores[name] = this.retrieveFromLocalStorage(name, getMethods(state))
+
+            if (! this.persisted.includes(name)) {
+                this.persisted.push(name)
+            }
+        }
+
         if (! this.stores[name]) {
             this.stores[name] = state
         }
@@ -56,6 +78,16 @@ const Spruce = {
         this.subscribers.filter(el => !! el.__x).forEach(el => {
             el.__x.updateElements(el)
         })
+    },
+
+    retrieveFromLocalStorage(name, methods = {}) {
+        const storage = JSON.parse(window.localStorage.getItem(`__spruce:${name}`))
+
+        return storage ? Object.assign(methods, storage) : null
+    },
+
+    updateLocalStorage(name) {
+        window.localStorage.setItem(`__spruce:${name}`, JSON.stringify(this.store(name)))
     }
 }
 

+ 9 - 1
src/utils.js

@@ -16,4 +16,12 @@ export const isObject = _ => {
     return Object.getPrototypeOf(_) === Object.prototype
 }
 
-export const isArray = _ => Array.isArray(_)
+export const isArray = _ => Array.isArray(_)
+
+export const getMethods = obj => {
+    let methods = {}
+
+    Object.entries(obj).filter(([key, value]) => typeof value === 'function').forEach(([key, value]) => methods[key] = value)
+
+    return methods
+}

+ 49 - 0
tests/persistence.spec.js

@@ -0,0 +1,49 @@
+import Alpine from 'alpinejs'
+import Spruce from '../dist/spruce'
+import { waitFor } from '@testing-library/dom'
+
+beforeEach(() => {
+    Spruce.subscribers = []
+})
+
+beforeAll(() => {
+    window.Spruce = Spruce
+    window.Alpine = Alpine
+
+    window.localStorage = {
+        storage: {},
+        getItem(key) {
+            return this.storage[key]
+        },
+        setItem(key, value) {
+            this.storage[key] = value
+        }
+    }
+})
+
+test('persisted stores are correctly persisted', async () => {
+    document.body.innerHTML = `
+        <div x-data>
+            <input type="text" x-model="$store.persisted.foo">
+        </div>
+    `
+    
+    Spruce.store('persisted', {
+        foo: 'bar',
+    }, true)
+
+    await Spruce.start()
+
+    Alpine.start()
+
+    expect(document.querySelector('input').value).toEqual('bar')
+
+    document.querySelector('input').value = 'car'
+
+    document.querySelector('input').dispatchEvent(new Event('input'))
+
+    await waitFor(() => {
+        expect(document.querySelector('input').value).toEqual('car')
+        expect(Spruce.retrieveFromLocalStorage('persisted').foo).toEqual('car')
+    })
+})

部分文件因文件數量過多而無法顯示