标签分类
技术文章
当前位置:主页 > 计算机编程 > javascript > 微信小程序开发增加mixin扩展的方法

微信小程序开发中如何增加mixin扩展

  • 发布时间:
  • 作者:码农之家原创
  • 点击:75

微信小程序开发增加mixin扩展的方法

这篇文章主要知识点是关于微信小程序开发,mixin扩展,微信小程序开发增加mixin扩展的方法,如何在微信小程序中实现Mixins方案 的内容,如果大家想对相关知识点有系统深入的学习,可以参阅以下电子书

小程序 巧应用:微信小程序开发实战
  • 类型:小程序大小:79.9 MB格式:PDF出版:机械工业出版社作者:熊普江
立即下载

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扩展

如何让小程序支持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。

总结

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

如何在微信小程序中实现Mixins方案

前言

在原生开发小程序的过程中,发现有多个页面都使用了几乎完全一样的逻辑。由于小程序官方并没有提供 Mixins 这种代码复用机制,所以只能采用非常不优雅的复制粘贴的方式去“复用”代码。随着功能越来越复杂,靠复制粘贴来维护代码显然不科学,于是便寻思着如何在小程序里面实现 Mixins。

什么是 Mixins

Mixins 直译过来是“混入”的意思,顾名思义就是把可复用的代码混入当前的代码里面。熟悉 VueJS 的同学应该清楚,它提供了更强大了代码复用能力,解耦了重复的模块,让系统维护更加方便优雅。

先看看在 VueJS 中是怎么使用 Mixins 的。

// define a mixin object
var myMixin = {
 created: function () {
 this.hello()
 },
 methods: {
 hello: function () {
  console.log('hello from mixin!')
 }
 }
}

// define a component that uses this mixin
var Component = Vue.extend({
 mixins: [myMixin]
})

var component = new Component() // => "hello from mixin!"

在上述的代码中,首先定义了一个名为 myMixin 的对象,里面定义了一些生命周期函数和方法。接着在一个新建的组件里面直接通过 mixins: [myMixin] 的方式注入,此时新建的组件便获得了来自 myMixin 的方法了。

明白了什么是 Mixins 以后,便可开始着手在小程序里面实现了。

Mixins 的机制

Mixins 也有一些小小的细节需要注意的,就是关于生命周期事件的执行顺序。在上一节的例子中,我们在 myMixin 里定义了一个 created() 方法,这是 VueJS 里面的一个生命周期事件。如果我们在新建组件 Component 里面也定义一个 created() 方法,那么执行结果会是如何呢?

var Component = Vue.extend({
 mixins: [myMixin],
 created: function () {
 console.log('hello from Component!')
 }
})

var component = new Component()

// =>
// Hello from mixin!
// Hello from Component!

可以看运行结果是先输出了来自 Mixin 的 log,再输出来自组件的 log。

除了生命周期函数以外,再看看对象属性的混入结果:

// define a mixin object
const myMixin = {
 data () {
 return {
  mixinData: 'data from mixin'
 }
 }
}

// define a component that uses this mixin
var Component = Vue.extend({
 mixins: [myMixin],
 data () {
 return {
  componentData: 'data from component'
 }
 },
 mounted () {
 console.log(this.$data)
 }
})

var component = new Component()

如何在微信小程序中实现Mixins方案

在 VueJS 中,会把来自 Mixins 和组件的对象属性当中的内容(如 data, methods等)混合,以确保两边的数据都同时存在。

经过上述的验证,我们可以得到 VueJS 中关于 Mixins 运行机制的结论:

  • 生命周期属性,会优先执行来自 Mixins 当中的,后执行来自组件当中的。
  • 对象类型属性,来自 Mixins 和来自组件中的会共存。

但是在小程序中,这套机制会和 VueJS 的有一点区别。在小程序中,自定义的方法是直接定义在 Page 的属性当中的,既不属于生命周期类型属性,也不属于对象类型属性。为了不引入奇怪的问题,我们为小程序的 Mixins 运行机制多加一条:

  • 小程序中的自定义方法,优先级为 Page > Mixins,即 Page 中的自定义方法会覆盖 Mixins 当中的。

代码实现

在小程序中,每个页面都由 Page(options) 函数定义,而 Mixins 则作用于这个函数当中的 options 对象。因此我们实现 Mixins 的思路就有了——劫持并改写 Page 函数,最后再重新把它释放出来。

新建一个 mixins.js 文件:

// 保存原生的 Page 函数
const originPage = Page

