微信小程式在頁面,自定義元件中使用資料監聽器

智慧女孩要禿頭~發表於2020-12-06

資料監聽器可以用於監聽和響應任何屬性和資料欄位的變化,通常會在監聽到某個值的改變時去操作data中的其它屬性值。

在自定義元件中使用監聽器:

Component({
  properties:{//監測傳過來的屬性
    num: {
      type: String,
      observer: function(newVal, oldVal) {
         console.log('properties-num', newVal)
      }
    },
    person: {
      type: Object,
      observer: function(newVal, oldVal) {
        // console.log('properties-person', newVal)
      }
    }
  },
  data: {
    aloneVal: 0,
    oneVal: null,
    twoVal: null
  },
  observers: {//能夠監測到props和data中的
    // 監聽全部 setData,每次 setData 都觸發,一般用不到 '**' 監聽全部
    '**':function (val) {
      console.log('**所有的setData變化:', val) // 此時返回的 val 值是一個包含所有data變數的物件
    },
    // 監聽 properties 接收的值的變化
    'num' (val) {
      console.log('observers-num', val)
    },
    // 監聽物件
    'person' (val) {
      console.log('observers-person', val)
    },
    // 監聽物件的屬性
    'person.name' (val) {
      console.log('observers-person.name', val)
    },
    // 監聽子元件中單個資料的變化
    'aloneVal' (val) {
      console.log('aloneVal', val)
    },
    // 監聽子元件中多個資料的變化
    'oneVal, twoVal' (val1, val2) {
      console.log('oneVal', val1)
      console.log('twoVal', val2)
    }
  },
  // 在元件在檢視層佈局完成後執行
  ready() {
    setInterval(()=>{
    this.setData({
      aloneVal:this.aloneVal+1
      })
    },1000)
  },
})

實現自定義元件的計算屬性computed功能需要利用自定義元件擴充套件behavior.js
https://developers.weixin.qq.com/miniprogram/dev/framework/custom-component/extend.html

新建一個behavior.js檔案

// behavior.js
module.exports = Behavior({
  lifetimes: {
    created() {
      this._originalSetData = this.setData // 原始 setData
      this.setData = this._setData // 封裝後的 setData
    }
  },
  definitionFilter(defFields) {
    const computed = defFields.computed || {}
    const computedKeys = Object.keys(computed)
    const computedCache = {}

    // 計算 computed
    const calcComputed = (scope, insertToData) => {
      const needUpdate = {}
      const data = defFields.data = defFields.data || {}

      for (let key of computedKeys) {
        const value = computed[key].call(scope) // 計算新值
        if (computedCache[key] !== value) needUpdate[key] = computedCache[key] = value
        if (insertToData) data[key] = needUpdate[key] // 直接插入到 data 中,初始化時才需要的操作
      }

      return needUpdate
    }

    // 重寫 setData 方法
    defFields.methods = defFields.methods || {}
    defFields.methods._setData = function (data, callback) {
      const originalSetData = this._originalSetData // 原始 setData
      originalSetData.call(this, data, callback) // 做 data 的 setData
      const needUpdate = calcComputed(this) // 計算 computed
      originalSetData.call(this, needUpdate) // 做 computed 的 setData
    }

    // 初始化 computed
    calcComputed(defFields, true) // 計算 computed
  }
})

在元件中使用:

const beh = require('./behavior.js')
Component({
  behaviors: [beh],
  data: {
    a: 0,
  },
  computed: {
    b() {
      return this.data.a + 100
    },
  },
  methods: {
    onTap() {
      this.setData({
        a: ++this.data.a,
      })
    }
  }
})

在頁面中使用資料監測:

1,在app.js中新增以下程式碼:

//app.js
App({

  setWatcher(page) {
    let data = page.data; // 獲取page 頁面data
    let watch = page.watch;
    for(let i in watch){
      let key = i.split('.'); // 將watch中的屬性以'.'切分成陣列
      let nowData = data; // 將data賦值給nowData
      let lastKey = key[key.length - 1];
      let watchFun = watch[i].handler || watch[i]; // 相容帶handler和不帶handler的兩種寫法
      let deep = watch[i].deep; // 若未設定deep,則為undefine
      this.observe(nowData, lastKey, watchFun, deep, page); // 監聽nowData物件的lastKey
      }
  },

    observe(obj, key, watchFun, deep, page) {
      let val = obj[key];
      // 判斷deep是true 且 val不能為空 且 typeof val==='object'(陣列內數值變化也需要深度監聽)
      if (deep && val != null && typeof val === 'object') {
      for(let i in val){
      this.observe(val, i, watchFun, deep, page); // 遞迴呼叫監聽函式
    }
  }
    let that = this;
    Object.defineProperty(obj, key, {
    configurable: true,
    enumerable: true,
    set: function (value) {
      // 用page物件呼叫,改變函式內this指向,以便this.data訪問data內的屬性值
      watchFun.call(page, value, val); // value是新值,val是舊值
      val = value;
      if (deep) { // 若是深度監聽,重新監聽該物件,以便監聽其屬性。
       that.observe(obj, key, watchFun, deep, page);
      }
    },
    get: function () {
     return val;
    }
    })
  }

})

2,在頁面index.js中使用:

// pages/index/index.js
import initComputed from '../../lib/wxComputed.min.js'// 引入computed屬性方法
const app = getApp()
Page({
data:{
  createProcesDescribe:'',
  createProcesName: '',
  lastName: 'aa',
  firstName: 'bb',
},

   onLoad(option){//監聽頁面載入
    app.setWatcher(this)// 設定監聽器,建議在onload裡設定
    
    initComputed(this)//執行computed屬性初始化
   },
   
  // computed屬性
  computed: {
    // 這是一個函式,返回值為此計算屬性的值
    fullName() {
        return this.data.lastName + '-' + this.data.firstName
    },
  },
  
  //watch監聽data裡的資料
   watch:{
    createProcesName:{
      handler(newVal,oldVal){
        console.log(newVal,oldVal)
      }
    },
    createProcesDescribe:{
      handler(newVal,oldVal){
        console.log(newVal,oldVal)
    },
  },
 })

總結:在自定義元件中直接使用observers其實跟Vue框架中使用watch作用一樣,如果在自定義元件中使用computed屬性功能則需要引入元件擴充套件。在頁面中使用watch也需要自己在外部新增監聽方法,然後在需要監聽的頁面中引入監聽器,並設定監聽器,如果在頁面中需要使用j計算屬性computed也需要在頁面中引入該功能檔案。

相關文章