最新文章专题视频专题问答1问答10问答100问答1000问答2000关键字专题1关键字专题50关键字专题500关键字专题1500TAG最新视频文章推荐1 推荐3 推荐5 推荐7 推荐9 推荐11 推荐13 推荐15 推荐17 推荐19 推荐21 推荐23 推荐25 推荐27 推荐29 推荐31 推荐33 推荐35 推荐37视频文章20视频文章30视频文章40视频文章50视频文章60 视频文章70视频文章80视频文章90视频文章100视频文章120视频文章140 视频2关键字专题关键字专题tag2tag3文章专题文章专题2文章索引1文章索引2文章索引3文章索引4文章索引5123456789101112131415文章专题3
问答文章1 问答文章501 问答文章1001 问答文章1501 问答文章2001 问答文章2501 问答文章3001 问答文章3501 问答文章4001 问答文章4501 问答文章5001 问答文章5501 问答文章6001 问答文章6501 问答文章7001 问答文章7501 问答文章8001 问答文章8501 问答文章9001 问答文章9501
当前位置: 首页 - 科技 - 知识百科 - 正文

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

来源:懂视网 责编:小采 时间:2020-11-27 22:33:06
文档

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

微信小程序开发教程之增加mixin扩展:Mixin简介 Mixin(织入)模式并不是GOF的《设计模式》归纳中的一种,但是在各种语言以及框架都会发现该模式(或者思想)的一些应用。简单来说,Mixin是带有全部实现或者部分实现的接口,其主要作用是更好的代码复用。 Mixin这个概念在React, Vue中
推荐度:
导读微信小程序开发教程之增加mixin扩展:Mixin简介 Mixin(织入)模式并不是GOF的《设计模式》归纳中的一种,但是在各种语言以及框架都会发现该模式(或者思想)的一些应用。简单来说,Mixin是带有全部实现或者部分实现的接口,其主要作用是更好的代码复用。 Mixin这个概念在React, Vue中

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。

    总结

    好了,

    声明:本网页内容旨在传播知识,若有侵权等问题请及时与本网联系,我们将在第一时间删除处理。TEL:177 7030 7066 E-MAIL:11247931@qq.com

    文档

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

    微信小程序开发教程之增加mixin扩展:Mixin简介 Mixin(织入)模式并不是GOF的《设计模式》归纳中的一种,但是在各种语言以及框架都会发现该模式(或者思想)的一些应用。简单来说,Mixin是带有全部实现或者部分实现的接口,其主要作用是更好的代码复用。 Mixin这个概念在React, Vue中
    推荐度:
    • 热门焦点

    最新推荐

    猜你喜欢

    热门推荐

    专题
    Top