Page = (options) => {
 const mixins = options.mixins
 // mixins 必须为数组
 if (Array.isArray(mixins)) {
 delete options.mixins
 // mixins 注入并执行相应逻辑
 merge(mixins, options)
 }
 // 释放原生 Page 函数
 originPage(options)
}

原理很简单,关键的地方在于 merge() 函数。merge 函数即为小程序 Mixins 运行机制的具体实现,完全按照上一节总结的三条结论来进行。

// 定义小程序内置的属性/方法
const originProperties = ['data', 'properties', 'options']
const originMethods = ['onLoad', 'onReady', 'onShow', 'onHide', 'onUnload', 'onPullDownRefresh', 'onReachBottom', 'onShareAppMessage', 'onPageScroll', 'onTabItemTap']

function merge (mixins, options) {
 mixins.forEach((mixin) => {
 if (Object.prototype.toString.call(mixin) !== '[object Object]') {
  throw new Error('mixin 类型必须为对象!')
 }
 // 遍历 mixin 里面的所有属性
 for (let [key, value] of Object.entries(mixin)) {
  if (originProperties.includes(key)) {
  // 内置对象属性混入
  options[key] = { ...value, ...options[key] }
  } else if (originMethods.includes(key)) {
  // 内置方法属性混入,优先执行混入的部分
  const originFunc = options[key]
  options[key] = function (...args) {
   value.call(this, ...args)
   return originFunc && originFunc.call(this, ...args)
  }
  } else {
  // 自定义方法混入
  options = { ...mixin, ...options }
  }
 }
 })
}

Mixins 使用

在小程序的 app.js 里引入 mixins.js

require('./mixins.js')

撰写一个 myMixin.js

module.exports = {
 data: { someData: 'myMixin' },
 onShow () { console.log('Log from mixin!') }
}

在 page/index/index.js 中使用

Page({
 mixins: [require('../../myMixin.js')]
})

如何在微信小程序中实现Mixins方案

大功告成!此时小程序已经具备 Mixins 的能力,对于代码解耦与复用来说将会更加方便。

总结

以上就是这篇文章的全部内容了,希望本文的内容对大家的学习或者工作具有一定的参考学习价值,谢谢大家对码农之家的支持。

以上就是本次给大家分享的全部知识点内容总结,大家还可以在下方相关文章里找到解决axios.interceptors.respon、 儿童python编程入门书籍推、 vue项目中使用md5加密以及、 等javascript文章进一步学习,感谢大家的阅读和支持。

上一篇:火狐浏览器页面缩放兼容性处理方法

下一篇:微信小程序动态添加分享数据的实操方法

展开 +

收起 -

学习笔记
网友NO.453732

详解Vue.js Mixins 混入使用

Mixins一般有两种用途: 1、在你已经写好了构造器后,需要增加方法或者临时的活动时使用的方法,这时用混入会减少源代码的污染。 2、很多地方都会用到的公用方法,用混入的方法可以减少代码量,实现代码重用。 一、Mixins的基本用法 我们现在有个数字点击递增的程序,假设已经完成了,这时我们希望每次数据变化时都能够在控制台打印出提示:“数据发生变化”. 代码实现过程: !DOCTYPE htmlhtml lang="en"head meta charset="UTF-8" script type="text/javascript" src="../assets/js/vue.js"/script titleMixins Option Demo/title/headbody h1Mixins Option Demo/h1 hr div id="app" pnum:{{ num }}/p Pbutton @click="add"增加数量/button/P /div script type="text/javascript" //额外临时加入时,用于显示日志 var addLog={ updated:function(){ console.log("数据放生变化,变化成"+this.num+"."); } } var app=new Vue({ el:'#app', data:{ num:1 }, methods:{ add:function(){ this.num++; } }, mixins:[addLog]//混入 }) /script/body/html 二、mixins的调用顺序 从执行的先后顺序来说,都是混入的先执行,然后构造器里的再执行,需要注意的是,这并不是方法的覆盖,而是被执行了两边。 在上边的代码的构造器里我们也加入了updated的钩子函数: updated:function(){ console.log("构造器里的updated方法。")}, 这时控制台输出的顺序是: mixins数据放生变化,变化成2. 构造器……

网友NO.603833

vue的mixins属性详解

