微信小程序 扩展Page页面的Mixins

前景:

在原生小程序的开发过程中,发现有很多页面使用了几乎完全一样的逻辑,例如:我们会遇到有部分功能需要登录之后才能访问,这个登录的逻辑就可以用封装成公共的逻辑,在需要的地方直接引用就可以了。

但是由于小程序官方并没有提供 Mixins 这种代码复用机制,所以只能用复制粘贴的方式去复用代码。随着功能越来越复杂,这样显得很不优雅,于是就想着怎么在小程序里面实现 Mixins。

什么是 Mixins

vue的官方定义:

混入 (mixin) 提供了一种非常灵活的方式,来分发 Vue 组件中的可复用功能。一个混入对象可以包含任意组件选项。当组件使用混入对象时,所有混入对象的选项将被"混合"进入该组件本身的选项。

vue中使用mixins

javascript 复制代码
// 定义一个混入对象
var myMixin = {
  created: function () {
    this.hello()
  },
  methods: {
    hello: function () {
      console.log('hello from mixin!')
    }
  }
}

// 定义一个使用混入对象的组件
var Component = Vue.extend({
  mixins: [myMixin]
})

var component = new Component() // => "hello from mixin!"
Mixins 的机制

1、当组件和混入对象含有同名选项时,比如,数据对象在内部会进行递归合并,并在发生冲突时以组件数据优先

javascript 复制代码
var mixin = {
  data: function () {
    return {
      message: 'hello',
      foo: 'abc'
    }
  }
}

new Vue({
  mixins: [mixin],
  data: function () {
    return {
      message: 'goodbye',
      bar: 'def'
    }
  },
  created: function () {
    console.log(this.$data)
    // => { message: "goodbye", foo: "abc", bar: "def" }
  }
})

2、同名钩子函数将合并为一个数组,因此都将被调用。另外,混入对象的钩子将在组件自身钩子之前调用。

javascript 复制代码
var mixin = {
  created: function () {
    console.log('混入对象的钩子被调用')
  }
}

new Vue({
  mixins: [mixin],
  created: function () {
    console.log('组件钩子被调用')
  }
})

// => "混入对象的钩子被调用"
// => "组件钩子被调用"

3、值为对象的选项,例如 methodscomponentsdirectives,将被合并为同一个对象。两个对象键名冲突时,取组件对象的键值对。

javascript 复制代码
var mixin = {
  methods: {
    foo: function () {
      console.log('foo')
    },
    conflicting: function () {
      console.log('from mixin')
    }
  }
}

var vm = new Vue({
  mixins: [mixin],
  methods: {
    bar: function () {
      console.log('bar')
    },
    conflicting: function () {
      console.log('from self')
    }
  }
})

vm.foo() // => "foo"
vm.bar() // => "bar"
vm.conflicting() // => "from self"

以上是vue中Mixins 的机制

在小程序中,这套机制会和 vue的有一些区别。

在小程序中,自定义的方法是直接定义在 Page 的属性当中的,既不属于生命周期类型属性,也不属于对象类型属性。

小程序的 Mixins 运行机制多加一条:

小程序中的自定义方法,优先级为 Page > Mixins;

即 Page 中的自定义方法会覆盖 Mixins 当中的。

小程序实现方法

在小程序中,每个页面都由 Page(options) 函数定义,而 Mixins 则作用于这个函数当中的 options 对象。

因此小程序实现 Mixins 的思路就有了------劫持并改写 Page 函数,最后再重新把它释放出来。

Mixins结构如下:

新建一个wx-mixins.js文件

javascript 复制代码
import { mergeOptions } from './merge-options';

// 保存原生的 Page 函数
const originPage = Page;

// 重新定义 Page
Page = (options) => {
  // 处理 mixins
  const mixins = options.mixins;
  // mixins 必须为数组
  if (Array.isArray(mixins)) {
    delete options.mixins;
    // mixins 注入并执行相应逻辑
    options = merge(mixins, options);
  }
  // 释放原生 Page 函数
  originPage(options)
}

新建一个merge-options.js文件

