函数式组件
# 题目
<script setup>
import { ref } from "vue";
/**
* 实现该函数式组件 :
* 1. 使用`list`数据渲染列表元素 (ul/li)
* 2. 当点击列表子元素时,将其文本颜色更改为红色
*/
const ListComponent = () => {};
const list = [
{
name: "John",
},
{
name: "Doe",
},
{
name: "Smith",
},
];
const activeIndex = ref(0);
function toggle() {
activeIndex.value = index;
}
</script>
<template>
<list-component :list="list" :active-index="activeIndex" @toggle="toggle" />
</template>
# 题目分析
这个题目要求我们实现一个函数式组件ListComponent
, 并且要求我们使用list
数据渲染列表元素, 并且当点击列表子元素时, 将其文本颜色更改为红色。
- 我们需要知道, 什么叫做函数式组件 (opens new window)
- 分析
list-component
组件- 它接收一个
list
属性, 并且渲染一个ul
列表元素 - 它接收一个
active-index
属性, 并且渲染一个li
列表元素 - 它接收一个
toggle
事件, 并且当点击li
列表子元素时, 触发该事件
- 它接收一个
# 实现
- 对于
声明式组件
,就是用一个.vue
文件,在里面写 template、script、style,哪里需要就在哪里导入 - 对于
函数式组件
,在封装时除了要写.vue
(甚至可以不写,直接写虚拟 DOM),还要多一个手动渲染
和卸载
的步骤,就要用到render 函数
和h 函数
了。
h()
函数用于辅助创建虚拟 DOM 节点,它是 hypescript
的简称————能生成 HTML
(超文本标记语言) 的 JavaScript
,它有另外一个名称,叫做 createVnode()
。
h()函数接收参数如下:
type
:类型参数,必填。内容为字符串或者 Vue 组件定义。props
:props 参数,非必填。传递内容是一个对象,对象内容包括了即将创建的节点的属性,例如id
、class
、style
等,节点的事件监听也是通过props
参数进行传递,并且以on
开头,以onXxx
的格式进行书写,如onInput
、onClick
等。children
:子节点,非必填。内容为字符串、数字、布尔值、数组、对象等,可以传递多个子节点, 内容可以是文本
、虚拟 DOM 节点
和插槽
等等。
<script setup>
import { ref, h } from "vue";
const ListComponent = (props, context) => {
const { list, activeIndex } = props;
const { emit } = context;
let listLi = list.map((e, index) => {
return h(
"li",
{
style: {
color: index === activeIndex ? "red" : "",
},
onClick: () => {
emit("toggle", index);
},
},
e.name
);
});
return h("ul", list);
};
const list = [
{
name: "John",
},
{
name: "Doe",
},
{
name: "Smith",
},
];
const activeIndex = ref(0);
function toggle(index) {
activeIndex.value = index;
}
</script>
<template>
<list-component :list="list" :activeIndex="activeIndex" @toggle="toggle" />
</template>
# 解析
ListComponent
组件props
:组件的props
属性context
:context
参数是一个对象,包含组件的attrs
、slots
和emit
property
- 提取
ListComponent
组件的属性和事件
const { list, activeIndex } = props;
const { emit } = context;
- 编写列表内容
const listLi = list.map((e, index) => {
return h(
"li", // 组件tag类型
{
// 配置
style: {
color: index === activeIndex ? "red" : "", // 当选中时,给当前的文本赋值为红色
},
onClick: () => {
emit("toggle", index); // 点击文本将当前文本的索引赋值给选中的值activeIndex
},
},
e.name // 文案
);
});
- 返回列表,将 li 标签的列表包在 ul 标签下
const ListComponent = (props, context) => {
const { list, activeIndex } = props;
const { emit } = context;
let listLi = list.map((e, index) => {
return h(
"li",
{
style: {
color: index === activeIndex ? "red" : "",
},
onClick: () => {
emit("toggle", index);
},
},
e.name
);
});
return h("ul", list);
};