微信小程序添加mixin扩展
react和vue都支持Mixin的概念。它为我们抽象业务逻辑和代码重用提供了便利。但是,applet原生框架并不直接支持Mixin。我们先来看一个非常实际的需求:
为所有的小程序页面添加运行环境类,以便于做一些风格黑客。具体来说,当小程序在不同的运行环境(开发者工具|iOS|Android)下运行时,平台值就是对应的运行环境值(‘iOS | Android | devtools’)
view class='{{platform}} '!-页面模板-/view
回顾vue中mixin的使用
文章开头提到的问题非常适合Mixin解决。让我们把这个需求变成一个Vue问题:给每个路由页面添加一个平台的样式类(虽然可能不太实用)。实现思路是为每个路由组件增加一个data:平台。代码实现如下:
//mixis/platform . jsconst getplatform=()={//具体实现略作省略,其中mock返回‘IOs’返回‘IOs’;};导出{ data(){ return { platform : getplatform()} } }//使用//view/index . vue从路由组件中的“mixis/platform”导入平台;导出{ mixin :[平台],//.}//使用///view/detail . vue从路由组件中的‘mixins/platform’导入平台;导出{ mixin :[平台],//.}这样,在索引、详细路由页面的viewModel中就有了价值平台,可以直接在模板中使用。
vue中mixin分类
Data mixin正常方法mixin生命周期方法mixin用代码表示,就像:
导出默认{ data(){ return { platform : ' IOs ' } },methods : { SayHello(){ console . log(` hello!`)}},created(){ console . log(` life cycle 3 `)} }
vue中mixin合并,执行策略
如果mixin之间存在重复,这些mixin将被专门合并以实现该策略。下图:
如何让小程序支持mixin
前面,我们复习了vue中mixin的相关知识。现在我们希望小程序支持mixin,并在vue中实现同样的mixin功能。
实现思路
我们来看看官方小程序页面的注册方法:
页面({ data : { text : }“这是页面数据。”},onLoad:函数(选项){ //页面加载时做一些初始化。},onReady:函数(){ //页面准备好后做点什么。},onShow:函数(){ //页面显示时做点什么。},On hide :函数()page hide时做点什么。},在unload :函数()上{/在页面关闭时做点什么。},自定义数据: {hi 3360' Mina'}})如果我们加入mixin配置,上述官方注册方式将变为:
页面({ mixin :[平台],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/reduxfunction 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;};
小结
- 本文主要讲了如何为小程序增加mixin支持。实现思路为:预处理configObj
Page(createPage(configObj))
在处理mixin重复时,与vue保持一致:
- data, methods会合并,组件自身具有最高优先级,其次mixins中后配置的mixin优先级较高。
- lifecycle不会合并。先顺序执行mixins中的lifecycle,再执行组件自身的lifecycle。
版权声明:微信小程序添加mixin扩展是由宝哥软件园云端程序自动收集整理而来。如果本文侵犯了你的权益,请联系本站底部QQ或者邮箱删除。