微信小程序开发教程中增加 mixin 扩展的要点剖析

why 120 2024-08-28

这篇文章主要介绍了关于微信小程序开发教程之增加mixin扩展,有着一定的参考价值,现在分享给大家,有需要的朋友可以参考一下

Mixin是一种思想,用部分实现的接口来实现代码复用。可以用来解决多继承的问题,又可以用来扩展功能。下面这篇文章主要给大家介绍了关于为微信小程序增加mixin扩展的相关资料,需要的朋友可以参考借鉴,下面来一起看看吧。

Mixin简介

Mixin(织入)模式并不是GOF的《设计模式》归纳中的一种,但是在各种语言以及框架都会发现该模式(或者思想)的一些应用。简单来说,Mixin是带有全部实现或者部分实现的接口,其主要作用是更好的代码复用。

Mixin这个概念在React, Vue中都有支持,它为我们抽象业务逻辑,代码复用提供了方便。然而小程序原生框架并没直接支持Mixin。我们先看一个很实际的需求:

为所有小程序页面增加运行环境class,以方便做一些样式hack。具体说就是小程序在不同的运行环境(开发者工具|iOS|Android)运行时,platform值为对应的运行环境值("ios|android|devtools")

1

2

3

<view class="{{platform}}">

 <!--页面模板-->

</view>

回顾vue中mixin的使用

文章开始提到的问题是非常适合使用Mixin来解决的。我们把这个需求转换成一个Vue问题:在每个路由页面中增加一个platform的样式class(虽然这样做可能没实际意义)。实现思路就是为每个路由组件增加一个data: platform。代码实现如下:

1

2

3

4

5

6

7

8

9

10

11

12

13

// mixins/platform.js

const getPlatform = () => {

 // 具体实现略,这里mock返回&#39;ios&#39;

 return &#39;ios&#39;;

};

 

export default {

 data() {

 return {

  platform: getPlatform()

 }

 }

}

1

2

3

4

5

6

7

8

// 在路由组件中使用

// views/index.vue

import platform from &#39;mixins/platform&#39;;

 

export default {

 mixins: [platform],

 // ...

}

1

2

3

4

5

6

7

8

// 在路由组件中使用

// views/detail.vue

import platform from &#39;mixins/platform&#39;;

 

export default {

 mixins: [platform],

 // ...

}

这样,在index,detail两个路由页的viewModel上就都有platform这个值,可以直接在模板中使用。

vue中mixin分类

  • data mixin

  • normal method mixin

  • lifecycle method mixin

用代码表示的话,就如:

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

export default {

 data () {

 return {

  platform: &#39;ios&#39;

 }

 },

 

 methods: {

 sayHello () {

  console.log(`hello!`)

 }

 },

 

 created () {

 console.log(`lifecycle3`)

 }

}

vue中mixin合并,执行策略

如果mixin间出现了重复,这些mixin会有具体的合并,执行策略。如下图:

image.png

如何让小程序支持mixin

在前面,我们回顾了vue中mixin的相关知识。现在我们要让小程序也支持mixin,实现vue中一样的mixin功能。

实现思路

我们先看一下官方的小程序页面注册方式:

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

20

21

22

23

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配置,上面的官方注册方式会变成:

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

20

21

22

23

24

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)就是一个普通的函数调用,我们加个中间方法:

1

Page(createPage(configObj))

在createPage这个方法中,我们可以预处理configObj中的mixin,把其中的配置按正确的方式合并到configObj上,最后交给Page() 。这就是实现mixin的思路。

具体实现

具体代码实现就不再赘述,可以看下面的代码。更详细的代码实现,更多扩展,测试可以参看github

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

20

21

22

23

24

25

26

27

28

29

30

31

32

33

34

35

36

37

38

39

40

41

42

43

44

45

46

47

48

49

50

51

52

53

54

55

56

57

58

59

60

61

62

63

64

65

66

67

68

69

70

71

72

73

74

75

76

77

78

79

80

81

82

83

84

85

86

87

88

89

90

91

92

93

94

95

96

97

98

99

100

101

102

103

104

105

106

107

108

109

110

111

112

113

114

115

116

117

118

119

120

121

122

123

124

125

126

127

128

129

130

131

132

133

134

135

136

137

138

139

140

141

142

143

144

145

146

147

148

149

150

151

152

153

154

155

156

157

158

159

160

/**

 * 为每个页面提供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

1

Page(createPage(configObj))

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

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

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

以上就是本文的全部内容,希望对大家的学习有所帮助。


版权声明:本文内容由网络用户投稿,版权归原作者所有,本站不拥有其著作权,亦不承担相应法律责任。如果您发现本站中有涉嫌抄袭或描述失实的内容,请联系我们jiasou666@gmail.com 处理,核实后本网站将在24小时内删除侵权内容。

上一篇:微信小程序网络请求的相关要点与解析
下一篇:微信小程序中 wx.request 的详细分析与解读
相关文章

 发表评论

暂时没有评论,来抢沙发吧~