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

server/2024/9/23 10:30:29/
前景:

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

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

什么是 Mixins

vue的官方定义:

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

vue中使用mixins

// 定义一个混入对象
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、当组件和混入对象含有同名选项时,比如,数据对象在内部会进行递归合并,并在发生冲突时以组件数据优先

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、同名钩子函数将合并为一个数组,因此都将被调用。另外,混入对象的钩子将在组件自身钩子之前调用。

var mixin = {created: function () {console.log('混入对象的钩子被调用')}
}new Vue({mixins: [mixin],created: function () {console.log('组件钩子被调用')}
})// => "混入对象的钩子被调用"
// => "组件钩子被调用"

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

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文件

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

 新建一个merge-options.js文件

/*** 扩展 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.toStringfunction 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

import './mixins/wx-mixins';

2.撰写一个 myMixin.js

// 全局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 中使用

Page({mixins: [require('../../myMixin.js')]
})


http://www.ppmy.cn/server/21207.html

相关文章

vivado学习笔记——ROM IP核调用

ROM为只读存储器。可以从任意地址上读取数据,但是不能写入。所以ROM中的数据,就需要提前存放进去。在IP核中,可以通过.coe文件进行数据存放,文件格式可以参考Xilinx官方标准。 使用matlab生成.coe文件,数据为正弦波波形…

【Elasticsearch<三>✈️✈️】常见基本属性的用法以及与MySQL的区别

目录 🍸前言 🍻一、索引 1.1 映射关系 (mappings) 1.2 映射关系查看 🍺二、文档 2.1 删除文档 2.2 更新文档 2.3 批量操作 🍹三、高级查询(Query 检索) 🍷四、章末 🍸前言 继本…

stm32_HAL_串口不定长数据接收发送

串口代码思路//不包括初始化 变量 uint8_t tx1[256];//缓冲区uint8_t Tx1_size0;//记录数据的个数 接收 产生第一个数据接收-----进入中断----复制数据-----继续接收----在进入中断----复制数据---在接收直到串口第一次数据全部接收完 中断源码 利用中断经行计算字符个数…

MySQL数据库总结

作者:爱塔居-CSDN博客 专栏:数据库 目录 前言 一、数据库操作 1.1 创建数据库 1.2 显示当前数据库 1.3 使用数据库 1.4 删除数据库 二、表的操作 2.1 查看表结构 2.2 创建表 2.3 删除表 三、表的增删改查操作(CRUD) 3.1 新增 3.…

Linux中文件描述符与重定向的深入探索

目录 1. 理解C语言的文件操作函数 2. 操作系统的文件操作接口 3. 文件描述符详解和其内核本质 4. 如何理解Linux下一切皆文件 5. Linux中的重定向 5.1 输出重定向 5.2 追加重定向 5.3 输入重定向 6. 结合文件描述符理解重定向 7.重定向的系统调用 在Linux操作系统中&a…

面试逻辑题,有8个小球和一个天平,一个小球偏重,其它小球一样重,问最少称几次可以找出那个重球?

1. 问题描述 现在有8个小球和一个天平,已知其中1个小球偏重,其余小球重量相等。问:最少称几次一定可以找出那个重量更大的小球? 2. 解题思路 第一次称重:将任意三个小球放在天平的左边,将另外三个小球放…

ChatGPT助力测试领域!探索人工智能编写测试用例的新前景

简介 测试用例是测试人员的核心工作内容,是测试人员思想的“实现类”,其充分体现了测试的思路,可以为后续的测试行为提供指导,是测试人员了解业务的重要根据和质量之根本。如果测试用例设计得不完成,出现了遗漏&#x…

B站无限评论暴力截留协议及教程

B站无限评论暴力截留协议及教程 B站无限评论暴力截留协议及教程,需要抓CK ,教程里面有讲如何抓取 网盘自动获取 链接:https://pan.baidu.com/s/1lpzKPim76qettahxvxtjaQ?pwd0b8x 提取码:0b8x