微信小程序开发教程之增加mixin扩展


Posted in Javascript onAugust 09, 2017

Mixin简介

Mixin(织入)模式并不是GOF的《设计模式》归纳中的一种,但是在各种语言以及框架都会发现该模式(或者思想)的一些应用。简单来说,Mixin是带有全部实现或者部分实现的接口,其主要作用是更好的代码复用。

Mixin这个概念在React, Vue中都有支持,它为我们抽象业务逻辑,代码复用提供了方便。然而小程序原生框架并没直接支持Mixin。我们先看一个很实际的需求:

为所有小程序页面增加运行环境class,以方便做一些样式hack。具体说就是小程序在不同的运行环境(开发者工具|iOS|Android)运行时,platform值为对应的运行环境值("ios|android|devtools")

<view class="{{platform}}">
 <!--页面模板-->
</view>

回顾vue中mixin的使用

文章开始提到的问题是非常适合使用Mixin来解决的。我们把这个需求转换成一个Vue问题:在每个路由页面中增加一个platform的样式class(虽然这样做可能没实际意义)。实现思路就是为每个路由组件增加一个data: platform。代码实现如下:

// mixins/platform.js
const getPlatform = () => {
 // 具体实现略,这里mock返回'ios'
 return 'ios';
};

export default {
 data() {
 return {
  platform: getPlatform()
 }
 }
}
// 在路由组件中使用
// views/index.vue
import platform from 'mixins/platform';

export default {
 mixins: [platform],
 // ...
}
// 在路由组件中使用
// views/detail.vue
import platform from 'mixins/platform';

export default {
 mixins: [platform],
 // ...
}

这样,在index,detail两个路由页的viewModel上就都有platform这个值,可以直接在模板中使用。

vue中mixin分类

  • data mixin
  • normal method mixin
  • lifecycle method mixin

用代码表示的话,就如:

export default {
 data () {
 return {
  platform: 'ios'
 }
 },

 methods: {
 sayHello () {
  console.log(`hello!`)
 }
 },

 created () {
 console.log(`lifecycle3`)
 }
}

vue中mixin合并,执行策略

如果mixin间出现了重复,这些mixin会有具体的合并,执行策略。如下图:

微信小程序开发教程之增加mixin扩展

如何让小程序支持mixin

在前面,我们回顾了vue中mixin的相关知识。现在我们要让小程序也支持mixin,实现vue中一样的mixin功能。

实现思路

我们先看一下官方的小程序页面注册方式:

Page({
 data: {
 text: "This is page data."
 },
 onLoad: function(options) {
 // Do some initialize when page load.
 },
 onReady: function() {
 // Do something when page ready.
 },
 onShow: function() {
 // Do something when page show.
 },
 onHide: function() {
 // Do something when page hide.
 },
 onUnload: function() {
 // Do something when page close.
 },
 customData: {
 hi: 'MINA'
 }
})

假如我们加入mixin配置,上面的官方注册方式会变成:

Page({
 mixins: [platform],
 data: {
 text: "This is page data."
 },
 onLoad: function(options) {
 // Do some initialize when page load.
 },
 onReady: function() {
 // Do something when page ready.
 },
 onShow: function() {
 // Do something when page show.
 },
 onHide: function() {
 // Do something when page hide.
 },
 onUnload: function() {
 // Do something when page close.
 },
 customData: {
 hi: 'MINA'
 }
})

这里有两个点,我们要特别注意:

  • Page(configObj) - 通过configObj配置小程序页面的data, method, lifecycle等
  • onLoad方法 - 页面main入口

要想让mixin中的定义有效,就要在configObj正式传给Page()之前做文章。其实Page(configObj)就是一个普通的函数调用,我们加个中间方法:

Page(createPage(configObj))

在createPage这个方法中,我们可以预处理configObj中的mixin,把其中的配置按正确的方式合并到configObj上,最后交给Page() 。这就是实现mixin的思路。

具体实现

具体代码实现就不再赘述,可以看下面的代码。更详细的代码实现,更多扩展,测试可以参看github

/**
 * 为每个页面提供mixin,page invoke桥接
 */

const isArray = v => Array.isArray(v);
const isFunction = v => typeof v === 'function';
const noop = function () {};

