Vue 中少用的组件访问

Vue 中少用的组件访问

Flying
2020-04-05 / 0 评论 / 126 阅读 / 正在检测是否收录...

在之前的文章中,我们讲到了 Vue 组件之间交互用单向数据流——通过 Props 向子组件传递数据,通过 emit 使用事件抛出一个值的模式。在组件上使用 v-model、sync还支持双向数据绑定,这是 Vue 的一个特性。本文将介绍 Vue 处理边界情况有关的组件访问功能。

访问父级组件实例

通过 $parent 属性

$parent属性可以用来从一个子组件访问父组件的实例。它提供了一种机会,可以在后期随时触达父级组件,以替代将数据以 prop 的方式传入子组件的方式。

// ByParent.vue
<template>
  <button @click="handleClick">Call parents's method</button>
</template>

<script>
export default {
  name: "ByParent",
  methods: {
    handleClick() {
      this.$parent.updateParentNow();
    }
  }
};
</script>

ByParent 子组件中,通过 this.$parent 可以访问父组件的实例的 updateParentNow 方法。如果一个组件父组件和多个子组件组成,在确认父子组件关系的前提下,这种组件访问方式还是比较方便的。像Element UI 组件库中的一些开发群组组件,比如单选组、标签页、折叠面板等组件会用到这种模式。如果我们只是使用封装好的组件,基本上用不到这种模式。

在绝大多数情况下,触达父级组件会使得你的应用更难调试和理解,尤其是当你变更了父级组件的数据的时候。

访问子组件实例或子元素

使用 Prop 函数

在 React 中处理表单时,要类似实现 Vue 双向数据绑定效果,一般会向子组件传两个 Prop:一个 value 变量,另一个 onChange 处理函数。由于函数时引用类型。父子组件中的 onChange 处理函数 会保留同一个指针引用,因此子组件可以将其内部实例或元素通过Prop 处理函数传回父组件。其实,在 Vue 中也可以这样做。看下面示例:

  • ByProp 组件
<template>
  <div>
    <div>{{ open }}</div>
    <button @click="handleUpdate">Call child</button>
  </div>
</template>
  
<script>
export default {
  name: "ChildProp",
  props: {
    onChange: {
      type: Function,
      default: null,
    },
  },

  methods: {
    handleUpdate() {
      this.now = Date.now();
      this.onChange(this.now);
    },
  },
};
</script>
  • 调用 ByProp
<fieldset>
  <legend>Function Prop</legend>
  <div>{{ propNow }}</div>
  <ByProp :on-change="updatePropNow" />
</fieldset>

本实例中我们将 ByProp 组件实例的 this.now 传回父组件 App 并在其模板显示最新值。

使用 ref 属性

尽管存在 prop 和事件,有的时候你仍可能需要在 JavaScript 里直接访问一个子组件。为了达到这个目的,你可以通过 ref 这个属性为子组件赋予一个 ID 引用。例如:

  • 调用

    <fieldset>
    <legend>$ref</legend>
    <ByRef ref="child" />
    <button @click="$refs.child.updateNow()">Call child</button>
    </fieldset>

现在在你已经定义了这个 ref 的组件 App 里,你可以使用this.$refs.child 来访问这个 <ByRef》 实例方法 updateNow

$refs 只会在组件渲染完成之后生效,并且它们不是响应式的。这仅作为一个用于直接操作子组件的“逃生舱”——你应该避免在模板或计算属性中访问 $refs。

监听状态 Prop

不像 $refs 那样强耦合,我们可以通过在父组件中动态改变传入子组件实例的状态 Prop,在子组件内部用 watch 监听该 Prop,如果有变化则改变子组件实例的值。如下示例所示:

  • 子组件代码:
// ByWatch.vue
<template>
  <div>{{ now }}</div>
</template>

<script>
export default {
  name: "ByWatch",
  props: {
    toggled: {
      type: Boolean,
      default: false,
    },
  },
  data() {
    return {
      now: Date.now(),
    };
  },
  watch: {
    toggled(newValue) {
      this.updateNow();
    },
  },
  methods: {
    updateNow() {
      this.now = Date.now();
    },
  },
};
</script>

ByWatch 组件中,我们监听了从父组件中传入的 toggled prop,toggled值变化时会调用 updateNow 方法更新当前时间戳。

  • 父组件调用

在父组件 App 中这样实例化 ByWatch 组件:

<fieldset>
  <legend>Watch Prop</legend>
  <ByWatch :toggled="toggled" />
  <button @click="toggled = !toggled">Call child</button>
</fieldset>

ByWatch 组件中,传入了 toggled prop,并通过切换按钮来模拟改变 toggled 值 。

监听状态 Prop 适合子在子组件实例未完全挂载时为其异步完成清理,一个真实案例就是,每次打开模态框时重置Form。

使用 Vuex

Vuex 作为专为 Vue.js 应用程序开发的状态管理模式 + 库。它采用集中式存储管理应用的所有组件的状态,并以相应的规则保证状态以一种可预测的方式发生变化。Vuex 只用在其他模式不好时的场景下使用,并不是真正少用的组件访问方式。相反,他是一个通用方案。不仅能访问父级组件实例,也能好呢方便的访问子组件实例或子元素。

  • 父组件调用

    <button @click="$store.commit('UPDATE_NOW')">Call child</button>

    App 父组件中,通过上点击按钮提交了 Mutation UPDATE_NOW

  • 子组件代码:
// ByVuex.vue
<template>
  <div>{{ now }}</div>
</template>

<script>
export default {
  name: "ByVuex",
  data() {
    return {
      now: Date.now(),
    };
  },
  mounted() {
    this.func = this.$store.subscribe((mutation) => {
      if (mutation.type === "UPDATE_NOW") {
        this.updateNow();
      }
    });
  },
  beforeDestroy() {
    this.func();
  },
  methods: {
    updateNow() {
      this.now = Date.now();
    },
  },
};
</script>

ByVuex 组件中,订阅 store 的 mutation。updateNow 方法会在 mutation UPDATE_NOW 完成后调用。

Vuex 中的发布/订阅模式大大降低了组件间、模块间的耦合度,值得试一试。

参考

1

评论 (0)

取消