Learning Vue 读书笔记 Chapter 4

4.1 Vue中的嵌套组件和数据流

我们将嵌套的组件称为子组件,而包含它们的组件则称为它们的父组件

父组件可以通过 props向子组件传递数据,而子组件则可以通过自定义事件(emits)向父组件发送事件。

4.1.1 使用Props向子组件传递数据

在 Vue 组件中,props 字段以对象或数组的形式存在,它包含了该组件可从父组件接收的所有可用数据属性。props 对象的每个属性都对应着目标组件的一个 prop(属性)。若要使组件能够接收父组件传递的数据,您需要在组件的选项对象中声明 props 字段。

在一个子组件中定义props

js 复制代码
export default { 
	name: 'ChildComponent', 
	props: { 
		name: String 
	} 
}

将动态变量作为属性传递给子组件。每当children[0]的值发生变化时,Vue也会更新ChildComponent中的name属性,如果需要,子组件将重新渲染其内容。

js 复制代码
<template>
  <ChildComponent :name="children[0]" />
</template>

<script lang="ts">
import ChildComponent from './ChildComponent.vue';

export default {
  data() {
    return {
      children: ['Red Sweater', 'Blue T-Shirt', 'Green Hat'],
    };
  },
};
</script>

如果name属性不是字符串类型,需要使用v-bind属性(或:)来向子组件传递静态数据,例如:对于布尔类型使用 :name="true",对于数组类型使用 :name="['hello', 'world']"

使用v-bind(不是:)来传递整个对象,并将其属性绑定到相关子组件的props上:

js 复制代码
<template> 
   <ProductComp v-bind="product" /> 
</template>
4.1.2 声明带验证和默认值的Prop类型

将prop定义为带有默认值的字符串

js 复制代码
export default {
  name: 'ChildComponent',
  props: {
    name: {
      type: String,
      default: 'Child component'
    }
  }
}
4.1.3 使用自定义类型检查声明Props

直接将pizza prop 的类型声明为Pizza类

js 复制代码
class Pizza {
  title: string;
  description: string;
  image: string;
  quantity: number;
  price: number;

  constructor(
    title: string,
    description: string,
    image: string,
    quantity: number,
    price: number
  ) {
    this.title = title;
    this.description = description;
    this.image = image;
    this.quantity = quantity;
    this.price = price;
  }
}

export default {
  name: 'PizzaComponent',
  props: {
    pizza: {
      type: Pizza,
      required: true
    }
  }
}

或者,你可以使用TypeScript的接口或类型来定义你的自定义类型,而不是使用类。然而,在这种情况下,你必须使用vue包中的PropType类型,并按照以下语法,将声明的类型映射到目标prop

js 复制代码
type: Object as PropType<Your-Custom-Type>
js 复制代码
import type { PropType } from 'vue';

interface Pizza {
  title: string;
  description: string;
  image: string;
  quantity: number;
  price: number;
}

export default {
  name: 'PizzaComponent',
  props: {
    pizza: {
      type: Object as PropType<Pizza>,
      required: true,
    },
  },
};
4.1.4 使用defineProps()和withDefaults() 定义Props

使用defineProps和

js 复制代码
<script setup>
import { defineProps } from 'vue';

const props = defineProps({
  name: {
    type: String,
    default: "Hello from the child component.",
  },
});
</script>

使用defineProps()和TypeScript 类型声明Props (?代表该type中name属性可有可无)

js 复制代码
<script setup>
import { defineProps } from 'vue';

type ChildProps = {
  name?: string;
};

const props = defineProps<ChildProps>();
</script>

使用defineProps() 和withDefaults()声明Props

js 复制代码
import { defineProps, withDefaults } from 'vue';

type ChildProps = {
  name?: string;
};

const props = withDefaults(defineProps<ChildProps>(), {
  name: 'Hello from the child component.',
});

4.2 使用自定义事件进行组件交流

Vue将传递给子组件的props数据视为只读的原始数据。单向数据流确保只有父组件可以更新数据属性。我们通常希望更新特定的数据属性并将其与父组件同步。为此,我们使用组件选项中的emits字段来声明自定义事件。

以ToDoList组件为例。这个ToDoList将使用ToDoItem作为其子组件来渲染任务列表,代码如下:

ToDoList Component

js 复制代码
<template>
  <ul style="list-style: none;">
    <li v-for="task in tasks" :key="task.id">
      <ToDoItem :task="task"  
      @task-completed-toggle="onTaskCompleted"/>
    </li>
  </ul>
</template>