// 借鉴redux https://github.com/reactjs/redux
function compose(...funcs) {
 if (funcs.length === 0) {
 return arg => arg;
 }

 if (funcs.length === 1) {
 return funcs[0];
 }

 const last = funcs[funcs.length - 1];
 const rest = funcs.slice(0, -1);
 return (...args) => rest.reduceRight((composed, f) => f(composed), last(...args));
}


// 页面堆栈
const pagesStack = getApp().$pagesStack;

const PAGE_EVENT = ['onLoad', 'onReady', 'onShow', 'onHide', 'onUnload', 'onPullDownRefresh', 'onReachBottom', 'onShareAppMessage'];
const APP_EVENT = ['onLaunch', 'onShow', 'onHide', 'onError'];

const onLoad = function (opts) {
 // 把pageModel放入页面堆栈
 pagesStack.addPage(this);

 this.$invoke = (pagePath, methodName, ...args) => {
 pagesStack.invoke(pagePath, methodName, ...args);
 };

 this.onBeforeLoad(opts);
 this.onNativeLoad(opts);
 this.onAfterLoad(opts);
};

const getMixinData = mixins => {
 let ret = {};

 mixins.forEach(mixin => {
 let { data={} } = mixin;

 Object.keys(data).forEach(key => {
  ret[key] = data[key];
 });
 });

 return ret;
};

const getMixinMethods = mixins => {
 let ret = {};

 mixins.forEach(mixin => {
 let { methods={} } = mixin;

 // 提取methods
 Object.keys(methods).forEach(key => {
  if (isFunction(methods[key])) {
  // mixin中的onLoad方法会被丢弃
  if (key === 'onLoad') return;

  ret[key] = methods[key];
  }
 });

 // 提取lifecycle
 PAGE_EVENT.forEach(key => {
  if (isFunction(mixin[key]) && key !== 'onLoad') {
  if (ret[key]) {
   // 多个mixin有相同lifecycle时,将方法转为数组存储
   ret[key] = ret[key].concat(mixin[key]);
  } else {
   ret[key] = [mixin[key]];
  }
  }
 })
 });

 return ret;
};

/**
 * 重复冲突处理借鉴vue:
 * data, methods会合并,组件自身具有最高优先级,其次mixins中后配置的mixin优先级较高
 * lifecycle不会合并。先顺序执行mixins中的lifecycle,再执行组件自身的lifecycle
 */

const mixData = (minxinData, nativeData) => {
 Object.keys(minxinData).forEach(key => {
 // page中定义的data不会被覆盖
 if (nativeData[key] === undefined) {
  nativeData[key] = minxinData[key];
 }
 });

 return nativeData;
};

const mixMethods = (mixinMethods, pageConf) => {
 Object.keys(mixinMethods).forEach(key => {
 // lifecycle方法
 if (PAGE_EVENT.includes(key)) {
  let methodsList = mixinMethods[key];

  if (isFunction(pageConf[key])) {
  methodsList.push(pageConf[key]);
  }

  pageConf[key] = (function () {
  return function (...args) {
   compose(...methodsList.reverse().map(f => f.bind(this)))(...args);
  };
  })();
 }

 // 普通方法
 else {
  if (pageConf[key] == null) {
  pageConf[key] = mixinMethods[key];
  }
 }
 });

 return pageConf;
};

export default pageConf => {

 let {
 mixins = [],
 onBeforeLoad = noop,
 onAfterLoad = noop
 } = pageConf;

 let onNativeLoad = pageConf.onLoad || noop;
 let nativeData = pageConf.data || {};

 let minxinData = getMixinData(mixins);
 let mixinMethods = getMixinMethods(mixins);

 Object.assign(pageConf, {
 data: mixData(minxinData, nativeData),
 onLoad,
 onBeforeLoad,
 onAfterLoad,
 onNativeLoad,
 });

 pageConf = mixMethods(mixinMethods, pageConf);

 return pageConf;
};

小结

1、本文主要讲了如何为小程序增加mixin支持。实现思路为:预处理configObj

Page(createPage(configObj))

2、在处理mixin重复时,与vue保持一致:

      data, methods会合并,组件自身具有最高优先级,其次mixins中后配置的mixin优先级较高。

      lifecycle不会合并。先顺序执行mixins中的lifecycle,再执行组件自身的lifecycle。

总结

