评论

我们为小程序开发的钩子与订阅系统

可以把小程序看做一个SPA的web应用,即可以内存数据共享,这就为我们的钩子系统提供了用武之地。可以跨路由页面使用钩子

钩子与订阅系统是两种很爽工具类方法,码代码的时候一直用一直爽,直到钩子漫天飞,然后就重构。。。,不想重构需要斟酌的来使用它们

可以把小程序看做一个SPA的web应用,即可以内存数据共享,这就为我们的钩子系统提供了用武之地。可以跨路由页面使用钩子

我们为aotoo小程序开发了一套用起来很简单的钩子/订阅/数据存储的通用方法hooks,虽然是钩子的英文名,但它是一个混合物。

const core = require(‘components/aotoo/core’)  
const lib = core.lib  
const hooks = lib.hooks  

// 实例
const hInst = hooks(‘hooks-name’) // 纯内存
// 或者
const hInst = hooks(‘hooks-name’, true) // 附带存储storage
//前者数据仅存储在内存
//后者在前者的基础上并通过wxstorage存储在本地

// api
// 数据部分
hInst.setItem(key, val)
hInst.getItem(key)
hInst.getInfo()  // 返回该实例所有存储的数据或所有storage数据
hInst.append(key, val) // 追加数据,仅限JSON/ARRAY类型数据可以被追加
hInst.delete(key) // 删除某条数据
hInst.clear() // 强力删除数据,并等待内存回收
hInst.destory()  // 只是清除内存数据,并等待内存回收

// 钩子部分
hInst.on(key, func)
hInst.off(key)
hInst.one(key, func)
hInst.once(key, func)
hInst.emit(key, param, context)

// 订阅发布部分
hInst.fire(key, param, context)

数据操作

日常数据操作,数据的增删改查

const core = require(‘components/aotoo/core’)  
const lib = core.lib  
const hooks = lib.hooks  
const hInst = hooks(‘hooks-name’, true) // 一个钩子实例,附带存储storage

// JSON类数据
hInst.setItem('one', {title: '标题'}) // 初始化数据
hInst.append('one', {name: '张三'})  // => {title, name}
hInst.append('one', '李四')  // => {title, name: '张三', rndKey: '李四'}
hInst.getItem('one') => // => {title, name}
hInst.delete('one') // => null

// 数组型
hInst.setItem('one', ['a']) // 初始化数据
hInst.append('one', {name: '张三'})  // => ['a', {name: '张三'}]
hInst.getItem('one') => // => ['a', {name: '张三'}]
hInst.delete('one') // => null

// 其他类型数据
// append无效
hInst.setItem('one', '你好') // 初始化数据
hInst.getItem('one') => // => 你好
hInst.delete('one') // => null

打开f12的application,可以看到数据被存进了storage

钩子

日常钩子操作,挂钩子方法,触发执行钩子方法,要注意的是所有钩子方法都是同步执行

const core = require(‘components/aotoo/core’)  
const lib = core.lib  
const hooks = lib.hooks  
const hInst = hooks('hooks-name', true) // 附带存储storage

// 恒有效的钩子方法
hInst.on('oneFun', function1) //挂载钩子方法
hInst.on('oneFun', function2)
hInst.on('oneFun', function3)

// 用完即删的方法,通过one来挂载
hInst.one('oneFun', function4)

// 只允许挂一个方法,最后进入的方法会被执行
hInst.once('twoFun', function5)
hInst.once('twoFun', function6)
hInst.once('twoFun', function7) // => 只有我才会被执行

hInst.emit('oneFun', {...}, context) // 无序遍历执行function1,2,3,4
hInst.emit('twoFun', {...}) // 只有function7被执行

emit方法只允许传递一个JSON类数据, context是钩子方法执行的上下文(默认为null)

订阅/发布

日常订阅系统操作

// 恒有效的钩子方法
hInst.on('oneFun', function1) //挂载钩子方法
hInst.on('oneFun', function2)
hInst.on('oneFun', function3)
hInst.one('oneFun', function4)

hInst.fire('oneFun', {...}, context) // 遍历function1,2,3,4,所有方法用完即删

fire方法只允许传递一个JSON类数据, context是钩子方法执行的上下文(默认为null)

源码

仅作为参看,因为aotoo有自己的运行环境

