Vue组件通信很难吗?

1. 父传子

通过自定义属性实现父传子,例如:

xml 复制代码
<template>
  <div id="home">
    <appMain :fromindex="msg" />
  </div>
</template>
<script>
  // 导入组件
  import appMain from "./appMain.vue";
  export default {
    // 注册组件
    components: {
      appMain,
    },
    data() {
      return {
        msg: "我是父组件的内容",
      };
    },
  };
</script>
xml 复制代码
<template>
  <div id="main">
    <p>{{ fromindex }}</p>
  </div>
</template>
<script>

  export default {
    props: ["fromindex"],
  };
</script>

描述:

  1. 在父组件中导入子组件文件
  2. 在父组件中注册子组件
  3. 定义需要传递给子组件的数据
  4. 通过在子组件的标签中动态绑定自定义属性将需要传递的数据以参数的形式传递给子组件
  5. 子组件需要通过 props 进行接收
  6. 最后就可以在子组件中使用传递来的数据了

需要注意:

  1. 父组件传递过来的是什么类型的数据,那么子组件拿到的就是什么类型的数据

为了避免传递错误类型的数据,我们就可以在子组件接收的时候做出校验。

2. 通信校验

校验项中的 type 可以是:

  1. String
  2. Number
  3. Boolean
  4. Array
  5. Object
  6. Date
  7. Function
  8. Symbol

例如

css 复制代码
props: {
  fromindex: Number, //传递来的数据必须是数字
    },

将上边子组件接收数据的地方改为上述代码:就表示这里只接收数字类型的数据,如果传递来的不是对应的数据类型,那么就会抛出如下错误信息:

css 复制代码
props: {
  fromindex: [Number, Boolean],
    },

以上表示两种类型满足一种即可

yaml 复制代码
props: {
  fromindex: {
    type: String,
      required: true,
      },
      },

如果没有从父组件传递数据,那么就会抛出错误:

错误描述:缺少必需的属性:fromindex

当然也允许这样书写:

yaml 复制代码
props: {
  fromindex: {
    type: [String, Number],
      required: true,
      },
      },

这样就表示字符串类型或数字类型都可以,但是必须传递

在这里 vue 允许我们设置默认值,也就是如果我们没有传递数据到子组件,那么子组件就会使用默认值,避免抛出错误

yaml 复制代码
props: {
  fromindex: {
    type: Number,
      default: 100,
      },
},

需要注意的是:

  1. 使用默认值,就不能设置必传属性,否则当没有传递参数的时候,还是会抛出错误
css 复制代码
props: {
  fromindex: {
    type: Object,
      default() {
        return { message: "hello" };
  },
    },
    },

当类型设置为 null 或者 undefined 的时候会跳过所有类型的检查

接下来就来看看如何自定义检验规则:

2.1. 自定义校验规则

javascript 复制代码
props: {
  fromindex: {
    validator(value) {
      return value.length > 5;
    },
  },
},

如果不符合自定义的规则就会抛出错误信息:

案例:

xml 复制代码
<template>
  <div id="home">
    <appMain :data="msg" />
  </div>
</template>
<script>
  // 导入组件
  import appMain from "./appMain.vue";
  export default {
    // 注册组件
    components: {
      appMain,
    },
    data() {
      return {
        msg: [
          { id: 1, username: "zhangsan" },
          { id: 2, username: "lisi" },
        ],
      };
    },
  };
</script>
xml 复制代码
<template>
  <div id="main">
    <p>{{ data }}</p>
    <!-- <main-aside></main-aside>
    <main-list></main-list> -->
  </div>
</template>
<script>
  export default {
    props: {
      data: {
        type: Array,
        required: true,
        validator(value) {
          if (!value.length) {
            console.error("不能为空数据");
            return false;
          }
          for (let i = 0; i < value.length; i++) {
            if (!value[i].id) {
              console.error("有一个数据没有ID");
              return false;
            }
          }
          return true;
        },
      },
    },
  };

如果不符合设定的规则就会抛出对应的错误

3. 子传父

xml 复制代码
<template>
  <div id="father">
    {{ msg }}
    {{ msg2 }}
    //第二步 
    <childComponent :func="func" />
  </div>
</template>
<script>
  import childComponent from "./childComponent.vue";
  export default {
    data() {
      return {
        msg: "父组件",
        msg2: "",
      };
    },
    components: {
      childComponent,
    },
    methods: {
      // 第一步
      func(value) {
        this.msg2 = value;//value就是子组件传递到父组件的数据
      },
    },
  };
</script>
xml 复制代码
<template>
  <div id="child">
    {{ msg }}
    // 第四步
    <button @click="send">触发</button>
  </div>