好了,以上就是这篇文章的全部内容了,希望本文的内容对大家的学习或者工作能带来一定的帮助,如果有疑问大家可以留言交流,谢谢大家对三水点靠木的支持。

Javascript 相关文章推荐
如何判断图片地址是否失效
Feb 02 Javascript
广告切换效果(缓动切换)
May 27 Javascript
Web表单提交之disabled问题js解决方法
Jan 13 Javascript
JQuery显示、隐藏div的几种方法简明总结
Apr 16 Javascript
BootstrapValidator不触发校验的实现代码
Sep 28 Javascript
jQuery实现表格与ckeckbox的全选与单选功能
Nov 24 Javascript
AngularJS学习第二篇 AngularJS依赖注入
Feb 13 Javascript
深入理解vue Render函数
Jul 19 Javascript
200行代码实现blockchain 区块链实例详解
Mar 14 Javascript
vue2中引用及使用 better-scroll的方法详解
Nov 15 Javascript
JS使用iView的Dropdown实现一个右键菜单
May 06 Javascript
js常用方法、检查是否有特殊字符串、倒序截取字符串操作完整示例
Jan 26 Javascript
javascript编程开发中取色器及封装$函数用法示例
Aug 09 #Javascript
vuejs父子组件之间数据交互详解
Aug 09 #Javascript
Angular在模板驱动表单中自定义校验器的方法
Aug 09 #Javascript
浅谈react+es6+webpack的基础配置
Aug 09 #Javascript
js中less常用的方法小结
Aug 09 #Javascript
利用JS做网页特效_大图轮播(实例讲解)
Aug 09 #Javascript
基于Vue实例对象的数据选项
Aug 09 #Javascript
You might like
PHP中使用localhost连接Mysql不成功的解决方法
2014/08/20 PHP
php绘制一个扇形的方法
2015/01/24 PHP
php实现的微信红包算法分析(非官方)
2015/09/25 PHP
Yii2实现ajax上传图片插件用法
2016/04/28 PHP
利用php获得flv视频长度的实例代码
2017/10/26 PHP
使javascript也能包含文件
2006/10/26 Javascript
IE与FireFox的兼容性问题分析
2007/04/22 Javascript
探讨JavaScript中声明全局变量三种方式的异同
2013/12/03 Javascript
返回上一页并自动刷新的JavaScript代码
2014/02/19 Javascript
删除javascript中注释语句的正则表达式
2014/06/11 Javascript
判断window.onload是否多次使用的方法
2014/09/21 Javascript
Javascript中的几种URL编码方法比较
2015/01/23 Javascript
JQuery页面地址处理插件jqURL详解
2015/05/03 Javascript
JS组件Bootstrap实现弹出框和提示框效果代码
2015/12/08 Javascript
jQuery Validate表单验证插件的基本使用方法及功能拓展
2017/01/04 Javascript
Vue 实用分页paging实例代码
2017/04/12 Javascript
Javascript之图片的延迟加载的实例详解
2017/07/24 Javascript
基于React+Redux的SSR实现方法
2018/07/03 Javascript
在微信小程序中渲染HTML内容3种解决方案及分析与问题解决
2020/01/12 Javascript
React实现轮播效果
2020/08/25 Javascript
JavaScript浅层克隆与深度克隆示例详解
2020/09/01 Javascript
nuxt 路由、过渡特效、中间件的实现代码
2020/11/06 Javascript
获取python的list中含有重复值的index方法
2018/06/27 Python
Pandas:Series和DataFrame删除指定轴上数据的方法
2018/11/10 Python
python实现将汉字保存成文本的方法
2018/11/16 Python
pandas 选取行和列数据的方法详解
2019/08/08 Python
python简单利用字典破解zip文件口令
2020/09/07 Python
Python如何使用vars返回对象的属性列表
2020/10/17 Python
《唯一的听众》教学反思
2014/02/20 职场文书
考核评语大全
2014/04/29 职场文书
文秘班元旦晚会活动策划方案
2014/08/28 职场文书
专题民主生活会对照检查材料思想汇报
2014/09/29 职场文书
2014年实验室工作总结
2014/12/03 职场文书
新员工实习期个人工作总结
2015/10/15 职场文书
AJAX实现指定部分页面刷新效果
2021/10/16 Javascript