阅读 217

vue组件传值[持续更新]

组件传值介绍

vue作为现在前端使用的主流框架之一,组件一直是vue的核心部分。起初,我在学习vue的过程中接触到了组件之间传值的功能,但是由于在工作中使用较少,对于这一部分没有深入的认识,导致在开发过程中遇到了一些问题,通过进一步的学习,我了解到了vue组件传值的方式以及存在两类不同的组件传值方式:同级组件之间的传值、父子组件之间的传值

1.组件作用

组件作用:用来减少Vue实例(对象)中的代码量,日后在使用Vue开发过程中,可以根据不同的业务功能将页面中划分不同的多个组件,然后多个组件去构建整个页面的布局,便于日后使用Vue进行开发时页面管理(可以做到复用组件),方便日后的维护。

2.组件定义

2.1 全局组件注册

全局组件注册给Vue实例对象,定义之后可以在Vue实例的作用范围内使用该组件

<div id="app">
<!-- 必须给需要绑定的元素,设置选择器(支持CSS的选择器,但是我推荐使用ID选择,容器是唯一) --> 
    <h2>{{title}}</h2> 
    <!-- 使用全局注册组件,可以做到复用 -->
    <login></login> <login></login> </div>
    <!-- 1.先引入框架的支持(VUE文件),推荐位置body结束标签的前面(使用的是浏览器运行环境
    ,如果使用NodeJS是另一种引入方式) -->
<script src="https://cdn.jsdelivr.net/npm/vue/dist/vue.js"></script> 
<script> 
    // 定义全局组件 
    Vue.component('login',{
        template:'<diu><h2>用户登陆页面</h2></diu>' }); 
    let vm = new Vue({
        el: '#app', 
        data: { 
            title: '组件学习 - 全局组件注册' 
        } 
    }); 
</script>

复制代码

代码说明:

  1. Vue.component 用来开发全局组件
  • 参数1:定义组件名称login
  • 参数2:组件配置 {} template:'' 用来书写组件的html代码,template中必须只能存在一个root元素
  1. 使用时需要在Vue实例的作用范围内根据组件名称使用全局组件
  2. 如果在注册组件过程中使用,驼峰命名规则组件的方式,在使用组件时,必须将驼峰的所有单词小写加入中划线(-)进行使用,个人不太推荐驼峰。

2.2局部组件注册

局部组件注册将组件注册给对应Vue实例中一个 components属性 来完成组件注册,这种方式不会对Vue实例造成累加。

<div id="app">
   <!-- 必须给需要绑定的元素,设置选择器(支持CSS的选择器,但是我推荐使用ID选择,容器是唯一) -->
   <h2>{{title}}</h2>
   <login></login>
   <register></register>
   <user-table></user-table>
 </div>
 <!-- 第三种方式 -->
 <template id="userTemplate">
   <diu><h2>用户列表页面</h2></diu>
 </template>
 <!-- 1.先引入框架的支持(VUE文件),推荐位置body结束标签的前面(使用的是浏览器运行环境,如果使用NodeJS是另一种引入方式) -->
 <script src="https://cdn.jsdelivr.net/npm/vue/dist/vue.js"></script>
 <!-- 3.创建VUE对象(ViewModel中间件),数据Model -->
 <script>
   let reg = {
     template: "<diu><h2>用户注册页面</h2></diu>",
   };
   const userTemplate = {
     template: "#userTemplate",
   };
   let vm = new Vue({
     el: "#app",
     data: {
       title: "组件学习 - 局部组件注册",
     },
     components: {
       login: {
         //第一种方式:直接定义
         template: "<diu><h2>用户登陆页面</h2></diu>",
       },
       register: reg, //第二种方式:定义变量
       userTable: userTemplate, //第三种方式:注意我使用了驼峰的方式,脚手架推荐方式
     },
   });
 </script>
复制代码

