zoukankan      html  css  js  c++  java
  • 微信小程序组件化实践

    Do Not Repeat Yourself

    如何提高代码质量,方法有许多:抽象、模块、组件化,我认为它们的中心点都是——Do Not Repeat Yourself.

    小程序组件化

    我们先看看小程序自带的组件化能力:

    • 模板,通过 <import src="../name.wxml" /> 引入 ---- success
    • 样式,通过 @import 'name.wxss' 引入 ---- success
    • js ??? ---- fail

    我们看到,微信小程序开发有一个很大的痛点,缺乏组件化能力,特别是在没有使用构建工具或第三方框架的情况下,稍不留神,就会充满大量重复的代码。

    小程序提供了自定义组件( V1.6.3 才支持)

    举个栗子。

    小程序虽然提供了一些常用的组件——toast、loading等,但因需求不同,我们要实现自己的 popupnotifymodal等公共组件。这些自定义组件的共同点是:单个组件模板、事件大致一样,区别是每次使用组件时显示的内容不同。于是,我们在每个页面都能看到相似的代码,emm,已经产生了臭味道。

    mixins

    通过混入的方式,将组件方法、数据绑定到页面实例上,类似VUE mixins,不过,这里实现要更为简单:

    function mixins(page, ...args) {
      let data = args.reduce((res, item) => Object.assign(res, item.data), page.data || {})
    
      Object.assign(page, ...args)
      page.setData(data)
    }
    

    这样,在页面onLoad时,执行mixins(this, Component1, ...)就可以了。

    写一个popup

    现在开始写一个 popup组件,设计给的样式如下图:

    除去布局和样式,应该如何设计数据模型?个人认为需要把握两点:职责分离、易于扩展。

    第一步,分析哪些数据是不变的、可变的。

    可变数据:标题、内容、按钮文字、按钮个数、点击按钮后续操作、蒙层是否可点
    不变数据:点击蒙层、取消按钮隐藏popup,显示/隐藏popup事件

    第二步,设计数据模型。

    通过第一步的分析,我们可以抛开具体的业务逻辑,设计出popup的数据模型:

    let popupModal = {
      title: '',
      content: '',
      confirmButtonName: '确认',
      cancelButtonName: '取消',
    
      show(),
      hide(),
      onConfirm(),
      onCancel(),
    }
    

    使用面向对象可以轻松分离可变、不可变数据,生成所需数据模型:

    class GeneratePopupModal {
      constructor(options) {
        this.setParams(options)
      }
    
      setParams({
        title,
        content,
        confirmButtonName,
        cancelButtonName,
        success,
      } = {}) {
        this.title = title || this.title
        this.content = content || this.content
        this.confirmButtonName = confirmButtonName || this.confirmButtonName
        this.cancelButtonName = cancelButtonName || this.cancelButtonName
    
        this.success = success
      }
      show() {
        return Promise.resolve(this.__show = true)
      }
      hide() {
        return Promise.resolve(this.__show = false)
      }
      onConfirm() {
        return this.hide()
          .then(() => {
            if(this.success) return this.success({confirm: true})
          })
      }
      onCancel() {
        return this.hide()
          .then(() => {
            if(this.success) return this.success({cancel: true})
          })
      }
    }
    

    第三步,生成数据渲染组件。

    为了使popup组件保持简单,只对外暴露三个方法:

    • $popup(opts) ---- 根据参数生成组件数据并显示 popup
    • tapPopupConfirmButton() ---- 点击 popup 确认按钮
    • tapPopupCancelButton() ---- 点击 popup 取消按钮
    const GeneratePopupModal = require('./generatePopupModal')
    const defalut = {
      title: '管家提示',
      content: '抱歉,我们遇到一点问题,请稍后再试',
      confirmButtonName: '确认'
    }
    
    let Popup = null
    
    module.exports = {
      $popup(options={}) {
        /**
         * 每次使用新建实例,避免状态共享
         * 通过私有 __show 控制 popup.show,实现组件 popup 的显示和隐藏
         */
        Popup = new GeneratePopupModal(defalut)
    
        Object.defineProperty(Popup, '__show', {
          configurable: true,
          set: v => {
            Popup.show = v
            this.setData({popupModal: Popup})
          }
        })
        return new Promise((resolve, reject) => {
          Popup.setParams(
            Object.assign({}, {success: resolve, fail: reject}, options)
          )
          Popup.__show = true
        })
      },
      tapPopupConfirmButton() {
        Popup.onConfirm()
      },
      tapPopupCancelButton() {
        Popup.onCancel()
      },
    }
    

    第四步,引入、使用组件

    在上面mixins部分,介绍了如何引入组件。那么,怎么使用它呢?这里提供一个栗子:

    this.$popup({ // 根据传参生成数据、显示 popup
      title: '删除行程',
      content: '确认删除此行程?删除后将不可恢复。',
      confirmButtonName: '删除',
      cancelButtonName: '我再想想',
    }).then(({confirm, cancel}) => { // 分别表示点击了确定、取消按钮
      if(confirm) { // 点击删除(确定)按钮后续操作
        return r4251({
          id: item.productid,
          type: item.type
        })
        .then(res => this.__fetchCartList())
        .catch(e => {
          this.$popup() // 显示默认数据的 popup
    
          console.error('[cart/list.js] #tapDeleteCart r4251 error: ', e)
        })
      }
    })
    

    总结

    至此,popup组件基本完成了,但这种方式依然存在很大的漏洞:

    • mixins 方式混入组件数据/方法,极易造成命名冲突。通过命名空间解决
    • 如果组件有 data 对象,那么页面销毁(unLoad)后,页面数据依然常驻内存。通过导出函数对象解决

    虽然上面的解决方案并不十分理想难道不是非常别扭?,但好在足够简洁、实用,也基本实现了设计预期。

    期待更好的实现思路......

    .

  • 相关阅读:
    【备忘录】Sublime Text编辑器如何在选中的多行行首增加字符串
    微信卡券领取页面提示签名错误,微信卡券JSAPI签名校验工具对比签名一模一样,cardExt扩展字段有问题
    程序运行时动态生成缓存时存在的问题
    golang的beego框架开发时出现的问题纪录
    【备忘录】CentOS服务器mysql忘记root密码恢复
    试玩swoole扩展 第一天
    spring 定时任务执行2次
    JVM CUP占用率过高排除方法,windows环境
    spring cloud 服务A调用服务B自定义token消失,记录
    java.sql.SQLException: Value '0000-00-00' can not be represented as java.sql.Timestamp
  • 原文地址:https://www.cnblogs.com/fayin/p/8558348.html
Copyright © 2011-2022 走看看