computedBehavior.js 4.9 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145
  1. /**
  2. * weapp custom component extend behavior -- computed
  3. * https://github.com/wechat-miniprogram/computed
  4. */
  5. import isEmpty from './isEmpty'
  6. import shallowEqual from './shallowEqual'
  7. module.exports = Behavior({
  8. lifetimes: {
  9. created () {
  10. this._computedCache = {}
  11. this._originalSetData = this.setData
  12. this.setData = this._setData
  13. this._doingSetData = false
  14. },
  15. },
  16. definitionFilter (defFields) {
  17. const computed = defFields.computed || {}
  18. const computedKeys = Object.keys(computed)
  19. // 计算 computed
  20. const calcComputed = (scope) => {
  21. const needUpdate = {}
  22. const computedCache = scope._computedCache || scope.data
  23. for (let i = 0, len = computedKeys.length; i < len; i++) {
  24. const key = computedKeys[i]
  25. const getter = computed[key]
  26. if (typeof getter === 'function') {
  27. const value = getter.call(scope)
  28. if (!shallowEqual(computedCache[key], value)) {
  29. needUpdate[key] = value
  30. computedCache[key] = value
  31. }
  32. }
  33. }
  34. return needUpdate
  35. }
  36. // 初始化 computed
  37. const initComputed = () => {
  38. defFields.data = defFields.data || {}
  39. // 先将 properties 里的字段写入到 data 中
  40. const data = defFields.data
  41. const properties = defFields.properties
  42. const hasOwnProperty = Object.prototype.hasOwnProperty
  43. if (properties) {
  44. // eslint-disable-next-line complexity
  45. Object.keys(properties).forEach((key) => {
  46. const value = properties[key]
  47. let oldObserver
  48. // eslint-disable-next-line max-len
  49. if (value === null || value === Number || value === String || value === Boolean || value === Object || value === Array) {
  50. properties[key] = {
  51. type: value,
  52. }
  53. } else if (typeof value === 'object') {
  54. if (hasOwnProperty.call(value, 'value')) {
  55. // 处理值
  56. data[key] = value.value
  57. }
  58. if (hasOwnProperty.call(value, 'observer')) {
  59. if (typeof value.observer === 'function') {
  60. oldObserver = value.observer
  61. } else if (typeof value.observer === 'string') {
  62. oldObserver = defFields.methods[value.observer]
  63. }
  64. }
  65. }
  66. // 追加 observer,用于监听变动
  67. properties[key].observer = function (...args) {
  68. const originalSetData = this._originalSetData
  69. if (this._doingSetData) {
  70. // eslint-disable-next-line no-console
  71. console.warn('can\'t call setData in computed getter function!')
  72. return
  73. }
  74. this._doingSetData = true
  75. // 计算 computed
  76. const needUpdate = calcComputed(this)
  77. // 做 computed 属性的 setData
  78. if (!isEmpty(needUpdate)) {
  79. originalSetData.call(this, needUpdate)
  80. }
  81. this._doingSetData = false
  82. if (oldObserver) oldObserver.apply(this, args)
  83. }
  84. })
  85. }
  86. // 计算 computed
  87. calcComputed(defFields, true)
  88. }
  89. initComputed()
  90. defFields.methods = defFields.methods || {}
  91. defFields.methods._setData = function (data, callback) {
  92. const originalSetData = this._originalSetData
  93. if (this._doingSetData) {
  94. // eslint-disable-next-line no-console
  95. console.warn('can\'t call setData in computed getter function!')
  96. return
  97. }
  98. this._doingSetData = true
  99. // TODO 过滤掉 data 中的 computed 字段
  100. const dataKeys = Object.keys(data)
  101. for (let i = 0, len = dataKeys.length; i < len; i++) {
  102. const key = dataKeys[i]
  103. if (computed[key]) delete data[key]
  104. }
  105. // 做 data 属性的 setData
  106. originalSetData.call(this, data, callback)
  107. // 计算 computed
  108. const needUpdate = calcComputed(this)
  109. // 做 computed 属性的 setData
  110. if (!isEmpty(needUpdate)) {
  111. originalSetData.call(this, needUpdate)
  112. }
  113. this._doingSetData = false
  114. }
  115. },
  116. })