[Vue 项目技巧] 父子组件 loading,传递 or 劫持

993 阅读1分钟

场景:子组件有 loading,但状态由父组件控制,那 loading 由谁维护呢?

常规做法:传递

父组件维护 loading,传递给子组件。

child.vue

<el-button :loading="loading" @click="query">查询</el-button>

export default {
  props: {
    loading: Boolean
  },
  methods: {
    query() {
      const obj = { data: 'xxx' }
      this.$emit('query', obj)
    }
  }
}

parent.vue

<child :loading="loading" @query="query" />

export default {
  data() {
    return {
      loading: false
    }
  },
  methods: {
    async query(obj) {
      try {
        this.loading = true
        const res = await HTTP.query(obj.data)
        //
      } catch (error) {
        //
      } finally {
        this.loading = false
      }
    }
  }
}

另一种做法:劫持

子组件自己维护 loading,父组件改变子组件传来的对象的属性,子组件劫持该对象属性的改变。

child.vue

<el-button :loading="loading" @click="query">查询</el-button>

export default {
  data() {
    return {
      loading: false
    }
  },
  methods: {
    query() {
      const obj = { data: 'xxx' }
      Object.defineProperty(obj, 'loading', {
        set: val => {
          this.loading = val
        }
      })
      this.$emit('query', obj)
    }
  }
}

以上是利用 Object.defineProperty 监听对象的属性,也可以使用 ES6 的 Proxy

query() {
  const obj = new Proxy(
    { data: 'xxx' },
    {
      set: (target, propKey, value, receiver) => {
        if (propKey === 'loading') this.loading = value
        return Reflect.set(target, propKey, value, receiver)
      }
    }
  )
  this.$emit('query', obj)
}

parent.vue

<child @query="query" />

export default {
  methods: {
    async query(obj) {
      try {
        obj.loading = true
        const res = await HTTP.query(obj.data)
        //
      } catch (error) {
        //
      } finally {
        obj.loading = false
      }
    }
  }
}

尤其当子组件有多个时这种方式就比较好维护。

<child v-for="item in list" :key="item.id" :data="item" @query="query" />