用户
 找回密码
 立即注册

QQ登录

只需一步,快速开始

扫一扫,登录网站

小程序社区 首页 教程 查看内容

微信小程序开发组件化(下)

Rolan 2017-8-15 10:15

代码实现单从组件功能而言,wepy已经满足了小程序绝大部分的组件开发需求,堪称完美。 但如果权衡整个项目:开发前需要查看开发文档,按照与小程序截然不同的写法编写小程序。对于不熟悉MVVM开发来说有一定的学习成 ...

代码实现 
单从组件功能而言,wepy已经满足了小程序绝大部分的组件开发需求,堪称完美。 
但如果权衡整个项目: 
开发前需要查看开发文档,按照与小程序截然不同的写法编写小程序。对于不熟悉MVVM开发来说有一定的学习成本,而且对于不利于开发者熟悉小程序(就像一个前端开发的入门者就直接使用React.js开发,这样很难熟悉诸如原生JS操作DOM的“相对底层”知识)。 
开发时也需要实时编译之后才能预览和调试,有一定的时间开销。 
开发编译后的代码未能正常运行,到底是编写问题还是编译问题还是框架问题?调试起来具有一定的复杂性。

所以wepy更适合大型小程序(当你有足够理由使用它时),就像Vuex官方文档中提到的那样:如果您的应用够简单,您最好不要使用 Vuex。一个简单的 global event bus 就足够您所需了。 
如果对于中小型的小程序,怎么较好的实现组件化? 
wxss毫无异议地采用@import引用。 
而对于模板,由于我们使用include方式引用,所以只能和wepy一样,通过命名的方式来隔离作用域。最简单的方式莫过于给其增加组件名成做前缀。譬如:

  1. // src/components/tab.wxml
  2. <view class="tab">
  3. <view class="tab_item tab_message{{tabActive == 0 ? ' active' : ''}}" bindtap="tabChange(0)">
  4. <image class="icon" src="../images/message{{tabActive == 0 ? '_active' : ''}}.png">image>
  5. <text class="title">微信text>
  6. view>
  7. ...
  8. view>

而对于js可以简单封装成对象,然后直接与页面对象合并即可:

  1. // src/components/tab.js
  2. export default class {
  3. data: {
  4. tabActive: {
  5. twoWay: true
  6. }
  7. },
  8. change (idx, evt) {
  9. this.active = +idx;
  10. }
  11. }

很多人都会想到这样调用:

  1. // src/pages/index.wxml
  2. <include src="../components/tab.wxml"/>
  3. ...
  4. // src/pages/index.js
  5. import tab from '../components/tab.js'
  6. let page = {
  7. ...
  8. }
  9. Object.assign(page, tab)
  10. Page(page)

直接用Object.assign来实现最大的坏处就是父页面无法监听组件事件,比如在例子中tab页签被点击的时候,tab组件内部需要做样式的变化,而对于父页面也需要监听这个事件来展现对应的内容。 
另一种情况,比如组件初始化也需要用到父页面的data属性的时候,那么需要父页面在初始化完成之后再调用组件本身的初始化函数。 
在onLoad中依次调用?这样需要对每个组件进行判断和调用,太多垃圾代码也容易出错,显然代码不够简介和健壮。 
借助MVVM框架的开发经验,我们可以采用Vue.js中的mixin思路来解决这个问题。 
将组件和页面对象进行混合。对于组件中的data属性,我们依然采取Object.assign的方式拷贝,由于组件对象中我们只要区分数据类型和事件函数,那么可以去掉data这一层。而对于事件我们需要既触发组件事件,也触发页面事件,那么需要写段代码来实现。最后组件的定义和调用可以修改为:

  1. // src/components/tab.js
  2. export default class {
  3. tabActive: {
  4. twoWay: true
  5. },
  6. change (idx, evt) {
  7. this.active = +idx;
  8. }
  9. }
  10. // src/pages/index.js
  11. import tab from '../components/tab.js'
  12. import {combine} from '../util.js'
  13. let page = {
  14. tabClick(event) {
  15. ...
  16. }
  17. ...
  18. }
  19. combine(page, tab)
  20. Page(page)

而对于combine函数只需要判断数据类型然后执行对应的操作即可。

  1. // util.js
  2. /**
  3. * 组件对象与页面对象融合
  4. * @param {Object} target 页面对象
  5. * @param {Object} source 组件对象,支持不定参数
  6. * return {Object} 返回一个融合后的页面对象
  7. */
  8. let combine = (target, ...source) => {
  9. source.forEach(function(arg) {
  10. if('object' === typeof arg) {
  11. for(let p in arg) {
  12. if('object' === typeof arg[p]) {
  13. // 对于对象,直接采用 Object.assign
  14. target[p = target[p || {}
  15. Object.assign(target[p], arg[p])
  16. } else if('function' === typeof arg[p]) {
  17. // 函数进行融合,先调用组件事件,然后调用父页面事件
  18. let fun = target[p ? target[p : function(){}
  19. delete target[p
  20. target[p = function() {
  21. arg[p].apply(this, arguments)
  22. fun.apply(this, arguments)
  23. }
  24. } else {
  25. // 基础数据类型,直接覆盖
  26. target[p = target[p || arg[p
  27. }
  28. }
  29. }
  30. })
  31. }
  32. export default {
  33. combine
  34. }

这种方案似乎简单明了地实现了组件化,但是有一个问题,那就是组件地复用。 
如果一个页面中同时使用多个tab组件就会出现一些麻烦,因为相同组件没有通过命名进行隔离,所以会有影响。 
解决方法只能通过修改组件本身或其它方式来支持了,不过好在大部分页面中不会出现这样的需求。

总结 
对于大型项目,学有余力的开发者,可以采用wepy这个非常优秀的小程序框架(既有构建工具,也有代码)。 
对于中小型项目和初学者,采取混合的方式能满足组件化开发需求。

鲜花
鲜花 (1)
鸡蛋
鸡蛋

刚表态过的朋友 (1 人)

分享至 : QQ空间
收藏
原作者: yalishizhude

相关阅读