# 如何写一个插件

# 构思插件的功能

  import BScroll from '@better-scroll/core'
  import MyPlugin from '@better-scroll/my-plugin'

  BScroll.use(MyPlugin)

  const bs = new BScroll('.wrapper', {
    myPlugin: {
      scrollText: 'I am scrolling',
      scrollEndText: 'Scroll has ended'
    },
    // 或者
    myPlugin: true
  })

  // 使用插件暴露到 bs 的事件
  bs.on('printScrollEndText', (scrollEndText) => {
    console.log(scrollEndText) // 打印 "Scroll has ended, position is (xx, yy)"
  })

  // 使用插件代理到 bs 实例上的方法
  bs.printScrollText() // 打印 "I am scrolling"

# 编写插件

  1. TypeScript 声明合并以及暴露插件方法
import BScroll from '@better-scroll/core'

export type MyPluginOptions = Partial<MyPluginConfig> | true

type MyPluginConfig = {
  scrollText: string,
  scrollEndText: string
}

interface PluginAPI {
  printScrollText(): void
}

declare module '@better-scroll/core' {
  interface CustomOptions {
    myPlugin?: MyPluginOptions
  }

  interface CustomAPI {
    myPlugin: PluginAPI
  }
}

这样做的好处,就是为了在引入 myPlugin 插件并且实例化 BetterScroll 的时候,能够有对应的 Options 提示以及 bs 能够有对应的方法提示,以 pulldown 插件为例:

  1. 编写插件主体

    • BetterScroll 的插件需要是一个类,并且具有以下特性:

      • 静态的 pluginName 属性。
      • 实现 PluginAPI 接口(当且仅当需要把插件方法代理至 bs)。
      • constructor 的第一个参数就是 BetterScroll 实例 bs,你可以通过 bs 的事件或者钩子来注入自己的逻辑。
        export default class MyPlugin implements PluginAPI {
          static pluginName = 'myPlugin'
          public options: MyPluginConfig
          constructor(public scroll: BScroll){
            this.handleOptions()
      
            this.handleBScroll()
      
            this.registerHooks()
          }
        }
      
    • handleOptions

      合并用户传入的 options,收缩它的类型。

        import { extend } from '@better-scroll/shared-utils'
        export default class MyPlugin {
          private handleOptions() {
            const userOptions = (this.scroll.options.myPlugin === true
              ? {}
              : this.scroll.options.myPlugin) as Partial<MyPluginConfig>
            const defaultOptions: MyPluginConfig = {
              scrollText: 'I am scrolling',
              scrollEndText: 'Scroll has ended'
            }
            this.options = extend(defaultOptions, userOptions)
          }
        }
      
    • handleBScroll

      代理事件以及方法至 BetterScroll 实例。

        export default class MyPlugin implements PluginAPI {
          private handleBScroll() {
            const propertiesConfig = [
              {
                key: 'printScrollText',
                sourceKey: 'plugins.myPlugin.printScrollText'
              }
            ]
            // 将 myPlugin.printScrollText 代理至 bs.printScrollText
            this.scroll.proxy(propertiesConfig)
            // 注册 printScrollEndText 事件至 bs,以至于用户可以通过 bs.on('printScrollEndText', handler) 来订阅事件
            this.scroll.registerType(['printScrollEndText'])
          }
      
          printScrollText() {
            console.log(this.options.scrollText)
          }
        }
      
    • registerHooks

      钩入 bs 的钩子,实现插件的逻辑,并且派发插件自定义的事件。

        export default class MyPlugin implements PluginAPI {
          private registerHooks() {
            const scroll = this.scroll
            scroll.on(scroll.eventTypes.scrollEnd, ({ x, y }) => {
              scroll.trigger(
                scroll.eventTypes.printScrollEndText,
                `${this.options.scrollEndText}, position is (${x}, ${y})`
              )
            })
          }
        }
      

恭喜你,一个简单的 BetterScroll 插件就已经完成啦,如果结合你的场景,需要更复杂的插件,可以仔细阅读 事件与钩子大全,它能很好的帮助你来完成一款独特的插件。

查看完整的 repo,以及线上例子