从0开始的操作系统手搓教程24——完成我们的键盘驱动子系统

目录

所以,我们现来说说转义字符

我们需要如何处理扫描码

当键入的是双字符键时

当键入的是字母键时

下一篇


我们下面来看看我们的键盘驱动子系统是一个怎么个事情。

驱动程序,你可以认为是对硬件的一层封装。我们按照手册规格的规定姿势,封装好一套一套的流程,我们上层的软件想要使用这个硬件,就直接找这个驱动接口,一个调用完事。这就是一个驱动。

所以,我们现来说说转义字符

我们下面呢,就是要准备讨论一个重要的东西:叫转义字符。因为我们编写键盘驱动程序,就必须要理解键盘如何处理一部分不可见字符的。

我的意思是——我们的字符集中的字符,分为两个大类:可见的字符和不可见的字符。可见的字符太好说了,abcdefg....,不可见的字符,则是一些输入控制字符。我们如何输入控制字符呢?啊哈,转义字符嘛!

在 C 语言中有三种转义字符。

  • 一般转义字符,'+单个字母'的形式。

  • 八进制转义字符,'\0+三位八进制数字表示的 ASCII 码'的形式。

  • 十六进制转义字符,'\x+两位十六进制数字表示的 ASCII 码'的形式。

我们实际上,就是将我们的字符的通码,转换成可见的ASCII字符,发送到上面需要的子程序。这样就完事了。

我们需要如何处理扫描码

当按下的键是可见字符时,屏幕上都会将其显示出来,比如按下了 a 键,屏幕上应该输出字符'a',给用户一个反馈,这样用户才觉得自己没按错,这是为打造好的用户体验最起码的素质。 当按下的键是控制字符时,我们应该做出相应的控制行为,并在屏幕上展现出这种行为,比如按下了backspace 键,咱们也应该在屏幕上让用户觉得光标所在处前面的字符被删掉了。

如果是一些用于操作方面的控制键,简称操作控制键,如<shift><ctrl><caps lock>,它通常是组合键,需要与其他键一起考虑,然后做出具体的行为展现,在键盘驱动中完成处理。 ​ 如果是一些用于字符方面的键,无论是可见字符,或是字符方面的控制键(简称字符控制键),如<backspace>,统统交给字符处理程序完成,比如咱们的 __ccos_putchar。还记得吗?__ccos_putchar能够处理 <backspace>,也就是'\b'。

所以,我们需要做的就是将操作控制键,转化成正确的含义传递给字符处理程序。也就是正确的ASCII码给我们的 __ccos_putchar

我们首先定义一下keymap,不然的话

#ifndef KEYBOARD_MAPPINGS_H
#define KEYBOARD_MAPPINGS_H
#include "include/device/configs/keyboard_ascii.h"
​
// Key mappings table
static char keymap[[maybe_unused]][][2]  = {/* ---------------------------------- *//* 0x00 */  {0, 0},     /* 0x01 */  {ESC,   ESC},       /* 0x02 */  {'1',   '!'},       /* 0x03 */  {'2',   '@'},       /* 0x04 */  {'3',   '#'},       /* 0x05 */  {'4',   '$'},       /* 0x06 */  {'5',   '%'},       /* 0x07 */  {'6',   '^'},       /* 0x08 */  {'7',   '&'},       /* 0x09 */  {'8',   '*'},       /* 0x0A */  {'9',   '('},       /* 0x0B */  {'0',   ')'},       /* 0x0C */  {'-',   '_'},       /* 0x0D */  {'=',   '+'},       /* 0x0E */  {BACKSPACE, BACKSPACE}, /* 0x0F */  {TAB,   TAB},       /* 0x10 */  {'q',   'Q'},       /* 0x11 */  {'w',   'W'},       /* 0x12 */  {'e',   'E'},       /* 0x13 */  {'r',   'R'},       /* 0x14 */  {'t',   'T'},       /* 0x15 */  {'y',   'Y'},       /* 0x16 */  {'u',   'U'},       /* 0x17 */  {'i',   'I'},       /* 0x18 */  {'o',   'O'},       /* 0x19 */  {'p',   'P'},       /* 0x1A */  {'[',   '{'},       /* 0x1B */  {']',   '}'},       /* 0x1C */  {ENTER,  ENTER},/* 0x1D */  {CTRL_L_CHAR, CTRL_L_CHAR},/* 0x1E */  {'a',   'A'},       /* 0x1F */  {'s',   'S'},       /* 0x20 */  {'d',   'D'},       /* 0x21 */  {'f',   'F'},       /* 0x22 */  {'g',   'G'},       /* 0x23 */  {'h',   'H'},       /* 0x24 */  {'j',   'J'},       /* 0x25 */  {'k',   'K'},       /* 0x26 */  {'l',   'L'},       /* 0x27 */  {';',   ':'},       /* 0x28 */  {'\'',  '"'},       /* 0x29 */  {'`',   '~'},       /* 0x2A */  {SHIFT_L_CHAR, SHIFT_L_CHAR},   /* 0x2B */  {'\\',  '|'},       /* 0x2C */  {'z',   'Z'},       /* 0x2D */  {'x',   'X'},       /* 0x2E */  {'c',   'C'},       /* 0x2F */  {'v',   'V'},       /* 0x30 */  {'b',   'B'},       /* 0x31 */  {'n',   'N'},       /* 0x32 */  {'m',   'M'},       /* 0x33 */  {',',   '<'},       /* 0x34 */  {'.',   '>'},       /* 0x35 */  {'/',   '?'},/* 0x36 */  {SHIFT_R_CHAR, SHIFT_R_CHAR},   /* 0x37 */  {'*',   '*'},       /* 0x38 */  {ALT_L_CHAR, ALT_L_CHAR},/* 0x39 */  {' ',   ' '},       /* 0x3A */  {CAPS_LOCK_CHAR, CAPS_LOCK_CHAR}
};
#endif

