评论

论函数复用的几大姿势

当遇到多个页面需要相同数据或函数时,要如何处理呢,本文将总结归纳五个常见方法。

开发过小程序的朋友们应该都遇到这样的情况,可能很多个页面有相同的函数,例如onShareAppMessage,有什么最佳实践吗,应该如何处理呢?

本次开发技巧,我从以下几种解决办法剖析:

  1. 将它复制粘贴到每个地方(最烂的做法)
  2. 抽象成一个公共函数,每个Page都手动引用
  3. 提取一个behavior,每个页面手动注入
  4. 通过Page封装一个新的newPage,以后每个页面都通过newPage注册
  5. 劫持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: {}
})

这样的缺点也是非常明显的:

  1. 创建新页面时,容易遗忘
  2. 如果多个相同的函数,则需要每个独立引入,不方便

提取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})
}

通过这种方式,不改变页面的注册方式,但可能会让不了解底层封装的开发者感到困惑:明明没注册的方法,怎么就自动注入了呢?

这种方式的缺点已经说了,优点也很明显,不改变任何原有的页面注册方式。

其实这个是一个挺好的思路,在一些特定的场景下,会有事半功倍的效果。

最后一次编辑于  2020-03-23  
点赞 11
收藏
评论

4 个评论

  • Admin²⁰²¹
    Admin²⁰²¹
    2020-08-07
    楼主思路很好,写得也用心,受益匪浅,点赞
    2020-08-07
    赞同 1
    回复
  • 呦…咔嘣
    呦…咔嘣
    2020-05-19

    这个必须赞,对于CommonJs的require来说,各种相对路径简直难受。

    最后的这条Page拦截太有用了,以前居然不知道可以在app.js里对Page进行相关的改动。

    希望顶上去更多的人能看到,我觉得这个技巧太实用。也有可能大佬们都知道的,只有我这个萌新不知道。

    2020-05-19
    赞同 1
    回复 1
    • Admin²⁰²¹
      Admin²⁰²¹
      2020-08-07
      这个思路非常好。初学者必备的。点赞
      2020-08-07
      1
      回复
  • UP
    UP
    2020-12-25

    楼主,劫持那个没法传参数算是缺点吗,好像函数的话无法传参啊,只能劫持data,或者不用传参数的这种

    2020-12-25
    赞同
    回复 3
    • LeeJim🌀
      LeeJim🌀
      2020-12-28
      可以传参的吧?
      2020-12-28
      回复
    • UP
      UP
      2020-12-28回复LeeJim🌀
      就拿你这个onShareAppMessage为例子,很多时候这个分享的标题,路径,图片都是拿这个页面的某个接口的数据,都是title: this.data.detail.title  ,大概是这个意思。如果用了劫持,我不知道参数要怎么传,我看你都是写死的。
      2020-12-28
      回复
    • LeeJim🌀
      LeeJim🌀
      2020-12-29回复UP
      参数不一样的话,就需要每个页面独自调用onShareAppMessage,传入不同的参数。不能复用了
      2020-12-29
      回复
  • 年华 错落成诗
    年华 错落成诗
    2020-11-11

    我想已同样的方法劫持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))
    }
    
    2020-11-11
    赞同
    回复
登录 后发表内容