网络日志

微信小程序(三)实现类似Vue中的 computed,watch 功能

前言

微信小程序和 vue 的语法非常像,会写 vue 的朋友上手 vue 基本是易如反掌。但是微信小程序中缺少了很多 vue 中没有的东西,比如计算属性 computed 、监听 watch。这些在平常开发中会经常用到。虽然微信小程序没有直接提供相关的 API 来使用这些,但是我们可以用其他的一些方法来简单的间接的实现这些功能。这篇文章就是记录一下实现这些功能的过程和一些个人感受。

实现步骤

computed 的实现过程

computed 的实现过程非常简单,我们可以通过微信小程序脚本语言 WXS(WeiXin Script) 来简单实现类似vue中的 计算属性功能(computed)。

  1. 先理解官方文档的示例

    <!--wxml-->
    <wxs module="m1">
    var msg = "hello world";
    
    module.exports.message = msg;
    </wxs>
    
    <view> {{m1.message}} </view>
    页面输出:hello world

我们可以把 <wxs></wxs> 标签当做一对 <script></script> 标签一样,我们可以在里面定义变量、函数。然后在 “Mustache” 语法双括号里使用我们定义的函数和变量。

  1. 看完官方示例后我们就来实现一个类似 vue 中的简单的 computed
// page.js
Page({
  data: {
    order: {
        time: '2022-06-27',
        status: 1,
        amount: 10
    }
  }
})
<wxs module="comp">
  module.exports = {
    handleMatchStatus: function (source) {
      switch (source) {
        case 1:
          return '已支付'
        case 2:
          return '支付失败'
        case 3:
          return '退款中'
      }
    }
  }
</wxs>

<view> {{comp.handleMatchStatu(order.status)}} </view>
输出:已支付

在后台拿到的一些数据,比如订单数据里面只有商品单价和数据,页面需要你显示总价,这个时候就可以这种方式来实现;或者是项目中有些数据是状态或者类型数据,比如订单状态、订单类型、商品渠道等,通过后端传过来的都是数字,有字典表的话还好办,没有字典表的话一般都是和后端商量好,定死几个值,这个时候就可以通过这种方式显示数值对应的文字意思。

watch 的实现过程

首先明确实现后的效果:能监听小程序 page 里面 data 的所有属性值实时的变化。

思路:小伙伴们应该都知道 vue2.x 中的 双向绑定是通过 Object.defineProperty 方法来实现的。所以我们微信小程序可以借鉴这一方法。通过遍历 page 里 data 的属性,使用 Object.defineProperty 逐个的对每个属性进行监听,发现属性值被修改时就在 set 里调用对应的处理函数。

具体实现代码:

/**
 * 
 * @param {*} data 监听的对象
 * @param {*} watch 听的 key 和回调函数组成的类
 */
export const setWatcher = (data, watch, _this) => { // 接收 page 传过来的data对象和watch对象
  //监听属性 并执行监听函数
  const observe = (obj, key, watchFn) => {
    var val = obj[key]; // 为改变之前的属性值
    Object.defineProperty(obj, key, {
      configurable: true,
      enumerable: true,
      set: (value) => {
        val = value;
        watchFn.call(_this, value); // 调用对应函数
      },
      get: () => {
        return val;
      }
    })
  }
  Object.keys(watch).forEach(v => { // 将watch对象内的key遍历
    observe(data, v, watch[v]); // 监听data内的v属性,传入 watch 内对应函数以调用
  })
}

函数有三个入数 datawatch_this;data 就是我们监听的对象,在小程序里也就是 page 里的 data。watch 就是一个对象,里面装着以键值对的形式储存的被监听的各个属性和对应的函数,key 是被监听属性,key 对应的值是这个属性值改变后的回调函数。_this 是调用我们封装的这个函数的环境。

封装完后我们就可以在 page 页面导入并且使用了

import { setWatcher } from '../../utils/util'

Page({
  data: {
    a: 1,
    b: 2
  }
  async onLoad(options) {
    setWatcher(this.data, this.watch, this);
  },
  // 监听的值
  watch: {
    a: function (newValue) {
      // 值改变后的操作
      console.log(newValue)
    },
    b: function (newValue) {
      // 值改变后的操作
      console.log(newValue)
    }
  }
})

注意的地方

WXS

  • WXS 函数不能作为组件的事件回调
  • 在 WXS 中是不能调用其他js文件的函数和变量的,还有小程序的 API 也不能调用,因为 WXS 的运行环境是和其他 js 代码隔离的。有优点也有缺点吧,速度快,但是不能和其他文件的代码交互。

    watch

  • 封装的时候一定要用传进来的 this ,通过 call 修改 this 指向到函数被调用的词法环境。否则就调不了 set 中传进来的回调函数.

总结

  • 说 iOS 设备上小程序内的 WXS 会比 JavaScript 代码快 2 ~ 20 倍。所以像这种订单流水中的一些数据用 WXS 简单实现的类似 vuecomputed 还是比较有优势,特别是这种要经常渲染到页面上的数据。
  • 上面代码中用 Object.defineProperty 这种方式封装实现的类似 vuewatch 的方式还有优化的地方,比如 data 中的深层数据改变时是监听不到的,还需深层次循环遍历。当然也可以借鉴 vue3 中用代理 proxy 的方式来实现。大家还有什么其他更好的方式可以在评论区留言,一起交流交流

写在最后

我是 AndyHu,目前暂时是一枚前端搬砖工程师。

文中如有错误,欢迎在评论区指正,如果这篇文章帮到了你,欢迎点赞和关注呀😊

未经许可禁止转载💌

speak less,do more.