likes
comments
collection
share

想在小程序中实现一个计算属性来监控表单的必填属性,失败了

作者站长头像
站长
· 阅读数 7

背景

小程序中的表单提交,需要校验表单填写完成按钮才是可点击状态,一般做法是每个表单变化的时候都去看是不是所有都填完成,也就是需要在每个可输入的地方做处理,希望能像vue的计算属性一样,在计算属性里面完成计算,不用在每个字段变化时处理

实现

reactive.js

let effectFunc = null
let bucket = new WeakMap()
export const reactive = (target) =>{
  for (let key in target) {
      if(target.hasOwnProperty(key)) {
          const value = target[key]
          if (value && typeof value === 'object') {
              target[key] = reactive(value)
          }
          setReactive(target, key, target[key])
      }
  }
  return target
}
export const setReactive = (target, pop, value) => {
  let reactiveValue = value;
   Object.defineProperty(target, pop, {
       get () {
         console.log('取值')
           track(target, pop)
           return reactiveValue
       },
       set(newValue) {
         console.log('设置值')
           if (newValue !== reactiveValue) {
               reactiveValue = newValue;
               trigger(target, pop);
           }
       }
   })
}
const trigger = (target, key) => {
  const mapBucket = bucket.get(target)
  if (!mapBucket) return
  const set = mapBucket.get(key)
  if (set && set.size > 0) {
      set.forEach(effect => {
          effect()
      })
  }
}
const track = (target, key) => {
 if (!effectFunc) return
  let mapBucket = bucket.get(target)
  if (!mapBucket) {
      bucket.set(target, mapBucket = new Map())
  }
  let set = mapBucket.get(key)
  if (!set) {
      mapBucket.set(key, set = new Set())
  }
  set.add(effectFunc)
}
export const computed = (func) => {
  let value;
  let dirty = true;

  const effect = () => {
      dirty = true;
      effectFunc = effect;
      value = func();
      return value;
  };

  const computedValue = {
      get value() {
          if (dirty) {
              value = effect();
              dirty = false;
          }
          return value;
      },
  };

  effect();
  return computedValue;
}

使用

import {reactive} from './reactive'
data: {
    test: reactive({
      a: 2
    })
}
onLoad: async function () {
    console.log(this.data.test.a)
    this.data.test.a = 7
    this.setData({
      [`test.a`]: 9
    })
    console.log(this.data.test.a)
		
}

问题

取值的时候能走get,设置值的时候不走set 自己仿写了一个极简Page

class Page {
    data = {}
    constructor(options) {
          this.data = options.data
           if ('onload' in options && typeof options.onload === 'function') {
               options.onload.call(this)
           }
    }
}
 new Page({
      data: reactive({person: {
              age: 3,
              a:5
          }}),
      onload() {
           console.log('页面加载完成', this.data.person.age)
          this.data.person.age = 6
      }
 })

发现set和get都是可以执行的,那应该就是小程序的内部实现机制的问题,也许可以拦截setData实现,下来研究

其他

  • 为什么不用proxy

    因为Proxy是 ECMAScript 6 标准的一部分,而一些老版本的小程序运行环境对ES6特性支持有限,像我的开发工具就不支持