3.关于父子组件的生命周期的执行过程,我会在后续增加

  • beforeCreate:组件开始初始化,仅仅注册组件自己事件和生命周期函数

  • created:组件已经注入data/methods/computed相关数据方法

  • beforeMount:将template中指向html编译Vue模版,此时还没有完成模版中内容渲染

  • mounted:将template中html编译模版进行数据渲染并且将渲染完成的数据再内存中形成虚拟dom替换template指向dom

  • beforeUpdate:当组件中data数据发生变化时,会触发beforeUpdate,此时页面中数据还是原始数据

  • updated:此时页面中数据和data属性一致

  • beforeDestroy:销毁VUE实例之前触发方法

  • destroy:VUE实例已经彻底销毁、监听进制全部消失

4.组件数据传递

20201016164141543.png

4.1(vue3以下组件传递)

4.1.1 父-->子(prop)

作用 props用来给组件传递相应静态数据或者动态数据

在子组件里定义一个props,即props:[msg],msg可以是对象也可以是基本数据类型

4.1.1.1父组件内容

<template>
  <div class="parent">
  //传递的数据   message
    <Children :msg="message"></Children>
  </div>
</template>

    <script>
     //引入的组件
      import Children from "../components/Children";

      export default {
        name: "Parent",
        components: {
          Children,
        },
        data() {
          return {
            message: "hello world",
          };
        },
      };
    </script>

复制代码

4.1.1.2子组件内容

    <template>
      <section>父组件传过来的消息是:{{myMsg}}</section>
    </template>

    <script>
      export default {
        name: "Children",
        components: {},
        //通过props接收到的内容
        props: ["msg"],
        data() {
          return {
          //可以通过this拿到传过来的数据
            myMsg: this.msg,
          };
        },
        methods: {},
      };
    </script>
复制代码

4.1.2子-->父(emit)

这里需要使用自定义事件,在子组件中使用this.$emit(‘myEvent’) 触发,然后在父组件中使用@myEvent监听

4.1.2.1 子组件

    <template>
      <section>
        <br />
        //声明点击事件进行传递
        <div @click="clickme">click me</div>
      </section>
    </template>

    <script>
      export default {
        name: "Children",
        components: {},
        data() {
          return {
            childNum: 0,
          };
        },
        methods: {
          clickme() {
            // 通过自定义事件addNum把值传给父组件
            this.$emit("addNum", this.childNum++);
          },
        },
      };
    </script>
复制代码

4.1.2.2 父组件

    <template>
      <div class="parent">
        这里是计数:{{parentNum}}
        //这里的addNum 是子组件传递的时候声明的时间  也就是子组件 this.$emit("addNum", this.childNum++);
        <Children-Com @addNum="getNum"></Children-Com>
      </div>
    </template>

    <script>
      import ChildrenCom from "../components/Children";

      export default {
        name: "Parent",
        components: {
          ChildrenCom,
        },
        data() {
          return {
            parentNum: 0,
          };
        },
        methods: {
          // childNum是由子组件传入的
          getNum(childNum) {
            this.parentNum = childNum;
          },
        },
      };
    </script>
复制代码

4.1.3兄弟组件传值(通过第三方声明bus组件进行相连)

运用自定义事件e m i t的触发和监听能力,定义一个公共的事件总线evenBus,通过它作为中间桥梁,我们就可以传值给任意组件了。而且通过eventBus的使用, 可以加深emit的触发和监听能力,定义一个公共的事件eventBus,通过它作为中间桥梁,我们就可以传值给任意组件了。而且通过eventBus的使用,可以加深emit的理解

4.1.3.1 bus组件

    import Vue from 'vue' 
    export default new Vue()
复制代码

4.1.3.2 Children1.vue(第一个兄弟组件)

    <template>
      <section>
        <div @click="Children1">push message</div>
        <br />
      </section>
    </template>

    <script>
    //引用bus组件
      import eventBus from "./EventBus";
      export default {
        name: "Children1",
        components: {},
        data() {
          return {
            childNum: 0,
          };
        },
        methods: {
          Children1() {
            // 通过事件总线发送消息
            eventBus.$emit("Children1", this.childNum++);
          },
        },
      };
    </script>
复制代码

4.1.3.3 Children2.vue(第二个兄弟组件)

    <template>
      <section>children1传过来的消息:{{msg}}</section>
    </template>

    <script>
    //引用bus组件
      import eventBus from "./EventBus";

      export default {
        name: "Children2",
        components: {},
        data() {
          return {
            msg: "",
          };
        },
        mounted() {
          // 通过事件总线监听消息
          eventBus.$on("Children1", (children1Msg) => {
            this.msg = children1Msg;
          });
        },
      };
    </script>