import {
  isString,
  isObject,
  isArray,
  isNumber,
  isFunction,
  formatQuery,
  suid,
  resetSuidCount,
} from './util'

class _hooks {
  constructor(props={}) {
    this.actions = {}
    this.storeData = {}
    this.storage = props.storage
  }
  destory() {
    this.actions = null
    this.storeData = null
    // wx.clearStorageSync()
  }
  getInfo(){
    return this.storage ? wx.getStorageInfoSync() : this.storeData
  }
  setItem(key, val){
    try {
      if (this.storage) {
        wx.setStorageSync(key, val)
      }
      this.storeData[key] = val
    } catch (error) {
      console.warn(error);
    }
  }
  getItem(key){
    try {
      let res
      if (this.storage) {
        res = wx.getStorageSync(key)
      }
      if (res) {
        this.storeData[key] = res
      }
      return res
    } catch (error) {
      console.warn(error);
    }
  }
  append(key, val){
    if (this.storeData[key]) {
      let sData = this.getItem(key)
      if (isArray(sData)) {
        sData = sData.concat(val)
      } else if(isObject(sData)) {
        if (isObject(val)) {
          sData = Object.assign(sData, val)
        } else {
          sData[suid('random_')] = val
        }
      } else {
        sData = val
      }
      this.setItem(key, sData)
    } else {
      this.setItem(key, val)
    }
  }
  delete(key){
    if (this.storage) {
      wx.removeStorageSync(key)
    }
    this.storeData[key] = null
  }
  clear(){
    this.destory()
    wx.clearStorageSync()
  }

  // ========= 下面为钩子方法 ===========
  on(key, cb) {
    let myActions = this.actions
    const hooksActionUniqId = suid('hooks_action_')
    if (cb) {
      cb['hooksActionUniqId'] = hooksActionUniqId
    }
    if (isString(key)) {
      if (myActions[key]) {
        myActions[key] = [].concat(myActions[key]).concat(cb)
      } else {
        myActions[key] = [cb]
      }
    }
  }
  off(key, fun) {
    if (isString(key)) {
      if (fun) {
        let hooksActionUniqId = fun.hooksActionUniqId
        if (hooksActionUniqId) {
          let theFuns = this.actions[key]
          let selectFunIndex
          if (theFuns) {
            theFuns.forEach(($f, ii) => {
              if ($f['hooksActionUniqId'] == hooksActionUniqId) {
                selectFunIndex = ii
              }
            })
            if (selectFunIndex) {
              theFuns.splice(selectFunIndex, 1)
            }
          }
        }
      } else {
        delete this.actions[key]
      }
    }
  }
  
  emit(key, param, ctx=null) {
    if (isString(key)) {
      if (this.actions[key]) {
        const vals = []
        const funs = this.actions[key]
        funs.forEach(fun => {
          if (isFunction(fun)) {
            const res = fun.call(ctx, param)
            if (res) vals.push(res) 
            else {
              vals.push(undefined)
            }
            if (fun.onlyonetime) {
              this.off(key, fun)
            }
            // vals.push(fun.call(ctx, param))
          }
        })
        if (vals.length) {
          return vals
        }
      }
    }
  }

  fire(key, param, ctx=null) {
    const vals = []
    function _fire(funcs=[]) {
      if (funcs.length) {
        const fun = funcs.shift()
        const res = fun.call(ctx, param)
        vals.push(res)
        _fire(funcs)
      } else {
        return vals
      }
    }

    if (isString(key) && this.actions[key]) {
      _fire(this.actions[key])
      if (vals.length) return vals
    }
  }

  one(key, cb) {
    if (key && typeof cb == 'function') {
      let mycb = function() { return cb.apply(this, arguments) }
      mycb.onlyonetime = true
    }
    this.on(key, cb)
  }

  once(key, cb) {
    let myActions = this.actions
    if (isString(key) && isFunction(cb)) {
      myActions[key] = [cb]
    }
  }
}

let myhooks = {}
export function hooks(idf, storage) {
  if (isString(idf)) {
    if (!myhooks[idf]) {
      myhooks[idf] = new _hooks({storage})
    }
    return myhooks[idf]
  }
}

aotoo架构,https://www.agzgz.com
安装aotoo,https://juejin.im/post/5d11ce33f265da1b60291108
小程序demo

点赞 1
收藏
评论
登录 后发表内容