收藏
评论

通过Mixins对小程序进行简单扩展


小程序开发有一段时间,在自定义组件之前,一直使用模板来实现模块化,但小程序对模块的逻辑是和页面绑定一起的,将这些模板的逻辑绑定到页面上,Mixins是比较合适的。还有就是要引入一些第三方库时,比如使用redux,使用全局混合就比较方便。


下面是我对Mixins的实现(Axe)


小程序引入Axe框架,只是简单的代理小程序的AppPage函数,引入Event事件和Mixins,完全不影响小程序自身的框架


混合两种方式

全局混合Axe.mixin

主要混入一些全局变量,如store

局部mixins

WPage({

 mixins:[{}]

})


定义新的页面入口,就是对原来的Page进行代理


}
export function WApp (options = {}) {
  if (app) return app
  options.$bus = bus
  app = new Axe(options, App)
  return app
}
 
export function WPage (options = {}) {
  options.$bus = bus
  let axe = new Axe(options, Page)
  axe.$root = app
  return axe

先看一下Axe类


import Event from '../event/index'
import { callHook } from './utils'
import {
  bindOptions,
  mergeOptions,
  pickOptions
} from './options'
 
let uid = 1
class Axe extends Event {
  constructor (options = {}, run) {
    super()
    // 上下文代指自己,主要给Event使用
    this._cxt = this
    // 是否是Page
    this._isPage = run === Page
    this._uid = uid++
    // 在app中执行了onLaunch,在page中执行了onReady
    this._ready = false
    // 在page中执行了onShow:_active=true, onHide:_active = false
    this._active = false
 
    let finalOptions = {}
    mergeOptions(finalOptions, Axe.options)
    mergeOptions(finalOptions, options)
 
    const wxOptions = bindOptions(this, finalOptions)
 
    let keys = Object.keys(this)
    callHook('Init', this)
    // 调用onInit钩子函数时,可能会在axe对象上绑定属性,需要同步到wxOptions上
    pickOptions(this, keys, wxOptions)
    run(wxOptions)
  }
 
  // 代理setData
  setData (data) {
    if (this.$cxt && this.$cxt.setData) {
      this.$cxt.setData(data)
      this.emit('axe:updated', data)
    } else if (process.env.NODE_ENV !== 'production') {
      console.log('[axe][setData]setData需要在onLoad后才可以使用')
    }
  }
 
  // 代理route
  get route () {
    if (this.$cxt) {
      return this.$cxt.route
    }
  }
}
 
export default Axe


Axe主要做了两件事,一是将全局选项和自定义选项扩展到Axe实例上,将最终的options提供给小程序的Page使用,二是代理

setData函数,需要注意的是,Page函数并没有返回一个this上下文,目前是在onLoad中才可以拿到。


其中比较重要的就是mergeOptions,合并选项的策略大致如下


data


在混合选项中的data必须是函数,会将结果进行深度合并


生命周期钩子函数

钩子函数会合并,不会覆盖,执行顺序

1:先执行全局混合,按照添加顺序执行

2: 执行模块的混合,按照mixins的顺序执行


其他options选项

直接替换,替换的顺序同生命周期钩子函数



在绑定最终的options时,需要注意在生命周期钩子函数绑定上下文

if (isHook(key)) {
  wxOptions[key] = axe[key] = function (...args) {
    // 绑定上下文
    axe.$cxt = this
    options[key].forEach((fn) => {
      fn.apply(axe, args)
    })
  }
}



简单实用,绑定redux


如在页面想这样使用redux,当数据更新时,数据自动setData

WPage({
  mapState (state) {
    return {
      isAdmin: state.isAdmin,
      unreadMessageCount: state.unreadMessageCount
    }
  }
})

可以通过全局混合来实现

import Axe from 'axe'
import {
  isPlainObject,
  shallowEqual
} from 'utils'
 
export function provider (store) {
  // 全局mixin
  Axe.mixin({
    $store: store,
    onLoad () {
      if (!this.onStateChange && !this.mapState) return
      this.$unsubscribe = this.$store.subscribe(() => {
        var state = this.$store.getState()
        listener(this, state)
      })
    },
 
    // 在页面进入前台时,将可能累计结果计算一遍
    onShow () {
      if (this._ready) {
        listener(this, this.$store.getState())
      }
    },
 
    onReady () {
      listener(this, this.$store.getState(), true)
    },
 
    onUnload () {
      if (this.$unsubscribe) {
        this.$unsubscribe()
      }
    }
  })
}
 
function listener (axe, state, init) {
  // 如果页面隐藏了不触发
  if (!axe._active) return
  if (axe.mapState) {
    var nextState = axe.mapState(state)
    if (isPlainObject(nextState)) {
      if (init || !shallowEqual(axe.state, nextState)) {
        axe.state = nextState
        axe.setData(axe.state)
      }
    }
  }
  if (axe.onStateChange) {
    axe.onStateChange(state)
  }
}




上面只是一些简单代码,这是完整代码

https://github.com/wanglei8381/wx-app-demo/tree/master/src/modules/axe

最后一次编辑于  2018-02-01  (未经腾讯允许,不得转载)
复制链接收藏赞 0