component.js 11 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411
  1. const utils = require('../../api/utils')
  2. const { warnLife, fnAppClass } = utils
  3. const Relations = require('../../api/relations')
  4. const config = require('../../api/config.js')
  5. const {
  6. createSelectorQuery,
  7. createIntersectionObserver,
  8. } = require('../../api/my')
  9. const {
  10. getUrl,
  11. updateData,
  12. processRelationPath,
  13. _relationNode,
  14. findRelationNode,
  15. compatibleLifetime,
  16. collectObserver,
  17. collectObservers,
  18. processTriggerEvent,
  19. observerHandle,
  20. handleProps,
  21. handleExternalClasses,
  22. handleAfterInit,
  23. mergeOptions,
  24. copy,
  25. nextUid,
  26. } = require('../utils')
  27. const SelectComponent = require('./selectComponent')
  28. const processRelationHandle = require('./processRelation')
  29. const createNode = require('./relation')
  30. const { antmoveAction } = require('./utils')
  31. function getInfo(key, obj) {
  32. let val = {}
  33. Object.keys(obj).forEach((item) => {
  34. if (key === item || key.indexOf(item) !== -1) {
  35. val = obj[item]
  36. }
  37. })
  38. return val
  39. }
  40. function processRelations(ctx, relationInfo = {}) {
  41. let route = ctx.is
  42. route = route.replace(/\/node_modules\/[a-z-]+\/[a-z-]+/, '')
  43. if (route[0] === '/') {
  44. route = route.substring(1)
  45. }
  46. const info = getInfo(route, relationInfo)
  47. if (info) {
  48. processRelationHandle(info, (node) => {
  49. if (node.$id === 'saveChildRef0') {
  50. ctx[node.$id] = function() {}
  51. node.$index = 0
  52. node.$route = route
  53. createNode.call(ctx, ctx, null, node)
  54. return false
  55. }
  56. ctx[node.$id] = function(ref) {
  57. ctx.$antmove = ctx.$antmove || {}
  58. if (ctx.$antmove[node.$id] === undefined) {
  59. ctx.$antmove[node.$id] = 0
  60. } else {
  61. ctx.$antmove[node.$id] += 1
  62. }
  63. this.selectComponentApp.preProcesscomponents(ref)
  64. node.$index = ctx.$antmove[node.$id]
  65. node.$route = route
  66. createNode.call(ctx, ref, null, node)
  67. }
  68. })
  69. } else {
  70. console.warn('Missing nodes relation of ', route)
  71. }
  72. }
  73. function handleRelations() {
  74. if (this.props.theRelations) {
  75. Object.keys(this.props.theRelations).forEach((relation) => {
  76. const _p = processRelationPath(this, relation)
  77. const relationInfo = this.props.theRelations[relation]
  78. let nodes = null
  79. if (relationInfo.type === 'child' || relationInfo.type === 'descendant') {
  80. return false
  81. }
  82. nodes = findRelationNode(this.$node, _p, relationInfo.type, true)
  83. if (!nodes || nodes[0] === undefined) {
  84. return false
  85. }
  86. nodes.forEach((n) => {
  87. if (!n) {
  88. // console.error('wrong relation reference of ', relationInfo);
  89. // console.error('from: ', this.$node.$self.is, 'to: ', _p);
  90. return false
  91. }
  92. _relationNode.call(this, n, {
  93. relationInfo,
  94. _p,
  95. relation,
  96. })
  97. })
  98. })
  99. }
  100. }
  101. function processObservers(observersObj, options, param) {
  102. if (options.observers) {
  103. collectObservers.call(this, observersObj, options, param)
  104. }
  105. }
  106. function processInit() {
  107. getUrl()
  108. }
  109. function processIntersectionObserver(context) {
  110. context.createIntersectionObserver = function(...p) {
  111. return createIntersectionObserver.fn(...p)
  112. }
  113. }
  114. /**
  115. *
  116. * @param {*} behavior
  117. * @param {*} _opts
  118. * @param {*} mixins
  119. */
  120. module.exports = {
  121. processTransformationComponent(_opts, options) {
  122. const fnApp = fnAppClass()
  123. options.properties = options.properties || {}
  124. const behaviors = options.behaviors || []
  125. const mixins = options.mixins || []
  126. const _export = options.export || ''
  127. delete options.behaviors
  128. delete options.mixins
  129. const retMixins = {}
  130. _opts.observerObj = {}
  131. _opts.observersObj = {}
  132. _opts.behaviorsArr = []
  133. processBehavior(retMixins, behaviors, _opts.behaviorsArr)
  134. processBehavior(retMixins, mixins, _opts.behaviorsArr)
  135. mergeOptions(retMixins, options)
  136. processBehaviorId(behaviors)
  137. processBehaviorId(mixins)
  138. Object.keys(options).forEach((key) => {
  139. _opts[key] = options[key]
  140. })
  141. handleProps(_opts)
  142. handleExternalClasses(_opts)
  143. const _life = compatibleLifetime(options)
  144. if (options.properties) {
  145. collectObserver(_opts.observerObj, options.properties, options)
  146. }
  147. if (!_opts.methods) {
  148. _opts.methods = {}
  149. }
  150. _opts.methods.antmoveAction = antmoveAction
  151. /**
  152. * 处理组件所在的页面尺寸变化时执行
  153. */
  154. if (_opts.pageLifetimes && _opts.pageLifetimes.resize) {
  155. _opts.methods.antmovePageLifetimes = function(e) {
  156. return _opts.pageLifetimes.resize(e)
  157. }
  158. }
  159. const didMount = function() {
  160. _life.error && warnLife('There is no error life cycle', 'error')
  161. _life.move && warnLife('There is no moved life cycle', 'moved')
  162. _life.pageLifetimes
  163. && warnLife(
  164. 'There is no page life cycle where the component resides,including(show,hide,resize)',
  165. 'pageLifetimes',
  166. )
  167. this.props.genericSelectable
  168. && warnLife('generic:selectable is Unsupported', 'generic')
  169. // process relations, get relation ast
  170. const relationAst = createNode.call(this, null, null, null, null, true)
  171. .mountedHandles
  172. relationAst.push(() => {
  173. handleRelations.call(this)
  174. })
  175. }
  176. fnApp.add('onInit', function() {
  177. this.onPageReady = function(p) {
  178. _opts.onPageReady && _opts.onPageReady.call(this, p)
  179. }
  180. })
  181. fnApp.add('deriveDataFromProps', () => {})
  182. fnApp.add('didMount', didMount)
  183. if (_opts.lifetimes && _opts.lifetimes.created) {
  184. fnApp.add('onInit', _opts.lifetimes.created)
  185. } else {
  186. fnApp.add('onInit', _opts.created)
  187. }
  188. fnApp.insert('onInit', function() {
  189. this.__wxExparserNodeId__ = nextUid()
  190. processIntersectionObserver(this)
  191. this.createSelectorQuery = function() {
  192. if (config.env !== 'production') {
  193. console.warn(
  194. '支付宝createSelectorQuery不支持限定选择器的选择范围,如使用,请保证对应选择器使用的唯一性',
  195. )
  196. }
  197. return createSelectorQuery.fn()
  198. }
  199. for (const method in this) {
  200. if (typeof this[method] === 'function') {
  201. this[method] = this[method].bind(this)
  202. }
  203. }
  204. this.getRelationNodes = function() {
  205. return []
  206. }
  207. processComponentExport(_export, behaviors, this)
  208. this.selectComponentApp = new SelectComponent(this)
  209. this.properties = {
  210. ..._opts.properties,
  211. }
  212. processInit.call(this, _opts, options, _life, fnApp)
  213. testBehaviors(behaviors)
  214. updateData.call(this)
  215. processRelations(this, Relations)
  216. this.selectComponentApp.connect()
  217. this.selectOwnerComponent = processSelectOwnerComponent.bind(this)
  218. this.getPageId = processGetPageId.bind(this)
  219. addAntmoveData.call(this)
  220. if (typeof this.triggerEvent !== 'function') {
  221. processTriggerEvent.call(this)
  222. }
  223. processObservers.call(this, _opts.observersObj, options, _opts)
  224. observerHandle(_opts.observerObj, _opts, this, true)
  225. })
  226. fnApp.bind('onInit', _opts)
  227. if (_opts.lifetimes && _opts.lifetimes.attached) {
  228. fnApp.add('didMount', _opts.lifetimes.attached)
  229. } else {
  230. fnApp.add('didMount', _opts.attached)
  231. }
  232. if (_opts.pageLifetimes && _opts.pageLifetimes.show) {
  233. fnApp.add('didMount', _opts.pageLifetimes.show)
  234. }
  235. fnApp.add('didMount', _opts.ready || (_opts.lifetimes && _opts.lifetimes.ready))
  236. const didUpdate = function(...param) {
  237. updateData.call(this, param)
  238. processObservers.call(
  239. this,
  240. _opts.observersObj,
  241. options,
  242. this.$antmove._data,
  243. )
  244. observerHandle(_opts.observerObj, this.$antmove._data, this)
  245. addAntmoveData.call(this)
  246. }
  247. fnApp.add('didUpdate', didUpdate)
  248. fnApp.add('didUpdate', function() {
  249. handleAfterInit.call(this)
  250. })
  251. fnApp.bind('deriveDataFromProps', _opts)
  252. fnApp.bind('didUpdate', _opts)
  253. fnApp.bind('didMount', _opts)
  254. if (_opts.lifetimes && _opts.lifetimes.detached) {
  255. fnApp.add('didUnmount', _opts.lifetimes.detached)
  256. } else {
  257. fnApp.add('didUnmount', options.detached)
  258. }
  259. fnApp.add('didUnmount', function() {
  260. // todo: 暂时这样处理使其不报错
  261. if (this.$node && this.$node.$parent) {
  262. this.$node.$parent.removeChild(this.$node)
  263. const refId = this.$node.$relationNode.$id
  264. this.$antmove[refId]--
  265. }
  266. })
  267. fnApp.bind('didUnmount', _opts)
  268. },
  269. }
  270. function addAntmoveData() {
  271. const _data = [{}, {}]
  272. const ctx = this
  273. const _props = {}
  274. for (const i in ctx.properties) {
  275. if (ctx.properties.hasOwnProperty(i)) {
  276. _props[i] = ctx.data[i]
  277. }
  278. }
  279. _data[0] = copy(_props)
  280. _data[1] = copy(ctx.data)
  281. this.$antmove = this.$antmove || {}
  282. this.$antmove._data = _data
  283. }
  284. /**
  285. * selectOwnerComponent
  286. */
  287. function processSelectOwnerComponent() {
  288. const node = this.$node
  289. if (node && node.$parent && node.$parent.$self) {
  290. return node.$parent.$self
  291. }
  292. return {}
  293. }
  294. /**
  295. * getPageId
  296. */
  297. function processGetPageId() {
  298. if (this.$page) {
  299. return `pageId:${this.$page.$id}`
  300. }
  301. return 'pageId: undefined'
  302. }
  303. /**
  304. * behavior
  305. */
  306. function processBehavior(_opts = {}, opts, $behaviors) {
  307. const self = this
  308. if (Array.isArray(opts)) {
  309. opts.forEach((item) => {
  310. if (
  311. typeof item === 'object'
  312. && ($behaviors.indexOf(item.$id) === -1 || item.$id === undefined)
  313. ) {
  314. $behaviors.push(item.$id)
  315. _process.call(self, _opts, item)
  316. }
  317. })
  318. } else if (typeof opts === 'object' && $behaviors.indexOf(opts.$id) === -1) {
  319. $behaviors.push(opts.$id)
  320. _process.call(self, _opts, opts)
  321. }
  322. function _process(__opts = {}, opt = {}) {
  323. if (opt.behaviors) {
  324. processBehavior.call(self, __opts, opt.behaviors, $behaviors)
  325. delete opt.behaviors
  326. }
  327. if (opt.mixins) {
  328. processBehavior(__opts, opt.mixins, $behaviors)
  329. delete opt.mixins
  330. }
  331. mergeOptions(opt, __opts)
  332. }
  333. }
  334. function processBehaviorId(behavior) {
  335. if (Array.isArray(behavior)) {
  336. behavior.forEach((item) => {
  337. if (typeof item === 'object' && item.$id) {
  338. delete item.$id
  339. }
  340. })
  341. } else if (typeof behavior === 'object' && behavior.$id) {
  342. delete behavior.$id
  343. }
  344. }
  345. function processComponentExport(_export, behaviors, self) {
  346. if (typeof _export !== 'function') {
  347. return
  348. }
  349. if (Array.isArray(behaviors)) {
  350. behaviors.forEach((bhv) => {
  351. if (bhv === 'wx://component-export') {
  352. self._this = _export()
  353. }
  354. })
  355. } else if (behaviors === 'wx://component-export') {
  356. self._this = _export()
  357. }
  358. }
  359. function testBehaviors(behaviors) {
  360. if (Array.isArray(behaviors)) {
  361. behaviors.forEach((bhv) => {
  362. if (bhv === 'wx://form-field') {
  363. warnLife(
  364. 'Wx://form-field in built-in behavior is not supported',
  365. 'behavior/form-field',
  366. )
  367. }
  368. })
  369. } else if (behaviors === 'wx://form-field') {
  370. warnLife(
  371. 'Wx://form-field in built-in behavior is not supported',
  372. 'behavior/form-field',
  373. )
  374. }
  375. }