</template>
<script>
  export default {
    props: ["func"],//第三步
    data() {
      return {
        msg: "子组件",
        msg1: "子组件中的数据",
      };
    },
    methods: {
      // 第五步
      send() {
        this.func(this.msg1);
      },
    },
  };
</script>

本质:

  1. 首先通过父传子,传递一个函数给子组件
  2. 子组件通过 props 接收传递来的函数
  3. 在子组件中定义一个函数,通过触发子组件的函数,来间接的触发父组件传递给子组件的函数,同时将想要传递给父组件的数据携带在父组件传递来的函数的参数中
  4. 在父组件中的那个函数的参数就是子组件实际传递给父组件的数据

以上的方式过程可能有些复杂,不是很间接,接下来就再来说一种简洁的方式:

3.1. 使用$emit 自定义事件来实现子传父

xml 复制代码
<template>
  <div id="father">
    {{ msg }}
    {{ msg2 }}
    <childComponent @customEvent="parentFn" />//第四步
  </div>
</template>
<script>
  import childComponent from "./childComponent.vue";
  export default {
    data() {
      return {
        msg: "父组件",
        msg2: "",
      };
    },
    components: {
      childComponent,
    },
    methods: {
      parentFn(value) {
        this.msg2 = value;//第五步:value就是子组件传递来的数据
      },
    },
  };
</script>
<style scoped lang="scss">
  #father {
    width: 500px;
    height: 300px;
    border: 2px solid #000;
    padding: 30px;
    box-sizing: border-box;
    margin: 100px auto;
  }
</style>
xml 复制代码
<template>
  <div id="child">
    {{ msg }}
    <button @click="send">触发</button>//第一步
  </div>
</template>
<script>
  export default {
    props: ["func"],
    data() {
      return {
        msg: "子组件",
        msg1: "子组件中的数据",
      };
    },
    methods: {
      send() {
        this.$emit("customEvent", this.msg1);//第二步
      },
    },
  };
</script>
<style scoped lang="scss">
  #child {
    width: 300px;
    height: 200px;
    border: 2px solid #e94b4b;
    margin: 20px;
  }
</style>

描述:

  1. 首先在子组件中创建触发事件
  2. 在事件中去通过 <math xmlns="http://www.w3.org/1998/Math/MathML"> e m i t 自定义一个事件, emit 自定义一个事件, </math>emit自定义一个事件,emit 接收两个参数:第一个参数表示事件名称,第二个参数就是子组件传递给父组件的数据
  3. 然后在父组件中的子组件标签中绑定自定义的事件
  4. 然后再父组件中使用绑定的这个事件,并且这个事件的参数就是子组件传递给父组件的数据

相比较之前那种传递方式,这种传递方式更简洁一些,省去了在子组件中接收父组件传递来的函数,反之去使用$emit 直接去创建事件来实现最终的子传父。

4. 使用 ref 实现组件之间的通信

ref 除了可以获取本页面的 dom 元素之外,也可以通过引用直接访问子组件的实例,当父组件中需要主动获取子组件中的数据或者方法的时候,可以使用$refs 来获取

4.1. 访问子组件

xml 复制代码
<template>
  <div id="father">
    {{ msg }}
    <childComponent ref="refChild" />//第一步
    <button @click="fn">获取子组件实例</button>//第二步,绑定事件来主动获取子组件数据
  </div>
</template>
<script>
import childComponent from "./childComponent.vue";
export default {
  data() {
    return {
      msg: "父组件",
    };
  },
  components: {
    childComponent,
  },
  methods: {
    fn() {
      console.log(this.$refs.refChild.msg1);//第三步:通过this.$refs.refChild来获取子组件实例
    },
  },
};
</script>
xml 复制代码
<template>
  <div id="child">
    {{ msg }}
  </div>
</template>
<script>
export default {
  data() {
    return {
      msg: "子组件",
      msg1: "子组件中的数据",
    };
  },
};
</script>

描述:

  1. 通过在子组件标签中定义 ref 属性来绑定 id 引用
  2. 然后通过在事件中使用this.$refs.refChild来获取子组件实例就可以访问到子组件

4.2. ref 子传父

xml 复制代码
<template>
  <div id="father">
    {{ msg }}
    {{msg1}}
    <childComponent ref="child" />//第三步:通过ref绑定自定义事件名称
  </div>
