微信小程序开发中关于增加mixin扩展的详解

互联网 17-9-12
Mixin是一种思想,用部分实现的接口来实现代码复用。可以用来解决多继承的问题,又可以用来扩展功能。下面这篇文章主要给大家介绍了关于为微信小程序增加mixin扩展的相关资料,需要的朋友可以参考借鉴,下面来一起看看吧。

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

在前面,我们回顾了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;  };

小结

Page(createPage(configObj))

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

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

总结

以上就是微信小程序开发中关于增加mixin扩展的详解的详细内容,更多内容请关注技术你好其它相关文章!

来源链接:
免责声明:
1.资讯内容不构成投资建议,投资者应独立决策并自行承担风险
2.本文版权归属原作所有,仅代表作者本人观点,不代表本站的观点或立场
标签: 小程序
上一篇:php获取远程图片并下载保存到本地的方法分析 下一篇:微信小程序中自定义模态对话框的实例详解

相关资讯