observer.ts 6.2 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202
  1. /** @format */
  2. import {autorun, IReactionDisposer, spy} from 'mobx'
  3. import * as mobx from 'mobx'
  4. if (cc.sys.isBrowser) {
  5. ;(window as any).mobx = mobx
  6. }
  7. const getComponentOrNode = <T extends cc.Component>(
  8. target: cc.Component,
  9. kv: Map<string, string>,
  10. component: {prototype: T} | null,
  11. componentStr?: string,
  12. componentCb?: Function,
  13. ) => {
  14. let lastV: string
  15. kv.forEach((v, k) => {
  16. if (lastV && lastV == v) {
  17. console.error(component ? component.prototype.constructor.name : 'node', '注解错误有相同的url', lastV)
  18. }
  19. lastV = v
  20. if (component) {
  21. target[k] = cc.find(v, target.node)?.getComponent(component)
  22. } else if (componentStr) {
  23. target[k] = cc.find(v, target.node)?.getComponent(componentStr)
  24. } else {
  25. target[k] = cc.find(v, target.node)
  26. }
  27. componentCb && componentCb(target[k])
  28. })
  29. }
  30. export const observer = <T extends {new (...args: any[]): cc.Component}>(constructor: T) => {
  31. return class extends constructor {
  32. _disposer: IReactionDisposer[] = []
  33. _reaction?: string[]
  34. _autorun?: string[]
  35. _nodes?: Map<string, string>
  36. _labels?: Map<string, string>
  37. _editBoxes?: Map<string, string>
  38. _lists?: Map<string, string>
  39. _toggles?: Map<string, string>
  40. onLoad() {
  41. if (this._nodes) {
  42. getComponentOrNode(this, this._nodes, null)
  43. }
  44. if (this._labels) {
  45. getComponentOrNode(this, this._labels, cc.Label)
  46. }
  47. if (this._editBoxes) {
  48. getComponentOrNode(this, this._editBoxes, cc.EditBox)
  49. }
  50. if (this._lists) {
  51. getComponentOrNode(this, this._lists, null, 'List', component => {
  52. component._init()
  53. })
  54. }
  55. if (this._toggles) {
  56. getComponentOrNode(this, this._toggles, cc.Toggle)
  57. }
  58. super.onLoad && super.onLoad()
  59. if (this._autorun) {
  60. let callback = x => {
  61. this[x]()
  62. }
  63. this._disposer.push(
  64. ...this._autorun.map(x =>
  65. autorun(() => {
  66. callback(x)
  67. }),
  68. ),
  69. )
  70. }
  71. if (this._reaction) {
  72. this._disposer.push(
  73. ...this._reaction.map(x => {
  74. return (this as any)[x]()
  75. }),
  76. )
  77. }
  78. }
  79. onEnable() {
  80. super.onEnable && super.onEnable()
  81. if (this._autorun) {
  82. let callback = x => {
  83. this[x]()
  84. }
  85. this._autorun.forEach(x => callback(x))
  86. }
  87. }
  88. onDestroy() {
  89. super.onDestroy && super.onDestroy()
  90. if (this._disposer) this._disposer.forEach(x => x())
  91. this._disposer.length = 0
  92. }
  93. }
  94. }
  95. export const render = (target: any, key: string, descriptor: TypedPropertyDescriptor<() => void>) => {
  96. let obs: string[] = target['_autorun']
  97. if (!obs) {
  98. obs = target['_autorun'] = []
  99. }
  100. obs.push(key)
  101. }
  102. interface Reactor {
  103. (target: any, key: string, descriptor: TypedPropertyDescriptor<() => IReactionDisposer>): void
  104. <T>(expression: (r: mobx.IReactionPublic) => T): (
  105. target: any,
  106. key: string,
  107. descriptor: TypedPropertyDescriptor<(arg: T) => void>,
  108. ) => void
  109. }
  110. export const reactor: Reactor = function () {
  111. if (arguments.length === 3) {
  112. return reactor1(arguments[0], arguments[1], arguments[2])
  113. } else {
  114. return reactor2(arguments[0])
  115. }
  116. } as any
  117. const reactor1 = (target: any, key: string, descriptor: TypedPropertyDescriptor<() => IReactionDisposer>) => {
  118. let obs: string[] = target['_reaction']
  119. if (!obs) {
  120. obs = target['_reaction'] = []
  121. }
  122. obs.push(key)
  123. }
  124. const reactor2 = <T, O extends cc.Component & {_reaction?: string[]}>(expression: (r: mobx.IReactionPublic) => T) => {
  125. return (target: O, key: string, descriptor: TypedPropertyDescriptor<(arg: T) => void>) => {
  126. let obs = target['_reaction']
  127. if (!obs) {
  128. obs = target['_reaction'] = []
  129. }
  130. obs.push(key)
  131. const _value = descriptor.value as (arg: T) => void
  132. descriptor.value = function (this: O) {
  133. return mobx.reaction(expression.bind(this), _value.bind(this))
  134. }
  135. }
  136. }
  137. /**
  138. * 和reactor搭配进行副作用操作
  139. */
  140. export const react = <T>(
  141. expression: (r: mobx.IReactionPublic) => T,
  142. effect: (arg: T, r: mobx.IReactionPublic) => void,
  143. ) => {
  144. return mobx.reaction(expression, effect, {fireImmediately: true})
  145. }
  146. export const node = (arg: string) => {
  147. return (target: any, propertyKey: string) => {
  148. let nodes: Map<string, string> = target['_nodes']
  149. if (!nodes) {
  150. nodes = target['_nodes'] = new Map<string, string>()
  151. }
  152. nodes.set(propertyKey, arg)
  153. }
  154. }
  155. export const label = (arg: string) => {
  156. return (target: any, propertyKey: string) => {
  157. let labels: Map<string, string> = target['_labels']
  158. if (!labels) {
  159. labels = target['_labels'] = new Map<string, string>()
  160. }
  161. labels.set(propertyKey, arg)
  162. }
  163. }
  164. export const editBox = (arg: string) => {
  165. return (target: any, propertyKey: string) => {
  166. let editBoxes: Map<string, string> = target['_editBoxes']
  167. if (!editBoxes) {
  168. editBoxes = target['_editBoxes'] = new Map<string, string>()
  169. }
  170. editBoxes.set(propertyKey, arg)
  171. }
  172. }
  173. export const list = (arg: string) => {
  174. return (target: any, propertyKey: string) => {
  175. let list: Map<string, string> = target['_lists']
  176. if (!list) {
  177. list = target['_lists'] = new Map<string, string>()
  178. }
  179. list.set(propertyKey, arg)
  180. }
  181. }
  182. export const toggle = (arg: string) => {
  183. return (target: any, propertyKey: string) => {
  184. let toggle: Map<string, string> = target['_toggles']
  185. if (!toggle) {
  186. toggle = target['_toggles'] = new Map<string, string>()
  187. }
  188. toggle.set(propertyKey, arg)
  189. }
  190. }