首頁 >微信小程式 >小程式開發 >微信小程式開發中關於增加mixin擴充功能的詳解

微信小程式開發中關於增加mixin擴充功能的詳解

黄舟
黄舟原創
2017-09-12 11:27:252355瀏覽

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返回&#39;ios&#39;
 return &#39;ios&#39;;
};

export default {
 data() {
 return {
  platform: getPlatform()
 }
 }
}


// 在路由组件中使用
// views/index.vue
import platform from &#39;mixins/platform&#39;;

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


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

這樣,在index,detail兩個路由頁的viewModel上就都有platform這個值,可以直接在模板中使用。

vue中mixin分類

  • #datain

  • normal method mixin

  • lifecycle method mixin

#用程式碼表示的話,就如:


export default {
 data () {
 return {
  platform: &#39;ios&#39;
 }
 },
 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: &#39;MINA&#39;
 }
})

假如我們加入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: &#39;MINA&#39;
 }
})

這裡有兩個點,我們要特別注意:

  • 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 === &#39;function&#39;;
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 = [&#39;onLoad&#39;, &#39;onReady&#39;, &#39;onShow&#39;, &#39;onHide&#39;, &#39;onUnload&#39;, &#39;onPullDownRefresh&#39;, &#39;onReachBottom&#39;, &#39;onShareAppMessage&#39;];
const APP_EVENT = [&#39;onLaunch&#39;, &#39;onShow&#39;, &#39;onHide&#39;, &#39;onError&#39;];

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 === &#39;onLoad&#39;) return;

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

 // 提取lifecycle
 PAGE_EVENT.forEach(key => {
  if (isFunction(mixin[key]) && key !== &#39;onLoad&#39;) {
  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。

總結

#

以上是微信小程式開發中關於增加mixin擴充功能的詳解的詳細內容。更多資訊請關注PHP中文網其他相關文章!

陳述:
本文內容由網友自願投稿,版權歸原作者所有。本站不承擔相應的法律責任。如發現涉嫌抄襲或侵權的內容,請聯絡admin@php.cn