javascript 复制代码
/**
 * 扩展 Page 的 mixins 选项
 * mixins 同名字段的覆盖与合并规则
 * 同名生命周期函数都会被调用,混入对象的生命周期函数在页面生命周期函数之前被调用
 * data 对象在内部会进行浅合并,并在命名发生冲突时以页面数据优先
 * 混入对象的属性或方法命名冲突时,页面定义的属性或方法优先
 */
// Page 生命周期函数列表
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, hooks) {
  mixins.forEach(mixin => {
    if (!isPlainObject(mixin)) {
      throw new Error("typeof mixin must be plain object")
    }

    // 混入对象中嵌套混入对象,递归合并
    if (mixin.mixins) {
      mixin = mergeOptions(mixin.mixins, mixin, hooks)
    }

    // 处理混入对象中每一个值, 可能是生命周期函数、可能是 data 或 方法
    Object.getOwnPropertyNames(mixin).forEach(function (key) {
      // 暂存页面中初始的值
      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(this, arguments)
          // 后执行页面中的生命周期函数
          if (originValue) {
            res = originValue.apply(this, arguments)
          }
          return res
        }
      }
      // 混入对象的值是对象
      else if (isPlainObject(mixinValue)) {
        options[key] = {
          ...mixinValue,
          ...originValue
        }
      }
      // 混入对象的属性或方法
      else {
        // 页面中不存在同名的属性或方法时,才使用混入对象的属性或方法
        if (!hasOwn(options, key)) {
          options[key] = mixinValue
        }
      }
    })
  })

  return options
}
module.exports = {
  mergeOptions: mergeOptions
}
Mixins 使用

1.在小程序的 app.js 里引入 mixins.js

javascript 复制代码
import './mixins/wx-mixins';

2.撰写一个 myMixin.js

javascript 复制代码
// 全局app实例
const app = getApp();

export default {
   data: {},
   /**
   * 生命周期函数--监听页面加载
   */
  onLoad: function () {
    
  },
  /**
   * 生命周期函数--监听页面初次渲染完成
   */
  onReady: function () {

  },

  /**
   * 生命周期函数--监听页面显示
   */
  onShow: function () {

    
  },

  /**
   * 生命周期函数--监听页面隐藏
   */
  onHide: function () {

  },

  /**
   * 生命周期函数--监听页面卸载
   */
  onUnload: function () {

  },

  /**
   * 页面相关事件处理函数--监听用户下拉动作
   */
  onPullDownRefresh: function () {

  },

  /**
   * 页面上拉触底事件的处理函数
   */
  onReachBottom: function () {

  },

  /**
   * 用户点击右上角分享
   */
  onShareAppMessage: function () {

  }
}

3.在 page/index/index.js 中使用

javascript 复制代码
Page({
  mixins: [require('../../myMixin.js')]
})
相关推荐
VXbishe23 分钟前
(附源码)基于springboot的“我来找房”微信小程序的设计与实现-计算机毕设 23157
java·python·微信小程序·node.js·c#·php·课程设计
大三觉醒push亡羊补牢女娲补天版2 小时前
微信小程序常见问题
微信小程序·小程序
漏刻有时3 小时前
微信小程序学习实录9:掌握wx.chooseMedia实现多图片文件上传功能(选择图片、预览图片、上传图片)
学习·微信小程序·notepad++
hmz8563 小时前
最新网课搜题答案查询小程序源码/题库多接口微信小程序源码+自带流量主
前端·微信小程序·小程序
小雨cc5566ru6 小时前
uniapp+Android智慧居家养老服务平台 0fjae微信小程序
android·微信小程序·uni-app
计算机学姐7 小时前
基于微信小程序的调查问卷管理系统
java·vue.js·spring boot·mysql·微信小程序·小程序·mybatis
正小安14 小时前
如何在微信小程序中实现分包加载和预下载
前端·微信小程序·小程序
说私域14 小时前
基于定制开发与2+1链动模式的商城小程序搭建策略
大数据·小程序
丁总学Java20 小时前
微信小程序-npm支持-如何使用npm包
前端·微信小程序·npm·node.js
工业互联网专业21 小时前
毕业设计选题:基于ssm+vue+uniapp的校园水电费管理小程序
vue.js·小程序·uni-app·毕业设计·ssm·源码·课程设计