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>

子组件:

<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>

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>

子组件:

<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>

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>

子组件:

<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>

在这里插入图片描述

二、非父子组件

在这里插入图片描述

注: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>

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>

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>

2、事件总线

在这里插入图片描述

在这里插入图片描述

在这里插入图片描述

三、slot

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

在这里插入图片描述

在这里插入图片描述

在这里插入图片描述

在这里插入图片描述

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

在这里插入图片描述

在这里插入图片描述

在这里插入图片描述

在这里插入图片描述

四、动态组件

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

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

在这里插入图片描述

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

注:一般配合router一起使用

在这里插入图片描述

在这里插入图片描述

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

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

在这里插入图片描述

在这里插入图片描述

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

在这里插入图片描述

六、引用元素和组件

在这里插入图片描述

在这里插入图片描述

七、生命周期

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

  • 2
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值