• vue3父子props 非props emit slot 具名/动态/作用域slot 动态/异步组件suspense $refs 生命周期


    项目代码地址:https://github.com/chenfenbgin/vue-component/tree/master

    在这里插入图片描述

    一、父子组件

    在这里插入图片描述

    1、父组件传递给子组件

    两种方式: 数组 和 对象类型

    在这里插入图片描述

    在这里插入图片描述

    在这里插入图片描述
    在这里插入图片描述

    注意:带有默认值的对象,对象或数组默认值必须从一个工厂函数中获取
    ,因为如果有多次引用该组件,防止他指向的对象不变。(引用赋值的关系),这里需要不同的对象。

    在这里插入图片描述

    在这里插入图片描述

    非props注意点:

    1.继承 非props的属性

    在这里插入图片描述

    2.禁用 非props的属性

    在这里插入图片描述
    父组件:

    <template>
      <div id="app">
        <!-- 这里可以传递多个 -->
        <!-- 这里传的非props的属性,会被添加到子组件的根节点上(在有根组件的前提下继承) -->
        <show-message
          class="chen"
          id="idchen"
          title="哈哈哈"
          content="我是哈哈哈"
        ></show-message>
        <show-message title="嘻嘻嘻" content="我是嘻嘻嘻"></show-message>
        <show-message :title="title" :content="content"></show-message>
    
        <!-- 可以有两种方式:
            一种就是messsage.title; 
            另一种就是v-bind='对象'
        -->
    
        <!-- 方式一 -->
        <show-message
          :title="message.title"
          :content="message.content"
        ></show-message>
    
        <!-- 方式二, 传递的是一个对象 -->
        <show-message v-bind="message"></show-message>
      </div>
    </template>
    
    <script type="text/javascript">
    import ShowMessage from "./ShowMessage.vue";
    
    export default {
      data() {
        return {
          title: "嘻嘻嘻",
          content: "嘻嘻嘻",
          message: {
            title: "嘿嘿嘿",
            content: "我是嘿嘿嘿",
          },
        };
      },
      components: {
        ShowMessage,
      },
    };
    </script>
    
    <style scoped></style>
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45
    • 46
    • 47
    • 48
    • 49
    • 50

    子组件:

    <template>
      <!-- 父组件传递过来的非props属性 -->
      <!-- <div class="chen"> -->
      <div>
        <!-- 父组件传递过来的title 和 content -->
        <!-- <h2>{{ title }}</h2> -->
    
        <!-- 从非props取出class只给h2标签 -->
        <!-- <h2 :class="$attrs.class">{{ title }}</h2> -->
    
        <!-- 如果这里也有id,class属性的话,可以直接使用v-bind  -->
        <h2 :="$attrs">{{ title }}</h2>
        <p>{{ content }}</p>
      </div>
    </template>
    
    <script type="text/javascript">
    export default {
      name: "ShowMessage",
      inheritAttrs: false, //不希望组件的根元素继承attribute
      /**
       * 父组件之间通信
       *    Props: 是你可以在组件上注册一些自定义的 attribute
       *    父组件给这些 attribute赋值,子组件通过attribute 的名称获取对应的值
       *
       *  用法:
       *    方式一: 字符串数组
       *    方式二: 对象数组
       */
    
      // 父传子方式一: 数组
      // props: ["title", "content"],
    
      // 父传子方式二: 对象
      props: {
        title: String,
        content: {
          type: String, //类型:   String Number Boolean Array Object Date Function Symbol
          required: true, //必传
          default: "2134", // 默认值
        },
        message: [String, Number], //多个可能的类型,
    
        messageObject: {
          type: Object,
          // 对象或者数组必须从一个工厂函数获取, 因为使用多个组件,其中一个把值改了,那就不是我们要的效果了
          default() {
            return {
              messageObject: "hello messageObject",
            };
          },
        },
    
        //自定义验证函数
        validatorFunction: {
          validator(value) {
            return ["success", "warning", "danger"].includes(value);
          },
        },
    
        // 具有默认值的函数
        propG: {
          type: Function,
          default() {
            return "Default function";
          },
        },
      },
      data() {
        return {};
      },
      components: {},
    };
    </script>
    
    <style scoped></style>
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45
    • 46
    • 47
    • 48
    • 49
    • 50
    • 51
    • 52
    • 53
    • 54
    • 55
    • 56
    • 57
    • 58
    • 59
    • 60
    • 61
    • 62
    • 63
    • 64
    • 65
    • 66
    • 67
    • 68
    • 69
    • 70
    • 71
    • 72
    • 73
    • 74
    • 75
    • 76

    2、子组件传递给父组件

    在这里插入图片描述

    在这里插入图片描述

    在这里插入图片描述

    父组件:

    <template>
      <div id="app">
        <h2>当前计数: {{ counter }}</h2>
        <!-- emtis第三步:监听事件,使用的是v-on对事件监听,@add='要操作的函数' -->
        <counter-operation
          @add="increment"
          @sub="decrement"
          @addN="incrementN"
        ></counter-operation>
      </div>
    </template>
    
    <script type="text/javascript">
    import CounterOperation from "./CounterOperatin.vue";
    
    export default {
      name: "app",
      data() {
        return {
          counter: 100,
        };
      },
      components: {
        CounterOperation,
      },
      methods: {
        increment() {
          this.counter++;
        },
        decrement() {
          this.counter--;
        },
        incrementN(num, name, age) {
          console.log(name, age);
          this.counter += num;
        },
      },
    };
    </script>
    
    <style scoped></style>
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41

    子组件:

    <template>
      <div id="app">
        <button @click="increment">+1</button>
        <button @click="decrement">-1</button>
    
        <input type="text" v-model.number="num" />
        <button @click="incrementN">+n</button>
      </div>
    </template>
    
    <script type="text/javascript">
    export default {
      name: "CounterOperation",
      // emits第一种写法:数组写法, vue3里面的写法: 注册要触发的事件,子组件需要触发的事件
      // 第1步: 注册事件, vue3中,需要定义会触发那些事件,emits: []
      // emits: ["add", "sub", "addN"],
    
      // emits第二种写法: 对象写法, 对象写法的目的是为了进行参数的验证
      emits: {
        add: null, //没有参数,空就是不需要验证
        sub: null,
        addN: (num, name, age) => {
          //一个参数写为payload
          if (num > 10) {
            //  大于10才给它传过去,其实都会传过去,只是报一个警告
            return true;
          }
          console.log(num, name, age);
          return false;
        },
      },
      data() {
        return {
          num: 3,
        };
      },
      components: {},
      methods: {
        increment() {
          console.log("+1");
          // 第2步: 触发事件: this.$emit("")
          this.$emit("add");
        },
        decrement() {
          console.log("-1");
          this.$emit("sub");
        },
        incrementN() {
          this.$emit("addN", this.num, "chen", 24);
        },
      },
    };
    </script>
    
    <style scoped></style>
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45
    • 46
    • 47
    • 48
    • 49
    • 50
    • 51
    • 52
    • 53
    • 54
    • 55

    3、组件间通行案例 - 商品切换

    父组件:

    <template>
      <div id="app">
        <tab-control :titles="titles" @titleClick="titleClick"></tab-control>
        <h2>{{ content[currentIndex] }}</h2>
      </div>
    </template>
    
    <script type="text/javascript">
    import TabControl from "./TabControl.vue";
    
    export default {
      name: "app",
      data() {
        return {
          titles: ["衣服", "鞋子", "裤子"],
          content: ["衣服页面", "鞋子页面", "裤子页面"],
          currentIndex: 0,
        };
      },
      components: {
        TabControl,
      },
      methods: {
        titleClick(index) {
          console.log(index);
          this.currentIndex = index;
        },
      },
    };
    </script>
    
    <style scoped>
    </style>
    
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34

    子组件:

    <template>
      <div class="tab-control">
        <div
          :class="{ active: currentIndex === index }"
          class="tab-control-item"
          v-for="(item, index) of titles"
          :key="index"
          @click="itemClick(index)"
        >
          <span> {{ item }} </span>
        </div>
      </div>
    </template>
    
    <script type="text/javascript">
    export default {
      name: "TabControl",
      emits: ["titleClick"],
      props: {
        titles: {
          type: Array,
          default: () => {
            return [];
          },
        },
      },
      data() {
        return {
          currentIndex: 0,
        };
      },
      components: {},
      methods: {
        itemClick(index) {
          this.currentIndex = index;
          this.$emit("titleClick", index);
        },
      },
    };
    </script>
    
    <style scoped>
    .tab-control {
      display: flex;
    }
    
    .tab-control-item {
      /* 3个占比都是1份 */
      flex: 1;
      text-align: center;
    }
    
    .tab-control-item.active {
      color: red;
    }
    
    .tab-control-item.active span {
      border-bottom: 5px solid red;
      padding: 5px 5px;
    }
    </style>
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45
    • 46
    • 47
    • 48
    • 49
    • 50
    • 51
    • 52
    • 53
    • 54
    • 55
    • 56
    • 57
    • 58
    • 59
    • 60
    • 61

    在这里插入图片描述

    二、非父子组件

    在这里插入图片描述

    注:Vuex也是可以使用的(复杂数据使用)。provide和inject主要用在子孙组件

    1、Provide 和 Inject

    在这里插入图片描述
    在这里插入图片描述

    在这里插入图片描述

    在这里插入图片描述
    App.vue:

    <template>
      <div>
        <home></home>
        <button @click="addName">+name</button>
      </div>
    </template>
    
    <script type="text/javascript">
    import Home from "./Home.vue";
    import { computed } from "vue";
    // 对于有模块化的组件来说,这里的this是undefined
    // console.log(this); // undefined
    
    export default {
      name: "App",
      /**
       * 父组件: 有一个provide选项来提供数据
       * 子组件: 有一个inject选项来开始使用这些数据
       */
      // 对象的提供者
      // provide: {
      //   name: 'chen',
      //   age: 23,
      // },
    
      // 我们把provide改成函数写法,这里this,在源码中会被绑定到组件实例上
      provide() {
        console.log(this); //组件实例
        return {
          name: "chen",
          age: 23,
          // 如果有引用data里面的数据,需要将provide写成函数形式返回
          // length: this.names.length,
    
          // 这里this,箭头函数时不绑定的。computed返回的是一个ref对象,需要使用.value
          length: computed(() => this.names.length), //改为响应式(如果names改变了,就可以这么使用)
        };
      },
      data() {
        return {
          names: ["abc", "dfb", "dfdr"],
        };
      },
      components: {
        Home,
      },
      methods: {
        addName() {
          this.names.push("cen"); //  这里provide里面的name是不会改变的,需要将其改为响应式的
          console.log(this.names);
        },
      },
    };
    </script>
    
    <style scoped></style>
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45
    • 46
    • 47
    • 48
    • 49
    • 50
    • 51
    • 52
    • 53
    • 54
    • 55
    • 56

    Home.vue :

    <template>
      <div>
        <home-content></home-content>
      </div>
    </template>
    
    <script type="text/javascript">
    import HomeContent from "./HomeContent.vue";
    
    export default {
      name: "Home",
      data() {
        return {};
      },
      components: {
        HomeContent,
      },
    };
    </script>
    
    <style scoped></style>
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21

    HomeContent.vue:

    <template>
      <div>HomeContent: {{ name }}-{{ age }} - {{ length.value }}</div>
    </template>
    
    <script type="text/javascript">
    export default {
      name: "HomeContent",
      // 将App.vue中provide的属性进行注入
      inject: ["name", "age", "length"],
    
      data() {
        return {};
      },
      components: {},
    };
    </script>
    
    <style scoped></style>
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18

    2、事件总线

    在这里插入图片描述

    在这里插入图片描述

    在这里插入图片描述

    三、slot

    在这里插入图片描述
    在这里插入图片描述

    在这里插入图片描述

    在这里插入图片描述

    在这里插入图片描述

    在这里插入图片描述

    注:我们希望父组件中访问子组件的data,需要使用作用域插槽。

    在这里插入图片描述

    在这里插入图片描述

    在这里插入图片描述

    在这里插入图片描述

    四、动态组件

    如果是简单的东西,就没有必要使用路由了呢, 应该使用动态组件。

    在这里插入图片描述
    在这里插入图片描述

    在这里插入图片描述

    五、组件保留状态、webpack代码分包、异步组件

    注:一般配合router一起使用

    在这里插入图片描述

    在这里插入图片描述

    在这里插入图片描述
    在这里插入图片描述

    注:一般使用异步组件也是配合路由使用异步

    在这里插入图片描述

    在这里插入图片描述

    开发中异步组件一般和suspens一起使用。
    异步组件的使用是为了做代码分包。

    在这里插入图片描述

    六、引用元素和组件

    在这里插入图片描述

    在这里插入图片描述

    七、生命周期

    在这里插入图片描述
    在这里插入图片描述

  • 相关阅读:
    Java抽象类
    智能运维应用之道,告别企业数字化转型危机
    QTextStream(文本流)
    深度学习每周学习总结N1(one-hot 编码案例)
    Atcoder Beginner Contest 294
    瑞康医药与亚马逊云科技达成战略合作,全国上百家子公司业务上云
    Win Docker Desktop + WSL2 部署PyTorch-CUDA服务至k8s算力集群
    juddi实验过程
    项目一:使用 Spring + SpringMVC + Mybatis + lombok 实现网络五子棋
    C. Manipulating History
  • 原文地址:https://blog.csdn.net/Anna_Liqi/article/details/125474655