开发过小程序的朋友们应该都遇到这样的情况,可能很多个页面有相同的函数,例如onShareAppMessage
,有什么最佳实践吗,应该如何处理呢?
本次开发技巧,我从以下几种解决办法剖析:
- 将它复制粘贴到每个地方(最烂的做法)
- 抽象成一个公共函数,每个
Page
都手动引用 - 提取一个behavior,每个页面手动注入
- 通过
Page
封装一个新的newPage
,以后每个页面都通过newPage
注册 - 劫持Page函数,注入预设方法,页面仍可使用
Page
注册
复制粘贴大法
这是最直观,也是初学者最常用到的办法。也是作为工程师最不应该采取的办法。这有一个致命的问题,如果某一天,需要改动这个函数,岂不是要将所有的地方都翻出来改,所以这个办法直接否决。
抽象公共函数
这种方式,解决了复制粘贴大法的致命问题,不需要改动很多地方,只需要改动这个抽象出来的函数即可。但是其实,这个方式不便捷,每次新增页面都需要手动引入这个函数。
以下都通过onShareAppMessage
方法举例。
假设在app.js
通过global
注册了onShareAppMessage
方法:
// app.js
global.onShareAppMessage = function() {
return {
title: '我在这里发现了很多好看的壁纸',
path: 'pages/index/index',
imageUrl: ''
}
}
那么此时每次新增的Page都需要这样引入:
// page.js
Page({
...global.onShareAppMessage,
data: {}
})
这样的缺点也是非常明显的:
- 创建新页面时,容易遗忘
- 如果多个相同的函数,则需要每个独立引入,不方便
提取Behavior
将多个函数集成到一个对象中,每个页面只需要引入这个对象即可注入多个相同的函数。这种方式可以解决 抽象公共函数 提到的 缺点2。
大致的实现方式如下:
同样在app.js
通过global
注册一个behavior
对象:
// app.js
global.commonPage = {
onShareAppMessage: function() {
return {
title: '我在这里发现了很多好看的壁纸',
path: 'pages/index/index',
imageUrl: ''
}
},
onHide: function() {
// do something
}
}
在新增的页面注入:
// page.js
Page({
data: {},
...global.commonPage,
}})
缺点仍然是,新增页面时容易遗忘
封装新Page
封装新的Page
,然后每个页面都通过这个新的Page
注册,而不是采用原有的Page
。
同理,在app.js
先封装一个新的Page
到全局变量global
:
// app.js
global.newPage = function(obj) {
let defaultSet = {
onShareAppMessage: function() {
return {
title: '我在这里发现了很多好看的壁纸',
path: 'pages/index/index',
imageUrl: ''
}
},
onShow() {
// do something
}
}
return Page({...defaultSet, ...obj})
}
往后在每个页面都使用新的newPage
注册:
// page.js
global.newPage({
data: {}
})
好处即是全新封装了Page
,后续只需关注是否使用了新的Page
即可;此外大家也很清晰知道这个是采用了新的封装,避免了覆盖原有的Page
方法。
我倒是觉得没什么明显缺点,要是非要鸡蛋里挑骨头的话,就是要显式调用新的函数注册页面。
劫持Page
劫持函数其实是挺危险的做法,因为开发人员可能会在定位问题时,忽略了这个被劫持的地方。
劫持Page
的做法,简单的说就是,覆盖Page
这个函数,重新实现Page
,但这个新的Page
内部仍会调用原有的Page
。说起来可能有点拗口,通过代码看就一目了然:
// app.js
let originalPage = Page
Page = function(obj) {
let defaultSet = {
onShareAppMessage: function() {
return {
title: '我在这里发现了很多好看的壁纸',
path: 'pages/index/index',
imageUrl: ''
}
},
onShow() {
// do something
}
}
return originalPage({ ...defaultSet, ...obj})
}
通过这种方式,不改变页面的注册方式,但可能会让不了解底层封装的开发者感到困惑:明明没注册的方法,怎么就自动注入了呢?
这种方式的缺点已经说了,优点也很明显,不改变任何原有的页面注册方式。
其实这个是一个挺好的思路,在一些特定的场景下,会有事半功倍的效果。
这个必须赞,对于CommonJs的require来说,各种相对路径简直难受。
最后的这条Page拦截太有用了,以前居然不知道可以在app.js里对Page进行相关的改动。
希望顶上去更多的人能看到,我觉得这个技巧太实用。也有可能大佬们都知道的,只有我这个萌新不知道。
又学到了
今天在学习Behavior,其实用这个方案就已经可以拿到80分了,谢谢。
楼主,劫持那个没法传参数算是缺点吗,好像函数的话无法传参啊,只能劫持data,或者不用传参数的这种
我想已同样的方法劫持Component ,不知到为什么不行,您能看看
const defaultSet={ data:{ theme:theme }, // 设置加载中的状态 tool setLoading(loading, state) { const key = `loading.${loading}` // this.setData({ // [key]: state ? true : false // }) } } const that = this let originalPage = Page Page = function(obj) { return originalPage(util.deepMerge(defaultSet,obj)) } let originalComponent = Component Component = function(obj) { return originalComponent(util.deepMerge(defaultSet,obj)) }