前端的平常开发中,常常须要使用到localStorage存储,在使用Vue做为开发框架时,但愿能与Vue的响应式系统集成到一块儿,能够像vue-router/vuex相似的模式使用。前端
vue的响应式原理,是为一个对象添加特定的属性描述符,劫持它全部属性的getter/setter。 在这里,咱们定义一个对象_storage
,遍历它的全部属性,经过官方暴露出的方法——Vue.util.defineReactive,把_storage
变成observable
了,那么只要修改_storage某个key
的值,依赖(模板/数据)就能实时更新。vue
接下来,咱们须要把_storage
对象与本地的localStorage
作实时同步。粗糙的办法是每次更新_storage
时手动更新localStorage
,这就不符合咱们作这个插件的初衷了,咱们的愿景是自动同步。git
咱们也像Vue那样使用Object.defineProperty
的功能,在getter/setter
里调用对应的localStorage api
不就能解决问题了?但是这样的话_storage
的每个属性都被Object.defineProperty
两次了,那么前面一次就会被覆盖,没法生效啊!github
Vue.util.defineReactive
会保存前一次的
getter/setter
(若是有的话),在将来每一次的
getter/setter
中都会执行一次,在这里咱们就能够调用相关的
localStorage api
了。
配置项web
localStorage
中使用到的key-value
,且须要指定数据类型,由于保存在本地存储的数据只能是字符串,因此取本地web存储数据时要根据原来的数据类型进行解析,例如对象须要JSON.parse
key
的公共前缀namespace
,方便标识某个项目使用的web存储const storage = Vue.use(Storage, 'my-namespace', { string: { type: String, default: 'test' }, number: { type: Number }, object: { type: Object, default: { hello: 'world' } } }) class Storage { static install (Vue, nameSpace, options) { if (typeof nameSpace === 'object') { options = nameSpace nameSpace = 'vue-storage' } return new Storage(Vue, nameSpace, options) } } 复制代码
插件定义Storage
类,且提供一个install
方法给Vue进行注册。key/value
提供type
和default
指定类型和默认值。vue-router
class Storage { constructor (Vue, nameSpace, options = {}) { const self = this this.Vue = Vue this.nameSpace = `${nameSpace}-` this.options = options // 刷新页面时,把本地storage从新取出来 const cacheStorage = Object.keys(window.localStorage) .filter(key => new RegExp(`^${this.nameSpace}`).test(key)) .reduce((acc, key) => Object.assign(acc, { [ key.replace(this.nameSpace, '') ]: window.localStorage[key] }), {}) // 每种数据类型的默认值 const keyMap = [ [ String, '' ], [ Boolean, '' ], [ Number, '' ], [ Array, [] ], [ Object, {} ], ] const map = this.typeMap = new Map(keyMap) let _storage = this.storage = { ...( Object.keys(options).reduce((acc, key) => { const { type, default: val } = options[key] if (!type) { Vue.util.warn(`type of the field 'key' is required`) return acc } return Object.assign(acc, { [key]: val === undefined ? map.get(type) : val }) }, {}) ), ...cacheStorage } } 复制代码
Storage
实例化的过程,先把本地web存储的数据提取出来,合并到配置的key/value
中,实现页面刷新不丢失数据。vuex
考虑到数据没有定义默认值,经过keyMap
为每种数据类型定义一个默认值。最终获得一个_storage
对象,且赋值到this.storage
供外部实例调用。api
class Storage { constructor () { Object.keys(_storage).forEach(key => { try { const val = _storage[key] this.set(key, val) } catch (e) { Vue.util.warn('vue-storage-error', e) } // 把_storage中key对应的value取值代理到localStorage中去 Object.defineProperty(_storage, key, { get: () => self.get(key), set: (val) => self.set(key, val), configurable: true }) // 定义可观察对象 Vue.util.defineReactive(_storage, key, _storage[key]) }) } get (key) { let val = window.localStorage.getItem(this._getKey(key)) val = this._parse(key, val) return val } set (key, val) { try { val = typeof val === 'object' ? JSON.stringify(val) : val window.localStorage.setItem(this._getKey(key), val) } catch (e) { Vue.util.warn(`storage setting fail, please check the value`) } } } 复制代码
经过Object.defineProperty
,_storage
中key对应的value
的读取/写入实际是在localStorage
中去读取/写入。markdown
class Storage { constructor () { const self = this Object.defineProperty(Vue.prototype, '$storage', { get: () => _storage }) // 代理Storage实例 Object.defineProperty(Vue.prototype, '$storager', { get: () => self }) Vue.storage = _storage Vue.storager = self } } 复制代码
参照vue-router
那样,对Vue/Vue实例
暴露_storage
可观察数据和Storage
实例,实如今组件内能够经过this.$storage
获取_storage
。框架
class Storage { get (key) { let val = window.localStorage.getItem(this._getKey(key)) val = this._parse(key, val) return val } set (key, val) { try { val = typeof val === 'object' ? JSON.stringify(val) : val window.localStorage.setItem(this._getKey(key), val) } catch (e) { Vue.util.warn(`storage setting fail, please check the value`) } } remove (key) { window.localStorage.removeItem(this._getKey(key)) } clear () { Object.keys(this.storage).forEach(key => { const lsKey = this._getKey(key) if (window.localStorage.hasOwnProperty(lsKey)) { window.localStorage.removeItem(lsKey) } }) } } 复制代码
提供get/set/remove/clear
方法操做localStorage
。相似于getItem/setItem/removeItem/clear
。
完