首先先给出官网 https://vuejs.org/v2/guide/mixins.html 今天在开发项目的时候要改变一个标签的属性,因为项目中有多个地方都要改(业务逻辑相同),所以就看有没办法只改变一个地方,把方法加进去,最后找官网就发现这个属性。 下面是我的-mixin.js 文件 import {mapGetters, mapMutations, mapActions} from 'vuex' export const playlistMixin = { computed: { ...mapGetters([ 'playList' ]) }, mounted() { this.handlePlaylist(this.playList) }, activated() { this.handlePlaylist(this.playList) }, watch: { playList(newVal) { this.handlePlaylist(newVal) } }, methods: { handlePlaylist() { throw new Error('component must implement handlePlaylist method') } } } 这个文件就暴露出一个对象,不过这个对象和组件很类似,也就是组件的js代码部分类似。 这个.js文件要做的事情就是,在生命周期中和playList 变量改变的时候触发handlePlaylist 函数,但是这个函数的逻辑是在各自要改变的组件当中去实现。下面看看怎么用Mixin。 import {playlistMixin} from 'common/js/mixin' //引入Mixin export default { mixins: [playlistMixin], methods: { handlePlaylist (playlist) { let bottom = playlist.length 0 ? '60px' : '' this.$refs.recommend.style.bottom = bottom this.$refs.scroll.refresh() }, } } 在使用的组件中这样调用。 mixins: 这个属性是个数组,也就是说可以加载多个 minxin 文件。 handlePlaylist 方法是完成业务逻辑。所以……

网友NO.132080

Vue中的混入的使用(vue mixins)

使用场景: 例如我们在Vue单页面开发的时候当多个组件中都需要下拉刷新,或者使用的都是一个方法的时候,我们就可以使用vue mixins进行封装调用,以及继承,具体看代码。 选项合并 var mixin = { data: function () { return { message: 'hello' } }, created:function(){ console.log('我是mixins中的created') }, methods:{ show:function(num){ console.log(num) //mixins种的函数可以接收组件种的传参。 }, foo: function () { console.log('foo') }, conflicting: function () { console.log('from mixin') } } } var vm = new Vue({ mixins: [mixin], data: function () { return { title: 'def', message: 'goodbye' } }, created: function () { console.log('我是Vue中的created') console.log(this.$data) this.show(50); //可通过函数传参,把组件中需要的参数传给mixins进行使用。 }, methods:{ bar: function () { console.log('bar') }, conflicting: function () { console.log('from self') } } }) vm.foo() // = "foo" vm.bar() // = "bar" vm.conflicting() // = "from self" 注意以下三点: 1、当组件和混入对象含有同名选项时,这些选项将以恰当的方式混合。比如,数据对象在内部会进行浅合并 (一层属性深度),在和组件的数据发生冲突时以组件数据优先。 2、同名钩子函数将混合为一个数组,因此都将被调用。另外,混入对象的钩子将在组件自身钩子之前调用。 3、值为对象的选项,例如 methods, components 和 dir……

网友NO.196295

Vue使用mixins实现压缩图片代码

本文介绍了Vue使用mixins实现压缩图片代码,分享给大家,具体如下: 图片压缩 创建mixins image-compress.js export default { methods: { /** * 检查并压缩图片大小 */ checkAndHandleCompression(file) { return new Promise((resolve, reject) = { this.imgBase64(file, (image, canvas) = { let maxSize = 2 * 1024; // 2M (单位KB) let fileSize = file.size / 1024; // 图片大小 (单位KB) let uploadSrc, uploadFile; if (fileSize maxSize) { // 如果图片大小大于maxSize,进行压缩 uploadSrc = canvas.toDataURL(file.type, maxSize / fileSize); uploadFile = this.convertBase64UrlToFile(uploadSrc, file.name); // 转成file文件 } else { uploadSrc = image.src; uploadFile = file; } let compressedSize = uploadFile.size / 1024;// 压缩后图片大小 (单位KB) // 判断图片大小是否小于maxSize,如果大于则继续压缩至小于为止 if (compressedSize.toFixed(2) maxSize) { this.checkAndHandleUpload(uploadFile); } else { let fileOptions = {uploadSrc, uploadFile}; resolve(fileOptions); } }); }); }, /** * 将图片转化为base64 */ imgBase64(file, callback) { // 看支持不支持FileReader if (!file || !window.FileReader) return; // 创建一个 Image 对象 let image = new Image(); // 绑定 load 事件处理器,加载完成后执行 image.onload = function () { // 创建 canvas DOM 对象 let canvas = document.createElement('canvas'); // 返回一个用于在画布上绘图的环境, '2d' 指定了您想要在画布上绘制的类型 le……

<
1
>

Copyright 2018-2019 xz577.com 码农之家

版权责任说明