Loading...
墨滴

qqf

2021/09/25  阅读:34  主题:WeChat-Format

在微信小程序中使用 mixins

Vue 提供了一种非常灵活的混入(mixins)方式,用于分发 Vue 组件中可复用的功能。微信小程序的 Component 使用 behaviors 可以实现类似 Vue 中的 mixins。

Page 创建页面不支持 behaviors 或 mixins,怎么在不同页面实现代码复用与共享呢?

有两种方式:

  • 使用 Component 创建页面
  • 自行扩展 Page 的 mixins 选项

使用 Component 创建页面

页面本身就可以看做是组件,将 Page 构造器直接替换为 Component 创建页面,可以使用 behaviors 实现组件逻辑的复用。

Component 构造器的主要区别是页面的方法需要定义在methods: {}中。

my-behavior.js

export default Behavior({
  data: {
    userAge25
  },
  createdfunction ({
    console.log('mixin created')
  },
  methods: {
    printOutUserAge () {
      console.log('mixin data userAge:'this.data.userAge)
    }
  }

使用 Behavior 来包装混入对象。

index.js


import myBehavior from "./my-behavior"

Component({
  behaviors: [myBehavior],
  data: {
    name'ejtoia'
  },

  createdfunction ({
    console.log('Page create')
    this.printOutUserName()
  },

  methods: {
    printOutUserName() {
      this.printOutUserAge()
      console.log(this.data.name)
    },
    onShareAppMessagefunction ({
      // 页面被用户分享时执行
    },
    onReachBottomfunction({
      // 页面触底执行
    }
  }
})

index 页面 log 依次输出为:

mixin created
Page created
mixin data userAge:25
ejtoia

Index 页面的 onShareAppMessageonReachBottom 等等方法都定义在 Component 构造器中的 methods 对象中。

behaviors 的同名字段的覆盖和组合规则查阅:

  • https://developers.weixin.qq.com/miniprogram/dev/framework/custom-component/behaviors.html

扩展 Page 的 mixins 选项

原理很好理解。先保存原始的 Page 构造器函数,然后重新定义新的 Page 构造器函数。

这相当于加入一个中间层函数,用来处理 options 选项对象的 mixins 值,把 mixins 中的每一个混入对象(包括生命周期函数、data、方法)按照一定的规则合并到 options 选项对象中,最后再调用原始 Page(options) 构造器函数创建页面。

wx-mixins.js 入口文件:

// 保存原始的 Page
const originPage = Page

// 重新定义 Page
Page = options => {
  // 处理 mixins
  const mixins = options.mixins
  if (Array.isArray(mixins)) {
    options = mergeOptions(mixins, options)
    delete options.mixins
  }
  // 执行原始的 Page
  originPage(options)
}

将 mixins 中每一个混入对象都合并到 options 选项对象中,在开始 mergeOptions 函数之前,先规定好混入的规则。

mixins 同名字段的覆盖与合并规则

  • 同名生命周期函数都会被调用,混入对象的生命周期函数在页面生命周期函数之前被调用

  • data 对象在内部会进行浅合并,并在命名发生冲突时以页面数据优先

  • 属性或方法命名冲突时,页面定义的属性或方法优先

mergeOptions 实现

const pageHooks = [
  "onLoad",
  "onShow",
  "onReady",
  "onHide",
  "onUnload",
  "onPullDownRefresh",
  "onReachBottom",
  "onShareAppMessage",
  "onPageScroll",
  "onResize",
  "onTabItemTap"
]

const hasOwnProperty = Object.prototype.hasOwnProperty;
const toString = Object.prototype.toString

function toRawType(value{
  return toString.call(value).slice(8-1);
}

function isPlainObject(value{
  return toRawType(value) === "Object";
}

function isFunction(value{
  return toRawType(value) === "Function";
}

function hasOwn(obj, key{
  return hasOwnProperty.call(obj, key);
}


function mergeOptions (mixins, options{
  mixins.forEach(mixin => {
    if (!isPlainObject(mixin)) {
      throw new Error("typeof mixin must be plain object"
    }

    // 支持混入对象中嵌套混入对象,递归处理
    if (mixin.mixins) {
      mixin = mergeOptions(mixin.mixins, mixin, hooks)
    }

    // 处理混入对象的每一个值, 可能是生命周期函数、可能是对象、也可能是方法
    for (const key in mixin) {
      // 暂存页面中原始的值
      const originValue = options[key]
      // 暂存混入对象的值
      const mixinValue = mixin[key]

      // 处理混入对象的生命周期函数
      if (pageHooks.includes(key)) {
        if (!isFunction(mixinValue)) {
          throw new Error(`typeof ${key} must be function`)
        }
        // 重写页面中对应的生命周期函数
        options[key] = function ({
          let res;
          // 先执行混入对象的生命周期函数
          res = mixinValue.apply(thisarguments)
          // 页面中定义了同名的生命周期函数,后执行
          if (originValue) res = originValue.apply(thisarguments)
          return res
        }
      }
      // 处理混入对象,值可能是 data
      else if (isPlainObject(mixinValue)) {
        options[key] = {
          ...mixinValue,
          ...originValue
        }
      }
      // 处理混入对象的属性或方法
      else {
        // 页面中不存在同名的属性或方法,才使用混入对象的属性或方法
        if (!hasOwn(options, key)) {
          options[key] = mixinValue
        }
      }
    }
  })

  return options
}

使用 mixins

小程序入口文件 app.js 中引入 wx-mixins.js 扩展的实现后,那么所有使用 Page 创建的页面都可以使用 mixins 选项。

app.js

import "./wx-mixins"

my-mixins.js

export default {
  data: {
    userAge: 25
  },
  onLoad: function () {
    console.log('mixin onLoad')
  },
  printOutUserAge () {
    console.log('mixin data userAge:', this.data.userAge)
  }

默认导出一个混入对象,包含多个页面共用的生命周期、data 和方法。

index.js

import myMixin from "./my-mixins"

Page({
  mixins: [index_mixins],
  data: {
    name'ejtoia'
  },

  onLoadfunction ({
    console.log('Page onLoad')
    this.printOutUserName()
  },

  printOutUserName() {
    this.printOutUserAge()
    console.log(this.data.name)
  },

  onShareAppMessagefunction ({
    // 页面被用户分享时执行
  },
  onReachBottomfunction({
    // 页面触底执行
  }
})

Index 页面使用混入对象后的 log 输出结果:

mixin onLoad
Page onLoad
mixin data userAge:25
ejtoia

mixins 混入对象相比 Component 创建页面,可以直接使用 Page 创建页面,不需要将属性或方法定义到 methods: {} 中。

总结

在微信小程序中 Page 使用比较频繁,为了方便页面的逻辑代码复用,扩展 Page 的 mixins 选项,它最核心的实现是 mergeOptions 合并函数。

也可以给 App、Component 构造器函数也扩展 mixins 选项,因为 App 使用不多,Component 已经内置了 behaviors 选项,所以这两个扩展 mixins 的意义不是很大。

参考

  • https://github.com/kallsave/wx-miniapp-mixins
  • https://cn.vuejs.org/v2/guide/mixins.html
  • https://developers.weixin.qq.com/miniprogram/dev/framework/custom-component/behaviors.html

qqf

2021/09/25  阅读:34  主题:WeChat-Format

作者介绍

qqf