MetaMask/obs-store

https://github.com/MetaMask/obs-store

ObservableStore

ObservableStore is a synchronous in-memory store for a single value, that you can subscribe to updates on

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()函数来得到存储的值
从上面的例子可以看出能且只能够存储一个值

streams

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.

ObservableStore可以转换成ObservableStoreStream流,并且是一个双工流

Special behavior: Doesnt buffer outgoing updates, writes latest state to dest on pipe.

不缓冲输出更新,将最新状态写到管道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)
)

 

Changelog

3.0.0

ObservableStore are no longer streams. You can create streams via asStream.

通过asStream来创建ObservableStoreStream

obs-store/index.js

'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)//存储newStatethis.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库:

obs-store/lib/asStream.js

作用是将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)
  }

}

 

obs-store/lib/localStorage.js

设置一个继承ObservableStoreLocalStorageStore,页面端的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)//将会导致storeTwostoreThree再分别触发一次

  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
}

 

posted @ 2018-11-07 16:54  慢行厚积  阅读(751)  评论(0编辑  收藏  举报