当前位置: 移动技术网 > IT编程>开发语言>JavaScript > 微信小程序开发教程之增加mixin扩展

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

2017年12月12日  | 移动技术网IT编程  | 我要评论
mixin简介 mixin(织入)模式并不是gof的《设计模式》归纳中的一种,但是在各种语言以及框架都会发现该模式(或者思想)的一些应用。简单来说,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;
};

小结

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

page(createpage(configobj))

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

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

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

总结

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

如您对本文有疑问或者有任何想说的,请点击进行留言回复,万千网友为您解惑!

相关文章:

验证码:
移动技术网