</template>
<script>
import childComponent from "./childComponent.vue";
export default {
  data() {
    return {
      msg: "父组件",
      msg1:'',
    };
  },
  components: {
    childComponent,
  },
  methods: {
    getChildName(val) {
      this.msg1 = val
    },
  },
  //在挂载后执行
  mounted() {
    this.$refs.child.$on("child", this.getChildName);//通过$on来监听子组件实例中的$emit,$on的第二个参数是函数,函数的参数就是子组件传递的数据
  },
};
</script>
xml 复制代码
<template>
  <div id="child">
    {{ msg }}
    <button @click="sendSchoolName">传递数据给父组件</button>//第一步
  </div>
</template>
<script>
export default {
  data() {
    return {
      msg: "子组件",
      msg1: "子组件中的数据",
    };
  },
  methods: {
    // 第二步通过触发子组件中的函数来自定义一个事件,并传递数据
    sendSchoolName() {
      // child是自定义的事件名,"金小子"就是要传递给父组件的参数
      this.$emit("child", "金小子");
    },
  },
};
</script>

描述:

  1. 在子组件中创建触发事件
  2. 在子组件的触发事件中创建自定义事件并传递子组件数据
  3. 然后在父组件中的子组件标签中通过 ref 绑定自定义事件名称为 ref 的 id 引用
  4. 在父组件中的挂在后的周期钩子中执行this.$refs.child.$on("child", this.getChildName);,通过 <math xmlns="http://www.w3.org/1998/Math/MathML"> o n 来监听子组件实例中的 on来监听子组件实例中的 </math>on来监听子组件实例中的emit,$on的第二个参数是函数,函数的参数就是子组件传递的数据

需要注意的是接收数据的时候,需要在父组件中的 data 中定义一个数据变量接收一下传递来的数据

$on 说明:

监听当前实例上的自定义事件。事件可以由 vm.$emit 触发。回调函数会接收所有传入事件触发函数的额外参数。

php 复制代码
vm.$on('test', function (msg) {
  console.log(msg)
})
vm.$emit('test', 'hi')
// => "hi"

需要强调一个事情,vue 规定谁触发了自定义事件,那么回调中的 this 指向的就是谁

4.3. ref 父传子

xml 复制代码
<template>
  <div id="father">
    {{ msg }}
    <childComponent ref="child" />
    <button @click="secd">传递给子组件</button>
  </div>
</template>
<script>
import childComponent from "./childComponent.vue";
export default {
  data() {
    return {
      msg: "父组件",
    };
  },
  components: {
    childComponent,
  },
  methods: {
    secd() {
      this.$refs.child.children(this.msg);
    },
  },
};
</script>
xml 复制代码
<template>
  <div id="child">
    {{ msg }}
  </div>
</template>
<script>
export default {
  data() {
    return {
      msg: "子组件",
      msg1: "子组件中的数据",
    };
  },
  methods: {
    children(value) {
      console.log(value);
    },
  },
};
</script>

描述:

  1. 在父组件中的子组件标签中绑定 ref 的 id 引用
  2. 在父组件中的事件中通过this.$refs.child获取到子组件实例,然后调用子组件中的方法
  3. 通过这个方法将要传递的数据作为函数的参数传递给子组件
  4. 子组件中函数的参数就是父组件传递的数据

4.4. 访问后代组件

本质就是链式的调用this.$refs.child

bash 复制代码
this.$refs.child.$refs.groundson

必须在对应的组件标签中绑定对应的 id 引用,比如child、groundson

5. props 和 ref 的区别

  1. props 着重于数据的传递,它并不能调用子组件的属性 data 和方法 methods,适合父传子
  2. $ref 着重于索引,主要用来调用子组件里的属性和方法,其实并不擅长数据传递,但是也可以传递参数
相关推荐
天才熊猫君1 小时前
npm 和 pnpm 的一些理解
前端
飞飞飞仔1 小时前
从 Cursor AI 到 Claude Code AI:我的辅助编程转型之路
前端
qb1 小时前
vue3.5.18源码:调试方式
前端·vue.js·架构
Spider_Man1 小时前
缓存策略大乱斗:让你的页面快到飞起!
前端·http·node.js
前端老鹰1 小时前
CSS overscroll-behavior:解决滚动穿透的 “边界控制” 专家
前端·css·html
一叶怎知秋1 小时前
【openlayers框架学习】九:openlayers中的交互类(select和draw)
前端·javascript·笔记·学习·交互
allenlluo2 小时前
浅谈Web Components
前端·javascript
Mintopia2 小时前
把猫咪装进 public/ 文件夹:Next.js 静态资源管理的魔幻漂流
前端·javascript·next.js
Spider_Man2 小时前
预览一开,灵魂出窍!低代码平台的魔法剧场大揭秘🎩✨
前端·低代码·typescript
xianxin_2 小时前
HTML 代码编写规范
前端