SPage.js 8.9 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345
  1. // 引入事件模块 https://github.com/GravityC/SPage
  2. const SEvent = require('SEvent.js')
  3. wx.$event = {
  4. on(eventName, handler) {
  5. return SEvent.getEvent(eventName).on(handler)
  6. },
  7. once(eventName, handler) {
  8. SEvent.getEvent(eventName).once(handler)
  9. },
  10. emit(eventName) {
  11. const args = Array.from(arguments).slice(1)
  12. SEvent.getEvent(eventName).emit(...args)
  13. },
  14. off(nameOrId, handler) {
  15. if(handler && typeof (handler) === 'function'){
  16. SEvent.getEvent(nameOrId).off(handler)
  17. }else{
  18. SEvent.off(nameOrId)
  19. }
  20. },
  21. remove(eventName) {
  22. SEvent.removeEvent(eventName)
  23. }
  24. }
  25. // 引入request模块
  26. const SRequest = require('SRequest.js')
  27. const $http = new SRequest()
  28. wx.$http = $http
  29. wx.$request = $http.request.bind($http)
  30. wx.$get = $http.get.bind($http)
  31. wx.$post = $http.post.bind($http)
  32. wx.$put = $http.put.bind($http)
  33. wx.$delete = $http.delete.bind($http)
  34. /* ----------------------------------------------------------------------------- */
  35. wx.$pages = {}
  36. // 获取当前页面实例
  37. wx.$getCurPage = function() {
  38. return getCurrentPages()[getCurrentPages().length - 1]
  39. }
  40. // 获取当前页面实例对应的页面名
  41. wx.$getCurPageName = function() {
  42. return wx.$getCurPage().$name
  43. }
  44. // $place与$take,$take调用完即删除引用
  45. let channel = {}
  46. wx.$place = function(id, value) {
  47. channel[id] = value
  48. }
  49. wx.$take = function(id) {
  50. const v = channel[id]
  51. channel[id] = null
  52. return v
  53. }
  54. /**
  55. * 封装wx.navigateTo
  56. * @url {string} - 跳转路径
  57. * @query {object} - 携带参数
  58. */
  59. wx.$route = function(url, query) {
  60. const page = getPage(url)
  61. query = query || {}
  62. query.from = wx.$getCurPageName() // Page || Component
  63. // 若page已加载可调用onNavigate方法
  64. if (page && page.onNavigate) {
  65. page.onNavigate(query)
  66. }
  67. url = url + '?' + parseQuery(query)
  68. wx.navigateTo({
  69. url
  70. })
  71. }
  72. // -----------------------------以下是工具函数----------------------
  73. //装饰器
  74. //@afterOrigin {Boolean} - true:装饰函数在原函数之后触发
  75. const decorator = function(originFn, decorateFn, afterOrigin) {
  76. const origin = originFn
  77. originFn = function(args) {
  78. if (afterOrigin) {
  79. if (origin) origin.call(this, args)
  80. decorateFn.call(this, args)
  81. } else {
  82. decorateFn.call(this, args)
  83. if (origin) origin.call(this, args)
  84. }
  85. }
  86. return originFn
  87. }
  88. // 解析query对象成string
  89. const parseQuery = function(query) {
  90. return Object.keys(query).map(k => `${k}=${query[k]}`).join('&')
  91. }
  92. // 从url中得到pageName
  93. const getPageName = function(url) {
  94. url = url.includes('?') ? url.split('?')[0] : url
  95. const arr = url.split('/')
  96. return arr[arr.length - 1]
  97. }
  98. /**
  99. * @str - pageName或者url
  100. */
  101. const getPage = function(str) {
  102. const name = str.includes('/') ? getPageName(str) : str
  103. return wx.$pages[name]
  104. }
  105. // 判断是否是空对象
  106. const isEmpty = function(obj) {
  107. return !Object.keys(obj).length
  108. }
  109. /**
  110. * 扩展computed计算属性
  111. */
  112. const extendComputed = function(option) {
  113. let page
  114. // 为$setData方法响应computed
  115. option.$setData = function(obj) {
  116. this.setData(obj)
  117. page = this // 绑定page实例
  118. const needUpdate = calcComputed() // 将需要改变的computed属性添加到接下来要setData的对象中
  119. if (!isEmpty(needUpdate)) {
  120. this.setData(needUpdate)
  121. }
  122. }
  123. const computed = option.computed || {}
  124. const computedKeys = Object.keys(computed)
  125. let computedCache = {}
  126. // 计算需更改的计算属性
  127. const calcComputed = function(isInit) {
  128. const needUpdate = {}
  129. const that = isInit ? option : page
  130. for (const key of computedKeys) {
  131. const value = computed[key].call(that)
  132. if (value !== computedCache[key]) needUpdate[key] = computedCache[key] = value
  133. if (isInit) option.data[key] = needUpdate[key] // 初始化操作
  134. }
  135. return needUpdate
  136. }
  137. // 页面unload时清空computedCache
  138. option.onUnload = decorator(option.onUnload, function() {
  139. computedCache = {}
  140. })
  141. calcComputed(true);
  142. }
  143. /**
  144. * 为所有Page和Component扩展方法
  145. */
  146. const extendFunctions = function(option) {
  147. option.$route = wx.$route
  148. option.$place = wx.$place
  149. option.$take = wx.$take
  150. // 封装wx.request
  151. option.$request = wx.$request
  152. option.$get = wx.$get
  153. option.$post = wx.$post
  154. option.$put = wx.$put
  155. option.$delete = wx.$delete
  156. // 事件机制
  157. option.$on = function(eventName, handler){
  158. this.$listeners = this.$listeners || []
  159. this.$listeners.push(wx.$event.on(eventName, handler))
  160. }
  161. option.$once = wx.$event.once
  162. option.$emit = wx.$event.emit
  163. option.$off = wx.$event.off
  164. option.$remove = wx.$event.remove
  165. }
  166. /**
  167. * -------------扩展App-------------------
  168. */
  169. let initPageObject
  170. let initCompObject
  171. const extendApp = function() {
  172. const originApp = App
  173. App = function(option) {
  174. if (option.$mixinP) {
  175. initPageObject = option.$mixinP
  176. }
  177. if (option.$mixinC) {
  178. initCompObject = option.$mixinC
  179. }
  180. originApp(option)
  181. }
  182. }
  183. /**
  184. * -------------扩展Page----------------
  185. */
  186. const extendPage = function() {
  187. const originPage = Page
  188. Page = function(option) {
  189. // 混合app.$mixinP
  190. if (initPageObject) {
  191. const hooks = ['onLoad', 'onShow', 'onReady', 'onHide', 'onUnload', 'onPullDownRefresh', 'onReachBottom', 'onShareAppMessage', 'onPageScroll', 'onResize', 'onTabItemTap']
  192. for (const k in initPageObject) {
  193. if (hooks.includes(k)) {
  194. option[k] = decorator(option[k], initPageObject[k])
  195. } else if (k === 'data') {
  196. option.data = Object.assign({}, initPageObject.data, option.data)
  197. } else if (!option.hasOwnProperty(k)) {
  198. option[k] = initPageObject[k]
  199. }
  200. }
  201. }
  202. // 若定义了$name, 则添加到wx.$pages中
  203. if (option.$name) {
  204. wx.$pages[option.$name] = option
  205. } else {
  206. option.$name = 'unKnow' //此处强行给$name赋值,为了后续区分是Page还是Component
  207. }
  208. // 添加$status属性
  209. option.$status = {
  210. isFirstShow: true
  211. }
  212. // 是否已执行过onNavigate函数
  213. option.$$isNavigated = false
  214. // 扩展computed属性
  215. extendComputed(option)
  216. // 扩展方法
  217. extendFunctions(option)
  218. // 装饰onNavigate
  219. option.onNavigate = decorator(option.onNavigate, function(query) {
  220. option.$$isNavigated = true
  221. })
  222. // 装饰onLoad
  223. option.onLoad = decorator(option.onLoad, function(query) {
  224. // 若页面有onNavigate方法但还没运行,则运行onNavigate方法
  225. if (option.onNavigate && !option.$$isNavigated) {
  226. option.onNavigate(query)
  227. }
  228. })
  229. // 装饰onShow
  230. option.onShow = decorator(option.onShow, function() {
  231. this.$status.isFirstShow = false
  232. }, true)
  233. // 隐藏页面时隐藏标题栏加载动画(坑爹的微信官方bug)
  234. option.onHide = decorator(option.onHide, function() {
  235. wx.hideNavigationBarLoading()
  236. })
  237. // 装饰onUnload
  238. option.onUnload = decorator(option.onUnload, function(){
  239. // 卸载本页面的监听器
  240. if (this.$listeners && this.$listeners.length) {
  241. for (const id of this.$listeners) {
  242. wx.$event.off(id)
  243. }
  244. }
  245. })
  246. //原生Page构造
  247. originPage(option)
  248. }
  249. }
  250. /**
  251. * --------------------------------------------扩展Component
  252. */
  253. const extendComponent = function() {
  254. const originComponent = Component
  255. Component = function(option) {
  256. option.methods = option.methods || {}
  257. // 混合app.$mixinC
  258. if (initCompObject) {
  259. const mixinObj = ['properties', 'data', 'observers', 'methods', 'options']
  260. for (const k in initCompObject) {
  261. if (k === 'lifetimes' || k === 'pageLifetimes') {
  262. if (!option[k]) {
  263. option[k] = initCompObject[k]
  264. } else {
  265. for (const h in initCompObject[k]) {
  266. option[k][h] = decorator(option[k][h], initCompObject[k][h])
  267. }
  268. }
  269. } else if (mixinObj.includes(k)) {
  270. option[k] = Object.assign({}, initCompObject[k], option[k])
  271. } else if (!option.hasOwnProperty(k)) {
  272. option[k] = initCompObject[k]
  273. }
  274. }
  275. }
  276. // 扩展computed属性
  277. extendComputed(option)
  278. // 扩展方法
  279. extendFunctions(option.methods)
  280. option.lifetimes = option.lifetimes || {}
  281. // 装饰detached
  282. option.lifetimes.detached = decorator(option.lifetimes.detached, function () {
  283. // 卸载本组件的监听器
  284. if (this.$listeners && this.$listeners.length) {
  285. for (const id of this.$listeners) {
  286. wx.$event.off(id)
  287. }
  288. }
  289. })
  290. // 获取当前页面实例
  291. option.methods.$getCurPage = wx.$getCurPage
  292. // 获取当前页面实例对应的页面名
  293. option.methods.$getCurPageName = wx.$getCurPageName
  294. // 重新定义$setData,便于扩展其他功能
  295. const originData = option.$setData
  296. option.methods.$setData = function(obj) {
  297. return originData.call(this, obj)
  298. }
  299. //原生Component构造
  300. originComponent(option)
  301. }
  302. }
  303. module.exports = (function() {
  304. extendApp()
  305. extendPage()
  306. extendComponent()
  307. }())