<script lang="ts">
import { defineComponent } from 'vue';
import ToDoItem from './ToDoItem.vue';
import type { Task } from './ToDoItem';

export default defineComponent({
  name: 'ToDoList',
  components: {
    ToDoItem,
  },
  data() {
    return {
      tasks: [
        { id: 1, title: 'Learn Vue', completed: false },
        { id: 2, title: 'Learn TypeScript', completed: false },
        { id: 3, title: 'Learn Vite', completed: false },
      ] as Task[],
    };
  },
  methods: {
  onTaskCompleted(payload: { id: number; completed: boolean }) {
    const index = this.tasks.findIndex(t => t.id === payload.id);
    if (index < 0) {
      return;
    }
    this.tasks[index].completed = payload.completed;
  }
}

});
</script>

ToDoItem Componet

js 复制代码
<template>
  <div>
    <input type="checkbox" 
    :checked="task.completed"  
    @change="onTaskCompleted"/>
    <span>{{ task.title }}</span>
  </div>
</template>

<script lang="ts">
import { defineComponent, type PropType } from 'vue';

export interface Task {
  id: number;
  title: string;
  completed: boolean;
}

export default defineComponent({
  name: 'ToDoItem',
  props: {
    task: {
      type: Object as PropType<Task>,
      required: true,
    },
  },
  emits: ['task-completed-toggle'],
  methods: {
  onTaskCompleted(event: Event) {
    this.$emit("task-completed-toggle", {
      ...this.task,
      completed: (event.target as HTMLInputElement)?.checked,
    });
  }
}

});
</script>

4.3 使用defineEmits()定义自定义事件

ToDoTtem使用defineEmits() 定义自定义事件

js 复制代码
<script lang="ts" setup>
  //...

  const props = defineProps({
    task: {
      type: Object as PropType<Task>,
      required: true,
    },
  });

  const emits = defineEmits(['task-completed-toggle']);

  const onTaskCompleted = (event: Event) => {
    emits("task-completed-toggle", {
      id: props.task.id,
      completed: (event.target as HTMLInputElement)?.checked,
    });
  }
</script>

4.4 使用provide/inject 进行组件沟通

4.4.1 使用provide 传递数据

组件的option 领域 provide 接受两种格式:数据对象函数。 provide 可以是一个包含要注入数据的对象,每个属性代表一个(键,值)数据类型。在以下示例中,ProductList 向其所有后代提供了数据值 selectedIds,其值为 [1]。

js 复制代码
<script>
export default {
  name: 'ProductList',
  // ...
  provide: {
    selectedIds: [1]
  },
}
</script>

provide 的另一种格式类型是一个返回对象的函数,该对象包含可用于注入后代的数据。这种格式类型的一个好处是,我们可以访问 this 实例,并将动态数据或组件方法映射到返回对象的相应字段。

js 复制代码
<script>
export default {
  // ...
  provide() {
    return {
      selectedIds: [1]
    };
  },
  // ...
}
</script>
4.4.2 使用 inject接收数据

在这段代码中,Vue将获取注入的selectedIds的值并将其赋给一个本地数据字段currentSelectedIds,如果没有注入值,则使用其默认值[]。

js 复制代码
<script lang='ts'>
export default {
  // ...
  inject: {
    currentSelectedIds: {
      from: 'selectedIds',
      default: [],
    },
  },
}
</script>
相关推荐
崔庆才丨静觅12 小时前
实用免费的 Short URL 短链接 API 对接说明
前端
崔庆才丨静觅12 小时前
5分钟快速搭建 AI 平台并用它赚钱!
前端
崔庆才丨静觅13 小时前
比官方便宜一半以上!Midjourney API 申请及使用
前端
Moment13 小时前
富文本编辑器在 AI 时代为什么这么受欢迎
前端·javascript·后端
崔庆才丨静觅13 小时前
刷屏全网的“nano-banana”API接入指南!0.1元/张量产高清创意图,开发者必藏
前端
剪刀石头布啊13 小时前
jwt介绍
前端
爱敲代码的小鱼13 小时前
AJAX(异步交互的技术来实现从服务端中获取数据):
前端·javascript·ajax
吹牛不交税13 小时前
admin.net-v2 框架使用笔记-netcore8.0/10.0版
vue.js·.netcore
Cobyte13 小时前
AI全栈实战:使用 Python+LangChain+Vue3 构建一个 LLM 聊天应用
前端·后端·aigc
NEXT0614 小时前
前端算法:从 O(n²) 到 O(n),列表转树的极致优化
前端·数据结构·算法