CSS作用域

作用域即使加了scoped,也会有穿透,这应该是Vue的BUG。
解决方案:在<template></template>标签下加一个根div

组件通信-父子

01.父->子:props

props数组用法(用的不多)

image.png

props对象用法(可以做更多约束)

类型约束示例

image.png

可以传入的类型

  • String
  • Number
  • Boolean
  • Array
  • Object
  • Date
  • Function
  • Symbol

Prop的大小写问题
image.png
非Prop的Attribute
image.png
image.png

02.子->父:事件

同Props,事件的定义方式也是有两种:数组方式&对象方式。
对象方式同样也是为了验证。

  1. <template>
  2. <div>
  3. <button @click="increment">+1</button>
  4. <button @click="decrement">-1</button>
  5. <input type="text" v-model.number="num">
  6. <button @click="incrementN">+n</button>
  7. </div>
  8. </template>
  9. <script>
  10. export default {
  11. // emits: ["add", "sub", "addN"],
  12. // 对象写法的目的是为了进行参数的验证
  13. emits: {
  14. add: null,
  15. sub: null,
  16. addN: (num, name, age) => {
  17. console.log(num, name, age);
  18. // 以下便是数据验证
  19. if (num > 10) {
  20. return true
  21. }
  22. return false;
  23. }
  24. },
  25. data() {
  26. return {
  27. num: 0
  28. }
  29. },
  30. methods: {
  31. increment() {
  32. console.log("+1");
  33. this.$emit("add");
  34. },
  35. decrement() {
  36. console.log("-1");
  37. this.$emit("sub");
  38. },
  39. incrementN() {
  40. this.$emit('addN', this.num, "why", 18);
  41. }
  42. }
  43. }
  44. </script>
  1. <template>
  2. <div>
  3. <h2>当前计数: {{counter}}</h2>
  4. <counter-operation @add="addOne"
  5. @sub="subOne"
  6. @addN="addNNum">
  7. </counter-operation>
  8. </div>
  9. </template>
  10. <script>
  11. import CounterOperation from './CounterOperation.vue';
  12. export default {
  13. components: {
  14. CounterOperation
  15. },
  16. data() {
  17. return {
  18. counter: 0
  19. }
  20. },
  21. methods: {
  22. addOne() {
  23. this.counter++
  24. },
  25. subOne() {
  26. this.counter--
  27. },
  28. addNNum(num, name, age) {
  29. console.log(name, age);
  30. this.counter += num;
  31. }
  32. }
  33. }
  34. </script>

03.综合小案例:Tab栏切换

  1. <template>
  2. <div class="tab-control">
  3. <div class="tab-control-item"
  4. :class="{ active: currentIndex === index }"
  5. v-for="(title, index) in titles"
  6. :key="title"
  7. @click="itemClick(index)">
  8. <span>{{ title }}</span>
  9. </div>
  10. </div>
  11. </template>
  12. <script lang="ts" setup>
  13. import { PropType, ref } from 'vue';
  14. const emit = defineEmits(['titleClick'])
  15. const props = defineProps({
  16. titles: {
  17. type: Array as PropType<string[]>,
  18. required: true
  19. }
  20. })
  21. const currentIndex = ref(0)
  22. const itemClick = (index: number) => {
  23. currentIndex.value = index
  24. emit('titleClick', index) // 发射事件,并传入index参数
  25. }
  26. </script>
  27. <style scoped>
  28. .tab-control {
  29. display: flex;
  30. }
  31. .tab-control-item {
  32. flex: 1;
  33. text-align: center;
  34. }
  35. .tab-control-item.active {
  36. color: red;
  37. }
  38. .tab-control-item.active span {
  39. border-bottom: 3px solid red;
  40. /** 这个padding太妙了呀!!! */
  41. padding: 5px 10px;
  42. }
  43. </style>

上面这个组件中用到的知识点非常多:

  • 动态样式,:class必须以冒号开头
  • <script setup>中定义事件和发射事件的方法:用定义事件后返回的对象来发射事件,优雅!
  • css技巧:原来Tab栏的border-bottom比字体要而且离字体有些距离,是通过设置span的padding值来实现的!!! ```vue

```

04.爷爷->孙子: 依赖注入

直接看官网:https://staging-cn.vuejs.org/guide/components/provide-inject.html

05.兄弟之间

可以设置一个公共父组件
也可以直接使用Pinia!