这个keymap,更多定义的是是否又跟shift进行组合的Keymap表,我的意思是——当我们只是嗯下A这个键的时候,正常出来的就是keymap[0x1e][0],然后,就是嗯下Shift的时候,我们正确的索引就是keymap[0x1e][1],这个没啥毛病,出来的是A

在keyboard_ascii.h的中(当时名字取得不太好),存放的是我们的键盘控制相关的定义,请看下文

#ifndef KEYBOARD_ASCII_H
#define KEYBOARD_ASCII_H
​
/* Port and interrupt number for keyboard input */
#define KEYBOARD_BUF_PORT       (0x60)  // I/O port for keyboard buffer
#define KEYBOARD_INTERRUPT_N    (0x21)  // Keyboard interrupt number
​
/* ASCII control characters */
#define ESC         ('\x1b')  // Escape key
#define BACKSPACE   ('\b')    // Backspace key
#define TAB         ('\t')    // Tab key
#define ENTER       ('\r')    // Enter (carriage return)
#define DELETE      ('\x7f')  // Delete key
​
/* Invisible control characters (do not produce a visible output) */
#define INVISIBLE          (0)   // Represents an invisible keypress
#define CTRL_L_CHAR        INVISIBLE  // Left Control key
#define CTRL_R_CHAR        INVISIBLE  // Right Control key
#define SHIFT_L_CHAR       INVISIBLE  // Left Shift key
#define SHIFT_R_CHAR       INVISIBLE  // Right Shift key
#define ALT_L_CHAR         INVISIBLE  // Left Alt key
#define ALT_R_CHAR         INVISIBLE  // Right Alt key
#define CAPS_LOCK_CHAR     INVISIBLE  // Caps Lock key
​
/* Make codes for modifier keys (sent when key is pressed) */
#define SHIFT_L_MAKE       (0x2A)   // Left Shift key make code
#define SHIFT_R_MAKE       (0x36)   // Right Shift key make code
#define ALT_L_MAKE         (0x38)   // Left Alt key make code
#define ALT_R_MAKE         (0xE038) // Right Alt key make code (extended)
#define CTRL_L_MAKE        (0x1D)   // Left Control key make code
#define CTRL_R_MAKE        (0xE01D) // Right Control key make code (extended)
#define CAPS_LOCK_MAKE     (0x3A)   // Caps Lock key make code
​
/* Break codes for modifier keys (sent when key is released) */
#define ALT_R_BREAK        (0xE0B8) // Right Alt key break code (extended)
#define CTRL_R_BREAK       (0xE09D) // Right Control key break code (extended)
​
#endif

为了薄记我们的控制摁键的状态,笔者抽象了一个这样的结构体:

