https://github.com/MetaMask/obs-storegit
ObservableStore
is a synchronous in-memory store for a single value, that you can subscribe to updates ongithub
ObservableStore是一個內存中的能夠訂閱更新的同步存儲,只存儲一個值瀏覽器
const store = new ObservableStore(initState) store.subscribe(function showValue(value) { console.log('saw value:', value) })
//經過調用putState()來存儲值 store.putState(5) // "saw value: 5" ,存儲了5 store.putState(true) // "saw value: true" ,true覆蓋了以前的5的值 store.putState({ hello: 'world' }) // "saw value: { hello: 'world' }" ,{ hello: 'world' }覆蓋了以前的true的值 console.log(store.getState().hello) // "world" ,經過調用getState()函數來獲得存儲的值
從上面的例子能夠看出能且只可以存儲一個值
Each ObservableStore
can be turned into an ObservableStoreStream
. An ObservableStoreStream
is a duplex stream that you can pipe new values into it or pipe its updated values out of it.app
ObservableStore
能夠轉換成ObservableStoreStream
流,而且是一個雙工流函數
Special behavior: Doesnt buffer outgoing updates, writes latest state to dest on pipe.oop
不緩衝輸出更新,將最新狀態寫到管道dest上測試
const pipe = require('pump') const asStream = require('obs-store/lib/asStream') const storeOne = new ObservableStore(initState) const storeTwo = new ObservableStore() pipe(//至關於asStream(storeOne).pipe(transformStream).pipe(asStream(storeTwo)),並且使用pump監聽錯誤 asStream(storeOne), transformStream, asStream(storeTwo) )
ObservableStore
are no longer streams. You can create streams via asStream
.ui
經過asStream
來建立ObservableStoreStream
流this
obs-store/index.jsspa
'use strict' const extend = require('xtend') const EventEmitter = require('events') class ObservableStore extends EventEmitter { constructor (initState = {}) { super() // set init state this._state = initState } // wrapper around internal getState getState () {//輸出值 return this._getState() } // wrapper around internal putState putState (newState) { this._putState(newState)//存儲newState值 this.emit('update', newState)//並觸發subscribe中的'update'事件,並調用相應的handler函數 } updateState (partialState) {//更改裏面的一部分的值 // if non-null object, merge if (partialState && typeof partialState === 'object') { const state = this.getState() const newState = Object.assign({}, state, partialState) this.putState(newState) // if not object, use new value } else { this.putState(partialState) } } // subscribe to changes subscribe (handler) { this.on('update', handler) } // unsubscribe to changes unsubscribe (handler) { this.removeListener('update', handler)//移除'update'事件 } // // private // // read from persistence _getState () { return this._state } // write to persistence _putState (newState) { this._state = newState } } module.exports = ObservableStore
其調用的lib庫:
做用是將ObsStore轉成ObsStoreStream流,並定義流相應的一些方法
const DuplexStream = require('stream').Duplex module.exports = asStream function asStream(obsStore) { return new ObsStoreStream(obsStore) } // // // // class ObsStoreStream extends DuplexStream { constructor(obsStore) { super({ // pass values, not serializations objectMode: true, }) // dont buffer outgoing updates this.resume() // save handler so we can unsubscribe later this.handler = (state) => this.push(state) // subscribe to obsStore changes this.obsStore = obsStore this.obsStore.subscribe(this.handler) } // emit current state on new destination pipe (dest, options) {//調用pipe函數,將obsStore.getState()值傳到dest const result = DuplexStream.prototype.pipe.call(this, dest, options) dest.write(this.obsStore.getState()) return result } // write from incomming stream to state _write (chunk, encoding, callback) { this.obsStore.putState(chunk) callback() } // noop - outgoing stream is asking us if we have data we arent giving it _read (size) { } // unsubscribe from event emitter _destroy (err, callback) { this.obsStore.unsubscribe(this.handler); super._destroy(err, callback) } }
設置一個繼承ObservableStore的LocalStorageStore,頁面端的global.localStorage就是這個
'use strict' const ObservableStore = require('../') class LocalStorageStore extends ObservableStore { constructor (opts = {}) { if (!global.localStorage) throw new Error('LocalStorageStore - can\'t find localStorage.')//global.localStorage即瀏覽器自己是否有本地存儲 super() this._storageKey = opts.storageKey if (!this._storageKey) throw new Error('LocalStorageStore - no storageKey specified.') } // // private // // read from persistence _getState () { const serialized = global.localStorage.getItem(this._storageKey)//不一樣的_storageKey對應的是本地存儲的不一樣內容 return serialized ? JSON.parse(serialized) : undefined } // write to persistence _putState (newState) { const serialized = JSON.stringify(newState) return global.localStorage.setItem(this._storageKey, serialized) } } module.exports = LocalStorageStore
還有一些別的,可是先無論,以後用到再說
測試:
'use strict' const test = require('tape')const pipe = streamUtils.pipeconst ObservableStore = require('../') const asStream = require('../lib/asStream') const TEST_WAIT = 200 test('basic stream', function(t){ t.plan(2) const initState = 'init' const nextState = 'next' const storeOne = new ObservableStore(initState)//在初始化時就存儲值initState const storeTwo = new ObservableStore()//在初始化時並無存儲值 storeTwo.once('update', (value) => {//監聽一次'update'事件 initValueCheck(value)//查看value值與initState值是否相同 storeTwo.once('update', nextValueCheck)//再監聽一次查看與nextState的值是否相同 }) pipe(//將storeOne流中的值initState傳給storeTwo流,storeTwo調用putState時會調用一次'update'事件,查看是否於initState值相同 asStream(storeOne), asStream(storeTwo) ) storeOne.putState(nextState)//將storeOne中的值從initState改變成nextState,這裏又會觸發一次'update'事件,此次是查看是否於nextState相同 function initValueCheck(value){ t.equal(value, initState, 'storeTwo subscribed: state is initState') } function nextValueCheck(value){ t.equal(value, nextState, 'storeTwo subscribed: state is nextState') } }) test('double stream', function(t){ t.plan(4) const initState = 'init' const nextState = 'next' const storeOne = new ObservableStore(initState) const storeTwo = new ObservableStore() storeTwo.once('update', (initValue) => { initValueCheck('storeTwo', initValue) storeTwo.once('update', (nextValue) => nextValueCheck('storeTwo', nextValue)) }) const storeThree = new ObservableStore() storeThree.once('update', (initValue) => { initValueCheck('storeThree', initValue) storeThree.once('update', (nextValue) => nextValueCheck('storeThree', nextValue)) }) pipe( asStream(storeOne), asStream(storeTwo)//storeTwo觸發一次'update' ) pipe( asStream(storeOne), asStream(storeThree)//storeThree觸發一次'update' ) storeOne.putState(nextState)//將會致使storeTwo、storeThree再分別觸發一次 function initValueCheck(label, value){ t.equal(value, initState, `${label} subscribed: state is initState`) } function nextValueCheck(label, value){ t.equal(value, nextState, `${label} subscribed: state is nextState`) } })
LocalStorageStore的測試,首先localStorage是定義好的,而且是有storageKey值的,如{ storageKey: 'test' }
'use strict' const test = require('tape') const LocalStorageStore = require('../lib/localStorage') test('localStorage - localStorage presence validation', function(t){ t.plan(2) t.notOk(global.localStorage, 'global.localStorage not defined') t.throws(() => { new LocalStorageStore({ storageKey: 'test' }) }, Error, 'throws error when localStorage is not defined') }) test('localStorage - storageKey validation', function(t){ t.plan(2) global.localStorage = createLocalStorage() t.ok(global.localStorage, 'global.localStorage is defined') t.throws(() => { new LocalStorageStore() }, Error, 'throws error when opts.storageKey is not defined') }) test('localStorage - basic test', function(t){ t.plan(2) global.localStorage = createLocalStorage() t.ok(global.localStorage, 'global.localStorage is defined') const store = new LocalStorageStore({ storageKey: 'test' }) store.putState(42) t.equal(store.getState(), 42, 'store works roundtrips values great') }) test('localStorage - obj test', function(t){ t.plan(2) global.localStorage = createLocalStorage() t.ok(global.localStorage, 'global.localStorage is defined') const store = new LocalStorageStore({ storageKey: 'test' }) store.putState({ a: 123 }) t.deepEqual(store.getState(), { a: 123 }, 'store works roundtrips obj values great') }) function createLocalStorage() { const values = {} const localStorage = {} localStorage.getItem = (key) => values[key] localStorage.setItem = (key, value) => values[key] = value return localStorage }