复制代码

4.1.3.4 把Children1和Children2放到一个组件里

    <template>
      <div class="parent">
        <Children1></Children1>
        <Children2></Children2>
      </div>
    </template>

    <script>
    //引入的两个兄弟组件 路径组件改变
      import Children1 from "../components/Children1";
      import Children2 from "../components/Children2";

      export default {
        name: "Parent",
        components: {
          Children1,
          Children2,
        },
        data() {
          return {};
        },
        methods: {},
      };
    </script>
复制代码

4.1.4 路由间传值

使用问号传值 A页面跳转B页面时使用 this.$router.push(’/B?name=danseek’)

B页面可以使用 this.$route.query.name 来获取A页面传过来的值

配置的路由如下

//这个就是router里面index.js的内容
    { 
        path: '/b/:name',
        name: 'b',
        component: () => import( '../views/B.vue')
    }
复制代码

4.2(vue3以上的组件传值)

4.2.1父传子(props)

4.2.1.1 父组件

    <template>
      <div class="home">
        <div>
          <Children :data="num" @fatherNums="shouFun"></Children>
        </div>
      </div>
    </template>

    <script>
      import { ref } from "vue";
      import Children from "../components/children";

      export default {
        name: "Home",
        components: {
          Children,
        },
        setup(props) {
          // 父传子组件
          const num = ref(18);
          return {
            num,
          };
        },
      };
    </script>
    
复制代码

4.2.1.2 子组件

 <template>
  <div>
    {{ num }}
  </div>
</template>

<script>
//需要引用的ref绑定
import { ref } from "vue";
export default {
  props: {
    data: Number,
  },
  setup(props, { emit }) {
  //接收也是props 
    const num = ref(props.data);
    return {
      num,
    };
  },
};
</script>
复制代码

4.2.2 子传父(emit)

4.2.2.1父组件

    <template>
      <div class="home">
        <div>
          <p>{{nums}}</p>
          <Children :data="num" @fatherNums="shouFun"></Children>
        </div>
      </div>
    </template>

    <script>
      import { ref } from "vue";
      //引入的组件
      import Children from "../components/children";

      export default {
        name: "Home",
        //是通过components属性来引入的组件
        components: {
          Children,
        },
        setup(props) {
          const nums = ref(null);
          // 父接子
          const shouFun = (val) => {
            console.log(val);
            nums.value = val;
          };

          return {
            nums,
          };
        },
      };
    </script>
复制代码

4.2.2.2 子组件

  <template>
      <div>
          <button @click="fatherNum">子传父</button>
      </div>
</template>

<script>
import { ref } from "@vue/reactivity";
export default {
  props: {
    data: Number,
  },
  setup(props, { emit }) {
  //通过emit和点击事件传递
    const fatherNum = () => {
      emit("fatherNums", 666);
    };
    return {
      fatherNum,
    };
  },
};
</script>
复制代码

总结

组件传递优点

props传递数据的优点显而易见、灵活简单,可以对props数据进行数据计算、数据监听等处理,十分灵活方便,但这里单单只是父子一层。

组件传递缺点

我们子组件中使用父组件props的时候,如果涉及到一些变量赋值,修改等操作,props被莫名其妙的修改了,连同父组件的数据也被篡改了,这让我们很疑惑,父组件的props不是不能修改吗?这里怎么变了,

其实在vue中的props能不能改变,这个得分情况。

props如果是基础数据类型,当改变时,就会抛出错误:

20210422221726802.png

当props是引用类型的话,我们修改这个数据的某一个属性的实话,就可以。

由此我们可以得出结论:子组件虽然不能直接对父组件prop进行重新赋值,但父组件是引用类型的时候,子组件可以修改父组件的props下面的属性

这就很尴尬了。如果我们设计的初衷就是父组件数据也能同时被修改,这个结果可以接受,如果不希望父组件的数据有变化是,这就是一个严重的逻辑bug。这就是props通讯的风险之一。

文章分类
阅读