/* Defines a structure to record the status of special keys */
static struct {bool ctrl_status;      // Indicates whether the Ctrl key is pressedbool shift_status;     // Indicates whether the Shift key is pressedbool alt_status;       // Indicates whether the Alt key is pressedbool caps_lock_status; // Indicates whether Caps Lock is activebool ext_scancode;     // Indicates if the scancode starts with 0xe0
} key_state;

上面的结构体就是用来处理我们的键盘的状态用的。上面的注释上我说的很清楚了。

下面,让我们仔细瞧瞧看!我们改造后的键盘中断处理程序是如何的。

static void keyboard_intr_handler(void)
{// fetch the recordingsbool prev_shift_down = key_state.shift_status;bool prev_caps_lock = key_state.caps_lock_status;
​bool break_code;uint16_t scancode = inb(KEYBOARD_BUF_PORT);
​if (scancode == SCANCODE_EXT) {key_state.ext_scancode = true;return;}

我们首先读取8042上的缓存的键盘扫描码,我们第一件事,就是看看我们的这一次是不是e0,是e0说明我们的键盘产生了多个扫描码,马上结束中断,准备好薄记。退出中断处理子程序

    if(key_state.ext_scancode){scancode = ((0xe000) | scancode);key_state.ext_scancode = false;}

我们判断是不是发生过0xe0的收到,收到的话,我们就要合并上一次的0xe0:scancode = ((0xe000) | scancode);,然后关闭标记,方便我们接受下一次的0xe0.

我们下一步就是判断,这一次是断码还是通码。我们检查一下字节的最高位:

// get break code
break_code = ((scancode & 0x0080) != 0); 

如果,我们收到的是一个断码,就要准备处理这个字符了(用户结束了输入,我们准备好清理一部分状态)

    if (break_code) {// Extract the make code by masking the break code bituint16_t make_code = (scancode &= 0xff7f);
​// Update the status of control keys if they are releasedif (make_code == CTRL_L_MAKE || make_code == CTRL_R_MAKE) {key_state.ctrl_status = false;} else if (make_code == SHIFT_L_MAKE || make_code == SHIFT_R_MAKE) {key_state.shift_status = false;} else if (make_code == ALT_L_MAKE || make_code == ALT_R_MAKE) {key_state.alt_status = false;}
​// Caps Lock does not toggle on release, so no update is needed herereturn;}

我们把最高位的1一清走,就得到了通码,为什么看通码呢?上一节想一想你松开Ctrl + A的Ctrl的,显然你就要结束全选全文了。那么,我们自然需要薄记——用户松开了Ctrl,不需要全选了。我们就要清理我们用来薄记的key_state的结构体的状态

我们的键盘上支支持到0x3a上,为了防止越界(其实更好的办法是取出来这个结构体的数组的大小做限制,但是笔者这里偷懒了,你可以自行尝试封装一个更好的判断)

走到下面,我们先要判断这个字符是不是非法的——我们看看,首先,我们要求这个扫描码必须在外面的处理范围内(还可以包含右ALT和右Ctrl键)

    // Handle make codes for defined keysif ((scancode > 0x00 && scancode < 0x3b) || (scancode == ALT_R_MAKE) || (scancode == CTRL_R_MAKE) ){// handling inside}else{ccos_puts("unknown key\n"); // Handle undefined keys}

那些不是的,我们直接给一个提示就好了,你可以啥也不做,这取决于你。

下面准备处理了:主键盘区主要就是数字键和字母键,因此现在要考虑的是之前是否按下了<shift>键和<capslock>键,大伙儿知道,主键盘区中部分键有两个意义,当与 shift 配合使用时,表示键中上面的字符,为方便讨论,暂 称它们为双字符键。(因为不是shift的时候,表达的是其他的含义——举个例子,可能是1,2,3等等,这个你仔细看看你的键盘,凡是一个键盘上面挤着两个字符的都是双字字符)<shift>键和<capslock>键对双字符键和字母键的影响是不一样的.

当键入的是双字符键时

如果同时按下了<shift>键,则应该转换为数字键上面的那个符号,比如当前按下的是数字2,之前若按下 shift 未松手的话,现在应该将其转换为字符'@'。 <capslock>键是否开启,对双字符键无影响。

当键入的是字母键时

如果之前开启了<caps lock>键,则应该转换为大写字母。 如果之前同时按下了<shift>键不松手,但没有按下<capslock>键,则也应该转换为大写字母。 若之前同时按下了<capslock>键和<shift>键,<shift>键将<capslock>键的功能抵消,因此键入的是字母键应该转换为小写字母。

嗯,那这样说来,我们最终可以统一到shift键的处理上(别太麻烦,将大写锁定的作用也归结到shift变量上,这样事情简单一些)。所以请出一个变量叫shift。

  • 若 shift 为 false,则表示 shift 为 0,这表示一维数组中第 0 个元素,即 keymap[通码][0]

  • 若 shift 为 true,则表示 shift 为 1,这表示一维数组中第 1 个元素,即 keymap[通码][1]

        bool shift =false; // Used to index characters from keymap based on Shift status

之后,我们处理这些双字字符:

  • 数字 0~9、字符'-'、字符'='的通码是< 0x0e。

  • 字符'`'的通码是 0x29。

  • 字符'['的通码是 0x1a。

  • 字符']'的通码是 0x1b。

  • 字符'\'的通码是 0x2b。

  • 字符';'的通码是 0x27。

  • 字符'''的通码是 0x28。

  • 字符','的通码是 0x33。

  • 字符'.'的通码是 0x34。

  • 字符'/'的通码是 0x35

        // Special key ranges that require Shift to access additional charactersif ((scancode < 0x0e) || (scancode == 0x29) || (scancode == 0x1a) ||(scancode == 0x1b) || (scancode == 0x2b) || (scancode == 0x27) ||(scancode == 0x28) || (scancode == 0x33) || (scancode == 0x34) ||(scancode == 0x35)){if (prev_shift_down) {shift = true;}	}else{// handling special keys}

如果是这些双字符键,并且按下了<shift>,那说明就是准备触发了第二列的索引,我们后面直接keymap[index][shift]的时候shift就是1嘛!

让我们看看else下的handling special keys的部分,实际上就是这个代码

           // For alphabetic keys, Shift and Caps Lock together cancel each// otherif (prev_shift_down && prev_caps_lock) {shift = false;} else if (prev_shift_down || prev_caps_lock) {shift = true;} else {shift = false;}

我们如果if (shift_down_last && caps_lock_last),说明我们同时嗯下了shift和caps lock,那就抵消了。

如果其中任何一个被嗯下,那事情就很简单了: shift = true;,余下的情况就是啥也没嗯下,

准备爬表!我们获取表的offset,办法就是直接提取低8位。剩下的位都已经在上面的判断处理干净了

scancode &= 0x00ff;
char cur_char = keymap[scancode][shift]; // Retrieve the character from the keymap

如果我们是可见的字符(参考keyboard.ascii.h,笔者对一切不可见字符直接塞上了0,这个时候,如果我们键入了一个不可见的字符,cur_char索引的结果就是0)

        // Add the character to the buffer if it is not nullif (cur_char) {__ccos_putchar(cur_char);return;}

直接输出这个字符!

当然,如果是控制不可见字符,我们就是准备薄记状态了,请看:

        // Update the status of control keys for the next inputif (scancode == CTRL_L_MAKE || scancode == CTRL_R_MAKE) {key_state.ctrl_status = true;} else if (scancode == SHIFT_L_MAKE || scancode == SHIFT_R_MAKE) {key_state.shift_status = true;} else if (scancode == ALT_L_MAKE || scancode == ALT_R_MAKE) {key_state.alt_status = true;} else if (scancode == CAPS_LOCK_MAKE) {// Toggle Caps Lock status on each presskey_state.caps_lock_status = !key_state.caps_lock_status;}

完事!直接上电看看情况。

非常好!

代码

CCOperateSystem/Documentations/9_Boost_BasicInputSubsystem/9.2_finish_input_subsystem_1_code at main · Charliechen114514/CCOperateSystemhttps://github.com/Charliechen114514/CCOperateSystem/tree/main/Documentations/9_Boost_BasicInputSubsystem/9.2_finish_input_subsystem_1_code

下一篇

从0开始的操作系统手搓教程25:使用环状缓冲区来让我们的键盘驱动真正的有作用起来-CSDN博客然而,上面的驱动程序,我相信不少朋友发现毛病了——那就是他是处理完一个字符之后,就迅速的扔掉,没法找到了。换而言之,我们需要一个缓冲区,让我们可以记住用户之前输入了什么。这不,环状缓冲区就来了。缓冲区就是为了解决生产者和消费者问题而存在的,想象一下。 https://blog.csdn.net/charlie114514191/article/details/146105601

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.rhkb.cn/news/30141.html

如若内容造成侵权/违法违规/事实不符,请联系长河编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

根据输入汉字生成带拼音的米字格字帖

实现了下面功能&#xff1a; 1、根据输入汉字&#xff0c;自动调整米字格和四线格的行数&#xff1b; 2、给汉字自动加上拼音和声调&#xff08;暂时不考虑多音字&#xff09;&#xff1b; 3、汉字在米字格&#xff0c;拼音在四线格&#xff0c; 4、第一列用黑色&#xff0c;2-…

软件高级架构师 - 软件工程

补充中 测试 测试类型 静态测试 动态测试 测试阶段 单元测试中&#xff0c;包含性能测试&#xff0c;如下&#xff1a; 集成测试中&#xff0c;包含以下&#xff1a; 维护 遗留系统处置 高水平低价值&#xff1a;采取集成 对于这类系统&#xff0c;采取 集成 的方式&…

DeepSeek专题:DeepSeek-V2核心知识点速览

AIGCmagic社区知识星球是国内首个以AIGC全栈技术与商业变现为主线的学习交流平台&#xff0c;涉及AI绘画、AI视频、大模型、AI多模态、数字人以及全行业AIGC赋能等100应用方向。星球内部包含海量学习资源、专业问答、前沿资讯、内推招聘、AI课程、AIGC模型、AIGC数据集和源码等…

电脑如何拦截端口号,实现阻断访问?

如果你弟弟喜欢玩游戏&#xff0c;你可以查询该应用占用的端口&#xff0c;结合以下方法即可阻断端口号&#xff0c;让弟弟好好学习&#xff0c;天天向上&#xff01; 拦截端口可以通过防火墙和路由器进行拦截 &#xff0c;以下是常用方法&#xff1a; 方法 1&#xff1a;使用…

【NLP 32、文本匹配任务 —— 深度学习】

大劫大难以后&#xff0c;人不该失去锐气&#xff0c;不该失去热度&#xff0c;你镇定了却依旧燃烧&#xff0c;你平静了却依旧浩荡&#xff0c;致那个从绝望中走出来的自己&#xff0c;共勉 —— 25.1.31 使用深度学习在文本匹配任务上主要有两种方式&#xff1a;① 表示型 ②…

【AD】5-15 Active Route的自动布线辅助

1.如图所示点击Panels&#xff0c;打开Active Route 2.按如图设置后&#xff0c;点击向导走线确定好走向后&#xff0c;在点击自动辅助布线&#xff0c;布好后可自行微调

使用 Arduino 和 ThingSpeak 通过 Internet 进行心跳监测

使用 Arduino 和 ThingSpeak 通过 Internet 进行心跳监测 在这个项目中,我们将使用 Arduino 制作一个心跳检测和监测系统,该系统将使用脉搏传感器检测心跳,并在与其连接的 LCD 上显示 BPM(每分钟心跳次数)读数。它还将使用 Wi-Fi 模块ESP8266将读数发送到 ThingSpeak 服务…

正则表达式,idea,插件anyrule

​​​​package lx;import java.util.regex.Pattern;public class lxx {public static void main(String[] args) {//正则表达式//写一个电话号码的正则表达式String regex "1[3-9]\\d{9}";//第一个数字是1&#xff0c;第二个数字是3-9&#xff0c;后面跟着9个数字…

分析不同高度障碍物检测情况

import re import matplotlib.pyplot as plt from datetime import datetime import matplotlib.dates as mdates from matplotlib.font_manager import FontProperties# 设置中文字体 font_path /usr/local/sunlogin/res/font/wqy-zenhei.ttc # 替换为你的实际字体路径 my_f…

Qt添加MySql数据库驱动

文章目录 一. 安装MySql二.编译mysql动态链接库 Qt版本&#xff1a;5.14.2 MySql版本&#xff1a;8.0.41 一. 安装MySql 参考这里进行安装&#xff1a;https://blog.csdn.net/qq_30150579/article/details/146042922 将mysql安装目录里的bin&#xff0c;include和lib拷贝出来…

算法005——有效三角形个数

力扣——有效三角形个数点击链接跳转 判断三条边是否能组成三角形&#xff0c;大家第一时间想到的就是两边之和大于第三边 但是运用这个方法&#xff0c;我们需要判断三次&#xff0c;有一个更简单的方法&#xff0c;只需要判断一次 因为 C 已经是三边之中最大的了&#xff…

【大学生体质】智能 AI 旅游推荐平台(Vue+SpringBoot3)-完整部署教程

智能 AI 旅游推荐平台开源文档 项目前端地址 ☀️项目介绍 智能 AI 旅游推荐平台&#xff08;Intelligent AI Travel Recommendation Platform&#xff09;是一个利用 AI 模型和数据分析为用户提供个性化旅游路线推荐、景点评分、旅游攻略分享等功能的综合性系统。该系统融合…

Hive八股

Hive八股 一级目录二级目录三级目录 Hive1Hive1hive简介2hive架构3hive与Hadoop的关系4hive与传统数据库对比5hive的数据存储 2Hive表类型1 Hive 数据类型2 Hive 内部表&#xff01;&#xff01;&#xff01;3 Hive 外部表 external table&#xff01;&#xff01;&#xff01;…

Aruco 库详解:计算机视觉中的高效标记检测工具

1. 引言&#xff1a;Aruco 在计算机视觉中的重要性 在计算机视觉领域&#xff0c;标记&#xff08;Marker&#xff09;检测和识别是许多应用的基础&#xff0c;包括 机器人导航、增强现实&#xff08;AR&#xff09;、相机标定&#xff08;Calibration&#xff09;以及物体跟踪…

智慧消防新篇章:4G液位/压力传感器,筑牢安全防线!

火灾无情&#xff0c;防患未“燃”&#xff01;在智慧消防时代&#xff0c;如何实现消防水系统的实时监测、预警&#xff0c;保障人民生命财产安全&#xff1f;山东一二三物联网深耕物联网领域&#xff0c;自主研发4G液位、4G压力智能传感器&#xff0c;为智慧消防水位、水压无…

STM32 子设备通过CAN发送数据到主设备

采集ADC、GPS经纬坐标、温湿度数据、大气压数据通过CAN方式发送给主设备端&#xff0c;帧ID按照如下定义&#xff1a; 我尼玛一个标准帧ID位数据是11位&#xff0c;扩展帧才是111829位&#xff0c;它说最开头的是四位是真类型&#xff0c;并给我如下解释&#xff1a; 它把帧的定…

通过Golang的container/list实现LRU缓存算法

文章目录 力扣&#xff1a;146. LRU 缓存主要结构 List 和 Element常用方法1. 初始化链表2. 插入元素3. 删除元素4. 遍历链表5. 获取链表长度使用场景注意事项 源代码阅读 在 Go 语言中&#xff0c;container/list 包提供了一个双向链表的实现。链表是一种常见的数据结构&#…

模型微调-基于LLaMA-Factory进行微调的一个简单案例

模型微调-基于LLaMA-Factory进行微调的一个简单案例 1. 租用云计算资源2. 拉取 LLaMa-Factory3. 安装依赖环境4. 启动 LLaMa-Factory 界面5. 从 Huggingface 下载模型6. 模型验证7. 模型微调 1. 租用云计算资源 以下示例基于 AutoDL 云计算资源。 在云计算平台选择可用的云计…

ArcGIS操作:13 生成最小外接矩阵

应用情景&#xff1a;筛选出屋面是否能放下12*60m的长方形&#xff0c;作为起降场候选点&#xff08;一个不规则的形状内&#xff0c;判断是否能放下指定长宽的长方形&#xff09; 1、面积初步筛选 Area ≥ 720 ㎡ 面积计算见 2、打开 ArcToolbox → Data Management Tools …

Vue 系列之:插槽

前言 插槽是定义在子组件中的&#xff0c;相当于一个占位符&#xff0c;父组件可以在这个占位符中填充HTML代码、组件等内容。 插槽显不显示、怎样显示是由父组件来控制的&#xff0c;而插槽在哪里显示就由子组件来进行控制。 基本使用 子组件&#xff1a; <template&g…