You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
work/blog/_localhost-2024_12_30_10_31...

304 lines
90 KiB

This file contains ambiguous Unicode characters!

This file contains ambiguous Unicode characters that may be confused with others in your current locale. If your use case is intentional and legitimate, you can safely ignore this warning. Use the Escape button to highlight these characters.

-- MySQL dump 10.13 Distrib 8.0.40, for Win64 (x86_64)
--
-- Host: 127.0.0.1 Database: blog
-- ------------------------------------------------------
-- Server version 8.0.40
/*!40101 SET @OLD_CHARACTER_SET_CLIENT=@@CHARACTER_SET_CLIENT */;
/*!40101 SET @OLD_CHARACTER_SET_RESULTS=@@CHARACTER_SET_RESULTS */;
/*!40101 SET @OLD_COLLATION_CONNECTION=@@COLLATION_CONNECTION */;
/*!50503 SET NAMES utf8mb4 */;
/*!40103 SET @OLD_TIME_ZONE=@@TIME_ZONE */;
/*!40103 SET TIME_ZONE='+00:00' */;
/*!40014 SET @OLD_UNIQUE_CHECKS=@@UNIQUE_CHECKS, UNIQUE_CHECKS=0 */;
/*!40014 SET @OLD_FOREIGN_KEY_CHECKS=@@FOREIGN_KEY_CHECKS, FOREIGN_KEY_CHECKS=0 */;
/*!40101 SET @OLD_SQL_MODE=@@SQL_MODE, SQL_MODE='NO_AUTO_VALUE_ON_ZERO' */;
/*!40111 SET @OLD_SQL_NOTES=@@SQL_NOTES, SQL_NOTES=0 */;
--
-- Table structure for table `administrators`
--
DROP TABLE IF EXISTS `administrators`;
/*!40101 SET @saved_cs_client = @@character_set_client */;
/*!50503 SET character_set_client = utf8mb4 */;
CREATE TABLE `administrators` (
`admin_id` int NOT NULL AUTO_INCREMENT,
`user_id` int NOT NULL,
PRIMARY KEY (`admin_id`),
UNIQUE KEY `user_id` (`user_id`),
CONSTRAINT `administrators_ibfk_1` FOREIGN KEY (`user_id`) REFERENCES `users` (`user_id`) ON DELETE CASCADE
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_0900_ai_ci;
/*!40101 SET character_set_client = @saved_cs_client */;
--
-- Dumping data for table `administrators`
--
LOCK TABLES `administrators` WRITE;
/*!40000 ALTER TABLE `administrators` DISABLE KEYS */;
/*!40000 ALTER TABLE `administrators` ENABLE KEYS */;
UNLOCK TABLES;
--
-- Table structure for table `announcements`
--
DROP TABLE IF EXISTS `announcements`;
/*!40101 SET @saved_cs_client = @@character_set_client */;
/*!50503 SET character_set_client = utf8mb4 */;
CREATE TABLE `announcements` (
`announcement_id` int NOT NULL AUTO_INCREMENT,
`admin_id` int NOT NULL,
`title` varchar(255) NOT NULL,
`content` text NOT NULL,
`created_at` timestamp NULL DEFAULT CURRENT_TIMESTAMP,
`updated_at` timestamp NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,
`is_published` tinyint(1) DEFAULT '0',
PRIMARY KEY (`announcement_id`),
KEY `admin_id` (`admin_id`),
CONSTRAINT `announcements_ibfk_1` FOREIGN KEY (`admin_id`) REFERENCES `users` (`user_id`) ON DELETE CASCADE
) ENGINE=InnoDB AUTO_INCREMENT=4 DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_0900_ai_ci;
/*!40101 SET character_set_client = @saved_cs_client */;
--
-- Dumping data for table `announcements`
--
LOCK TABLES `announcements` WRITE;
/*!40000 ALTER TABLE `announcements` DISABLE KEYS */;
INSERT INTO `announcements` VALUES (1,1,'公告1','Content of the first announcement.','2024-12-12 11:38:49','2024-12-28 00:09:37',1),(2,1,'公告2','Content of the second announcement.','2024-12-12 11:38:49','2024-12-28 00:09:37',1),(3,1,'公告3','Content of the twentieth announcement.','2024-12-12 11:38:49','2024-12-28 00:09:37',1);
/*!40000 ALTER TABLE `announcements` ENABLE KEYS */;
UNLOCK TABLES;
--
-- Table structure for table `comments`
--
DROP TABLE IF EXISTS `comments`;
/*!40101 SET @saved_cs_client = @@character_set_client */;
/*!50503 SET character_set_client = utf8mb4 */;
CREATE TABLE `comments` (
`comment_id` int NOT NULL AUTO_INCREMENT,
`post_id` int NOT NULL,
`user_id` int NOT NULL,
`content` varchar(1024) NOT NULL,
`created_at` timestamp NULL DEFAULT CURRENT_TIMESTAMP,
`updated_at` timestamp NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,
`is_deleted` tinyint(1) DEFAULT '0',
PRIMARY KEY (`comment_id`),
KEY `post_id` (`post_id`),
KEY `user_id` (`user_id`),
CONSTRAINT `comments_ibfk_1` FOREIGN KEY (`post_id`) REFERENCES `posts` (`post_id`) ON DELETE CASCADE,
CONSTRAINT `comments_ibfk_2` FOREIGN KEY (`user_id`) REFERENCES `users` (`user_id`) ON DELETE CASCADE
) ENGINE=InnoDB AUTO_INCREMENT=12 DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_0900_ai_ci;
/*!40101 SET character_set_client = @saved_cs_client */;
--
-- Dumping data for table `comments`
--
LOCK TABLES `comments` WRITE;
/*!40000 ALTER TABLE `comments` DISABLE KEYS */;
INSERT INTO `comments` VALUES (1,9,1,'001','2024-12-27 13:35:21','2024-12-27 13:35:21',0),(2,9,1,'002','2024-12-27 13:35:21','2024-12-27 13:35:21',0),(3,37,4,'vue评论示例001','2024-12-27 14:57:50','2024-12-27 14:57:50',0),(11,52,4,'测试时间为2024年12月29日21:46:29','2024-12-29 13:46:31','2024-12-29 13:46:31',0);
/*!40000 ALTER TABLE `comments` ENABLE KEYS */;
UNLOCK TABLES;
--
-- Table structure for table `posts`
--
DROP TABLE IF EXISTS `posts`;
/*!40101 SET @saved_cs_client = @@character_set_client */;
/*!50503 SET character_set_client = utf8mb4 */;
CREATE TABLE `posts` (
`post_id` int NOT NULL AUTO_INCREMENT,
`user_id` int NOT NULL,
`title` varchar(255) NOT NULL,
`content` longtext,
`created_at` timestamp NULL DEFAULT CURRENT_TIMESTAMP,
`updated_at` timestamp NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,
`priority` int DEFAULT '0',
PRIMARY KEY (`post_id`),
KEY `user_id` (`user_id`),
CONSTRAINT `posts_ibfk_1` FOREIGN KEY (`user_id`) REFERENCES `users` (`user_id`) ON DELETE CASCADE
) ENGINE=InnoDB AUTO_INCREMENT=53 DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_0900_ai_ci;
/*!40101 SET character_set_client = @saved_cs_client */;
--
-- Dumping data for table `posts`
--
LOCK TABLES `posts` WRITE;
/*!40000 ALTER TABLE `posts` DISABLE KEYS */;
INSERT INTO `posts` VALUES (9,2,'Third Post by User2','This is the content of the third post by user2. It is a detailed explanation of the topic.','2024-12-12 11:47:15','2024-12-12 11:47:15',0),(11,3,'Fourth Post by User2','This is the content of the fourth post by user2. It is a detailed explanation of the topic.','2024-12-12 11:47:15','2024-12-16 11:23:21',0),(12,1,'Fifth Post by User1','This is the content of the fifth post by user1. It is a detailed explanation of the topic.','2024-12-12 11:47:15','2024-12-12 11:47:15',0),(13,3,'Fifth Post by User2','This is the content of the fifth post by user2. It is a detailed explanation of the topic.','2024-12-12 11:47:15','2024-12-16 11:23:21',0),(14,3,'Sixth Post by User1','This is the content of the sixth post by user1. It is a detailed explanation of the topic.','2024-12-12 11:47:15','2024-12-16 11:23:21',0),(15,2,'Sixth Post by User2','This is the content of the sixth post by user2. It is a detailed explanation of the topic.','2024-12-12 11:47:15','2024-12-12 11:47:15',0),(16,1,'Seventh Post by User1','This is the content of the seventh post by user1. It is a detailed explanation of the topic.','2024-12-12 11:47:15','2024-12-12 11:47:15',0),(17,2,'Seventh Post by User2','This is the content of the seventh post by user2. It is a detailed explanation of the topic.','2024-12-12 11:47:15','2024-12-12 11:47:15',0),(18,1,'Eighth Post by User1','This is the content of the eighth post by user1. It is a detailed explanation of the topic.','2024-12-12 11:47:15','2024-12-12 11:47:15',0),(19,2,'Eighth Post by User2','This is the content of the eighth post by user2. It is a detailed explanation of the topic.','2024-12-12 11:47:15','2024-12-12 11:47:15',0),(20,1,'小20改','2024年12月17日12:56:12','2024-12-12 11:47:15','2024-12-17 04:57:08',0),(21,2,'Ninth Post by User2','This is the content of the ninth post by user2. It is a detailed explanation of the topic.','2024-12-12 11:47:15','2024-12-12 11:47:15',0),(22,1,'Tenth Post by User1','This is the content of the tenth post by user1. It is a detailed explanation of the topic.','2024-12-12 11:47:15','2024-12-12 11:47:15',0),(23,2,'Tenth Post by User2','This is the content of the tenth post by user2. It is a detailed explanation of the topic.','2024-12-12 11:47:15','2024-12-12 11:47:15',0),(25,1,'文章标题示例1','文章内容示例1','2024-12-13 16:36:32','2024-12-13 16:36:32',0),(26,4,'# 我的宠物','我有一只非常可爱的小狗,它的名字叫球球。球球全身毛茸茸的,颜色像雪一样白。它的眼睛又大又圆,每次看到我都会摇着尾巴欢迎我回家。\n\n球球最喜欢玩的游戏是“接飞盘”。每天放学后我都会带它到公园去玩这个游戏。球球跑得非常快总能准确地接住我扔出去的飞盘。我们玩得很开心常常引来其他小朋友羡慕的目光。\n\n除了游戏球球还是我的好伙伴。当我遇到困难或者心情不好时它总是静静地坐在我身边仿佛在安慰我。球球不仅是我最好的朋友也是我家的一员。\n','2024-12-14 16:31:47','2024-12-14 16:31:47',0),(27,4,'我的理想','# 我的理想\n\n每个人都有自己的理想而我的理想是成为一名医生。从小我就对医学充满了好奇经常看一些关于人体构造的书籍梦想着有一天能够帮助病人恢复健康。\n\n我知道要实现这个理想并不容易需要学习很多知识并且还要经过长时间的训练。但是我不怕困难因为这是我真心想要做的事情。我会努力学习争取考上一所好的医学院校将来成为一名优秀的医生。\n\n我相信只要坚持不懈地努力就一定可以实现自己的理想。我要用自己的双手去治愈更多的人让他们重新获得健康的笑容。','2024-12-14 16:34:12','2024-12-14 16:34:12',0),(28,4,'美丽的校园','# 美丽的校园\n\n我们的学校坐落在一条宁静的小河边周围环绕着青山绿水环境十分优美。走进校园首先映入眼帘的是宽阔的操场那里是我们课间活动的好地方。操场上铺满了绿色的草坪旁边还有几棵高大的松树夏天的时候可以为我们遮挡阳光。\n\n沿着操场往前走就是教学楼了。教学楼里有明亮的教室、整洁的走廊以及现代化的教学设备。在这里我们不仅可以学到丰富的知识还能参加各种有趣的课外活动。老师们也非常关心我们总是耐心地解答我们的问题。\n\n每当清晨来临整个校园都充满了生机与活力。同学们背着书包走进校园脸上洋溢着灿烂的笑容。这就是我美丽的校园一个充满希望与梦想的地方。','2024-12-14 16:34:12','2024-12-14 16:34:12',0),(29,4,'快乐的周末','# 快乐的周末\n\n每个周末都是我最期待的日子之一。周六早上我会早早起床穿上运动服跟着爸爸妈妈一起去爬山。站在山顶上望着远处连绵起伏的山脉呼吸着新鲜空气感觉整个人都变得神清气爽。\n\n下午回到家后我们会一起做一顿丰盛的晚餐。爸爸负责炒菜妈妈帮忙打下手而我则在一旁观看并学习烹饪技巧。一家人围坐在一起享用美食其乐融融。\n\n晚上则是属于我的自由时间。有时候我会看书沉浸在精彩的故事中有时候我会画画用画笔记录下美好的瞬间。这样的周末既充实又快乐让我感到无比幸福。','2024-12-14 16:34:12','2024-12-14 16:34:12',0),(35,4,'zxc','朝霞不出门\n','2024-12-17 04:26:59','2024-12-22 14:14:37',0),(36,4,'36','3621321','2024-12-17 04:57:55','2024-12-22 13:45:39',2),(37,4,'vue3','# 1. Vue3简介\n- 2020年9月18日`Vue.js`发布版`3.0`版本,代号:`One Piece`n\n- 经历了:[4800+次提交](https://github.com/vuejs/core/commits/main)、[40+个RFC](https://github.com/vuejs/rfcs/tree/master/active-rfcs)、[600+次PR](https://github.com/vuejs/vue-next/pulls?q=is%3Apr+is%3Amerged+-author%3Aapp%2Fdependabot-preview+)、[300+贡献者](https://github.com/vuejs/core/graphs/contributors)\n- 官方发版地址:[Release v3.0.0 One Piece · vuejs/core](https://github.com/vuejs/core/releases/tag/v3.0.0)\n- 截止2023年10月最新的公开版本为`3.3.4`\n\n <img src=\"images/1695089947298-161c1b47-eb86-42fb-b1f8-d6a4fcab8ee2.png\" alt=\"image.png\" style=\"zoom:30%;\" /> \n\n## 1.1. 【性能的提升】\n\n- 打包大小减少`41%`。\n\n- 初次渲染快`55%`, 更新渲染快`133%`。\n\n- 内存减少`54%`。\n\n \n## 1.2.【 源码的升级】\n\n- 使用`Proxy`代替`defineProperty`实现响应式。\n\n- 重写虚拟`DOM`的实现和`Tree-Shaking`。\n\n \n## 1.3. 【拥抱TypeScript】\n\n- `Vue3`可以更好的支持`TypeScript`。\n\n \n## 1.4. 【新的特性】\n\n1. `Composition API`(组合`API`\n - `setup`\n - `ref`与`reactive`\n - `computed`与`watch`\n \n ......\n \n2. 新的内置组件:\n - `Fragment`\n - `Teleport`\n - `Suspense`\n\n ......\n\n3. 其他改变:\n - 新的生命周期钩子\n - `data` 选项应始终被声明为一个函数\n - 移除`keyCode`支持作为` v-on` 的修饰符\n\n ......\n\n\n\n# 2. 创建Vue3工程\n\n## 2.1. 【基于 vue-cli 创建】\n点击查看[官方文档](https://cli.vuejs.org/zh/guide/creating-a-project.html#vue-create)\n\n> 备注:目前`vue-cli`已处于维护模式,官方推荐基于 `Vite` 创建项目。\n\n```powershell\n## 查看@vue/cli版本确保@vue/cli版本在4.5.0以上\nvue --version\n\n## 安装或者升级你的@vue/cli \nnpm install -g @vue/cli\n\n## 执行创建命令\nvue create vue_test\n\n## 随后选择3.x\n## Choose a version of Vue.js that you want to start the project with (Use arrow keys)\n## > 3.x\n## 2.x\n\n## 启动\ncd vue_test\nnpm run serve\n```\n\n---\n\n## 2.2. 【基于 vite 创建】(推荐)\n`vite` 是新一代前端构建工具,官网地址:[https://vitejs.cn](https://vitejs.cn/)`vite`的优势如下:\n\n- 轻量快速的热重载(`HMR`),能实现极速的服务启动。\n- 对 `TypeScript`、`JSX`、`CSS` 等支持开箱即用。\n- 真正的按需编译,不再等待整个应用编译完成。\n- `webpack`构建 与 `vite`构建对比图如下:\n<img src=\"images/1683167182037-71c78210-8217-4e7d-9a83-e463035efbbe.png\" alt=\"webpack构建\" title=\"webpack构建\" style=\"zoom:20%;box-shadow:0 0 10px black\" /> <img src=\"images/1683167204081-582dc237-72bc-499e-9589-2cdfd452e62f.png\" alt=\"vite构建\" title=\"vite构建\" style=\"zoom: 20%;box-shadow:0 0 10px black\" />\n* 具体操作如下(点击查看[官方文档](https://cn.vuejs.org/guide/quick-start.html#creating-a-vue-application)\n\n```powershell\n## 1.创建命令\nnpm create vue@latest\n\n## 2.具体配置\n## 配置项目名称\n√ Project name: vue3_test\n## 是否添加TypeScript支持\n√ Add TypeScript? Yes\n## 是否添加JSX支持\n√ Add JSX Support? No\n## 是否添加路由环境\n√ Add Vue Router for Single Page Application development? No\n## 是否添加pinia环境\n√ Add Pinia for state management? No\n## 是否添加单元测试\n√ Add Vitest for Unit Testing? No\n## 是否添加端到端测试方案\n√ Add an End-to-End Testing Solution? » No\n## 是否添加ESLint语法检查\n√ Add ESLint for code quality? Yes\n## 是否添加Prettiert代码格式化\n√ Add Prettier for code formatting? No\n```\n自己动手编写一个App组件\n\n```vue\n<template>\n <div class=\"app\">\n <h1>你好啊!</h1>\n </div>\n</template>\n\n<script lang=\"ts\">\n export default {\n name:\'App\' //组件名\n }\n</script>\n\n<style>\n .app {\n background-color: #ddd;\n box-shadow: 0 0 10px;\n border-radius: 10px;\n padding: 20px;\n }\n</style>\n```\n\n安装官方推荐的`vscode`插件:\n\n<img src=\"images/volar.png\" alt=\"Snipaste_2023-10-08_20-46-34\" style=\"zoom:50%;\" /> \n\n<img src=\"images/image-20231218085906380.png\" alt=\"image-20231218085906380\" style=\"zoom:42%;\" /> \n\n总结\n\n- `Vite` 项目中,`index.html` 是项目的入口文件,在项目最外层。\n- 加载`index.html`后,`Vite` 解析 `<script type=\"module\" src=\"xxx\">` 指向的`JavaScript`。\n- `Vue3`**中是通过 **`createApp` 函数创建一个应用实例。\n## 2.3. 【一个简单的效果】\n\n`Vue3`向下兼容`Vue2`语法,且`Vue3`中的模板中可以没有根标签\n\n```vue\n<template>\n <div class=\"person\">\n <h2>姓名:{{name}}</h2>\n <h2>年龄:{{age}}</h2>\n <button @click=\"changeName\">修改名字</button>\n <button @click=\"changeAge\">年龄+1</button>\n <button @click=\"showTel\">点我查看联系方式</button>\n </div>\n</template>\n\n<script lang=\"ts\">\n export default {\n name:\'App\',\n data() {\n return {\n name:\'\',\n age:18,\n tel:\'13888888888\'\n }\n },\n methods:{\n changeName(){\n this.name = \'zhang-san\'\n },\n changeAge(){\n this.age += 1\n },\n showTel(){\n alert(this.tel)\n }\n },\n }\n</script>\n```\n\n\n# 3. Vue3核心语法\n## 3.1. 【OptionsAPI 与 CompositionAPI】\n\n- `Vue2`的`API`设计是`Options`(配置)风格的。\n- `Vue3`的`API`设计是`Composition`(组合)风格的。\n### Options API 的弊端\n\n`Options`类型的 `API`,数据、方法、计算属性等,是分散在:`data`、`methods`、`computed`中的,若想新增或者修改一个需求,就需要分别修改:`data`、`methods`、`computed`,不便于维护和复用。\n\n<img src=\"images/1696662197101-55d2b251-f6e5-47f4-b3f1-d8531bbf9279.gif\" alt=\"1.gif\" style=\"zoom:70%;border-radius:20px\" /><img src=\"images/1696662200734-1bad8249-d7a2-423e-a3c3-ab4c110628be.gif\" alt=\"2.gif\" style=\"zoom:70%;border-radius:20px\" />\n\n### Composition API 的优势\n\n可以用函数的方式更加优雅的组织代码让相关功能的代码更加有序的组织在一起。\n\n<img src=\"images/1696662249851-db6403a1-acb5-481a-88e0-e1e34d2ef53a.gif\" alt=\"3.gif\" style=\"height:300px;border-radius:10px\" /><img src=\"images/1696662256560-7239b9f9-a770-43c1-9386-6cc12ef1e9c0.gif\" alt=\"4.gif\" style=\"height:300px;border-radius:10px\" />\n\n> 说明:以上四张动图原创作者:大帅老猿\n\n## 3.2. 【拉开序幕的 setup】\n### setup 概述\n`setup`是`Vue3`中一个新的配置项,值是一个函数,它是 `Composition API` **“表演的舞台**_**”**_组件中所用到的数据、方法、计算属性、监视......等等,均配置在`setup`中。\n\n特点如下\n\n- `setup`函数返回的对象中的内容,可直接在模板中使用。\n- `setup`中访问`this`是`undefined`。\n- `setup`函数会在`beforeCreate`之前调用,它是“领先”所有钩子执行的。\n```vue\n<template>\n <div class=\"person\">\n <h2>姓名:{{name}}</h2>\n <h2>年龄:{{age}}</h2>\n <button @click=\"changeName\">修改名字</button>\n <button @click=\"changeAge\">年龄+1</button>\n <button @click=\"showTel\">点我查看联系方式</button>\n </div>\n</template>\n\n<script lang=\"ts\">\n export default {\n name:\'Person\',\n setup(){\n // 数据原来写在data中注意此时的name、age、tel数据都不是响应式数据\n let name = \'\'\n let age = 18\n let tel = \'13888888888\'\n\n // 方法原来写在methods中\n function changeName(){\n name = \'zhang-san\' //注意此时这么修改name页面是不变化的\n console.log(name)\n }\n function changeAge(){\n age += 1 //注意此时这么修改age页面是不变化的\n console.log(age)\n }\n function showTel(){\n alert(tel)\n }\n\n // 返回一个对象,对象中的内容,模板中可以直接使用\n return {name,age,tel,changeName,changeAge,showTel}\n }\n }\n</script>\n```\n### setup 的返回值\n\n- 若返回一个**对象**:则对象中的:属性、方法等,在模板中均可以直接使用**(重点关注)。**\n- 若返回一个**函数**:则可以自定义渲染内容,代码如下:\n```jsx\nsetup(){\n return ()=> \'\'\n}\n```\n### setup 与 Options API 的关系\n\n- `Vue2` 的配置(`data`、`methos`......)中**可以访问到** `setup`中的属性、方法。\n- 但在`setup`中**不能访问到**`Vue2`的配置(`data`、`methos`......)。\n- 如果与`Vue2`冲突,则`setup`优先。\n### setup 语法糖\n`setup`函数有一个语法糖,这个语法糖,可以让我们把`setup`独立出去,代码如下:\n\n```vue\n<template>\n <div class=\"person\">\n <h2>姓名:{{name}}</h2>\n <h2>年龄:{{age}}</h2>\n <button @click=\"changName\">修改名字</button>\n <button @click=\"changAge\">年龄+1</button>\n <button @click=\"showTel\">点我查看联系方式</button>\n </div>\n</template>\n\n<script lang=\"ts\">\n export default {\n name:\'Person\',\n }\n</script>\n\n<!-- 下面的写法是setup语法糖 -->\n<script setup lang=\"ts\">\n console.log(this) //undefined\n \n // 数据注意此时的name、age、tel都不是响应式数据\n let name = \'\'\n let age = 18\n let tel = \'13888888888\'\n\n // 方法\n function changName(){\n name = \'\'//注意此时这么修改name页面是不变化的\n }\n function changAge(){\n console.log(age)\n age += 1 //注意此时这么修改age页面是不变化的\n }\n function showTel(){\n alert(tel)\n }\n</script>\n```\n扩展上述代码还需要编写一个不写`setup`的`script`标签,去指定组件名字,比较麻烦,我们可以借助`vite`中的插件简化\n\n1. 第一步:`npm i vite-plugin-vue-setup-extend -D`\n2. 第二步:`vite.config.ts`\n```jsx\nimport { defineConfig } from \'vite\'\nimport VueSetupExtend from \'vite-plugin-vue-setup-extend\'\n\nexport default defineConfig({\n plugins: [ VueSetupExtend() ]\n})\n```\n\n3. 第三步:`<script setup lang=\"ts\" name=\"Person\">`\n## 3.3. 【ref 创建:基本类型的响应式数据】\n\n- **作用:**定义响应式变量。\n- **语法:**`let xxx = ref(初始值)`。\n- **返回值:**一个`RefImpl`的实例对象,简称`ref对象`或`ref``ref`对象的`value`**属性是响应式的**。\n- **注意点:**\n - `JS`中操作数据需要:`xxx.value`,但模板中不需要`.value`,直接使用即可。\n - 对于`let name = ref(\'\')`来说,`name`不是响应式的,`name.value`是响应式的。\n```vue\n<template>\n <div class=\"person\">\n <h2>姓名:{{name}}</h2>\n <h2>年龄:{{age}}</h2>\n <button @click=\"changeName\">修改名字</button>\n <button @click=\"changeAge\">年龄+1</button>\n <button @click=\"showTel\">点我查看联系方式</button>\n </div>\n</template>\n\n<script setup lang=\"ts\" name=\"Person\">\n import {ref} from \'vue\'\n // name和age是一个RefImpl的实例对象简称ref对象它们的value属性是响应式的。\n let name = ref(\'\')\n let age = ref(18)\n // tel就是一个普通的字符串不是响应式的\n let tel = \'13888888888\'\n\n function changeName(){\n // JS中操作ref对象时候需要.value\n name.value = \'\'\n console.log(name.value)\n\n // 注意name不是响应式的name.value是响应式的所以如下代码并不会引起页面的更新。\n // name = ref(\'zhang-san\')\n }\n function changeAge(){\n // JS中操作ref对象时候需要.value\n age.value += 1 \n console.log(age.value)\n }\n function showTel(){\n alert(tel)\n }\n</script>\n```\n## 3.4. 【reactive 创建:对象类型的响应式数据】\n\n- **作用:**定义一个**响应式对象**(基本类型不要用它,要用`ref`,否则报错)\n- **语法:**`let 响应式对象= reactive(源对象)`。\n- **返回值:**一个`Proxy`的实例对象,简称:响应式对象。\n- **注意点:**`reactive`定义的响应式数据是“深层次”的。\n```vue\n<template>\n <div class=\"person\">\n <h2>汽车信息:一台{{ car.brand }}汽车,价值{{ car.price }}万</h2>\n <h2>游戏列表:</h2>\n <ul>\n <li v-for=\"g in games\" :key=\"g.id\">{{ g.name }}</li>\n </ul>\n <h2>测试:{{obj.a.b.c.d}}</h2>\n <button @click=\"changeCarPrice\">修改汽车价格</button>\n <button @click=\"changeFirstGame\">修改第一游戏</button>\n <button @click=\"test\">测试</button>\n </div>\n</template>\n\n<script lang=\"ts\" setup name=\"Person\">\nimport { reactive } from \'vue\'\n\n// 数据\nlet car = reactive({ brand: \'\', price: 100 })\nlet games = reactive([\n { id: \'ahsgdyfa01\', name: \'\' },\n { id: \'ahsgdyfa02\', name: \'耀\' },\n { id: \'ahsgdyfa03\', name: \'\' }\n])\nlet obj = reactive({\n a:{\n b:{\n c:{\n d:666\n }\n }\n }\n})\n\nfunction changeCarPrice() {\n car.price += 10\n}\nfunction changeFirstGame() {\n games[0].name = \'\'\n}\nfunction test(){\n obj.a.b.c.d = 999\n}\n</script>\n```\n## 3.5. 【ref 创建:对象类型的响应式数据】\n\n- 其实`ref`接收的数据可以是:**基本类型**、**对象类型**。\n- 若`ref`接收的是对象类型,内部其实也是调用了`reactive`函数。\n```vue\n<template>\n <div class=\"person\">\n <h2>汽车信息:一台{{ car.brand }}汽车,价值{{ car.price }}万</h2>\n <h2>游戏列表:</h2>\n <ul>\n <li v-for=\"g in games\" :key=\"g.id\">{{ g.name }}</li>\n </ul>\n <h2>测试:{{obj.a.b.c.d}}</h2>\n <button @click=\"changeCarPrice\">修改汽车价格</button>\n <button @click=\"changeFirstGame\">修改第一游戏</button>\n <button @click=\"test\">测试</button>\n </div>\n</template>\n\n<script lang=\"ts\" setup name=\"Person\">\nimport { ref } from \'vue\'\n\n// 数据\nlet car = ref({ brand: \'\', price: 100 })\nlet games = ref([\n { id: \'ahsgdyfa01\', name: \'\' },\n { id: \'ahsgdyfa02\', name: \'耀\' },\n { id: \'ahsgdyfa03\', name: \'\' }\n])\nlet obj = ref({\n a:{\n b:{\n c:{\n d:666\n }\n }\n }\n})\n\nconsole.log(car)\n\nfunction changeCarPrice() {\n car.value.price += 10\n}\nfunction changeFirstGame() {\n games.value[0].name = \'\'\n}\nfunction test(){\n obj.value.a.b.c.d = 999\n}\n</script>\n```\n## 3.6. 【ref 对比 reactive】\n宏观角度看\n\n> 1. `ref`用来定义:**基本类型数据**、**对象类型数据**\n>\n> 2. `reactive`用来定义:**对象类型数据**。\n\n- 区别:\n\n> 1. `ref`创建的变量必须使用`.value`(可以使用`volar`插件自动添加`.value`)。\n>\n> <img src=\"images/自动补充value.png\" alt=\"自动补充value\" style=\"zoom:50%;border-radius:20px\" /> \n>\n> 2. `reactive`重新分配一个新对象,会**失去**响应式(可以使用`Object.assign`去整体替换)。\n\n- 使用原则:\n> 1. 若需要一个基本类型的响应式数据,必须使用`ref`。\n> 2. 若需要一个响应式对象,层级不深,`ref`、`reactive`都可以。\n> 3. 若需要一个响应式对象,且层级较深,推荐使用`reactive`。\n\n## 3.7. 【toRefs 与 toRef】\n\n- 作用:将一个响应式对象中的每一个属性,转换为`ref`对象。\n- 备注:`toRefs`与`toRef`功能一致,但`toRefs`可以批量转换。\n- 语法如下:\n```vue\n<template>\n <div class=\"person\">\n <h2>姓名:{{person.name}}</h2>\n <h2>年龄:{{person.age}}</h2>\n <h2>性别:{{person.gender}}</h2>\n <button @click=\"changeName\">修改名字</button>\n <button @click=\"changeAge\">修改年龄</button>\n <button @click=\"changeGender\">修改性别</button>\n </div>\n</template>\n\n<script lang=\"ts\" setup name=\"Person\">\n import {ref,reactive,toRefs,toRef} from \'vue\'\n\n // 数据\n let person = reactive({name:\'\', age:18, gender:\'\'})\n \n // 通过toRefs将person对象中的n个属性批量取出且依然保持响应式的能力\n let {name,gender} = toRefs(person)\n \n // 通过toRef将person对象中的gender属性取出且依然保持响应式的能力\n let age = toRef(person,\'age\')\n\n // 方法\n function changeName(){\n name.value += \'~\'\n }\n function changeAge(){\n age.value += 1\n }\n function changeGender(){\n gender.value = \'\'\n }\n</script>\n```\n## 3.8. 【computed】\n\n作用根据已有数据计算出新数据和`Vue2`中的`computed`作用一致)。\n\n<img src=\"images/computed.gif\" style=\"zoom:20%;\" /> \n\n```vue\n<template>\n <div class=\"person\">\n 姓:<input type=\"text\" v-model=\"firstName\"> <br>\n 名:<input type=\"text\" v-model=\"lastName\"> <br>\n 全名:<span>{{fullName}}</span> <br>\n <button @click=\"changeFullName\">全名改为li-si</button>\n </div>\n</template>\n\n<script setup lang=\"ts\" name=\"App\">\n import {ref,computed} from \'vue\'\n\n let firstName = ref(\'zhang\')\n let lastName = ref(\'san\')\n\n // 计算属性——只读取,不修改\n /* let fullName = computed(()=>{\n return firstName.value + \'-\' + lastName.value\n }) */\n\n\n // 计算属性——既读取又修改\n let fullName = computed({\n // 读取\n get(){\n return firstName.value + \'-\' + lastName.value\n },\n // 修改\n set(val){\n console.log(\'fullName\',val)\n firstName.value = val.split(\'-\')[0]\n lastName.value = val.split(\'-\')[1]\n }\n })\n\n function changeFullName(){\n fullName.value = \'li-si\'\n } \n</script>\n```\n## 3.9.【watch】\n\n- 作用:监视数据的变化(和`Vue2`中的`watch`作用一致)\n- 特点:`Vue3`中的`watch`只能监视以下**四种数据**\n> 1. `ref`定义的数据。\n> 2. `reactive`定义的数据。\n> 3. 函数返回一个值(`getter`函数)。\n> 4. 一个包含上述内容的数组。\n\n我们在`Vue3`中使用`watch`的时候,通常会遇到以下几种情况:\n### * 情况一\n监视`ref`定义的【基本类型】数据:直接写数据名即可,监视的是其`value`值的改变。\n\n```vue\n<template>\n <div class=\"person\">\n <h1>情况一监视【ref】定义的【基本类型】数据</h1>\n <h2>当前求和为:{{sum}}</h2>\n <button @click=\"changeSum\">点我sum+1</button>\n </div>\n</template>\n\n<script lang=\"ts\" setup name=\"Person\">\n import {ref,watch} from \'vue\'\n // 数据\n let sum = ref(0)\n // 方法\n function changeSum(){\n sum.value += 1\n }\n // 监视情况一监视【ref】定义的【基本类型】数据\n const stopWatch = watch(sum,(newValue,oldValue)=>{\n console.log(\'sum\',newValue,oldValue)\n if(newValue >= 10){\n stopWatch()\n }\n })\n</script>\n```\n### * 情况二\n监视`ref`定义的【对象类型】数据:直接写数据名,监视的是对象的【地址值】,若想监视对象内部的数据,要手动开启深度监视。\n\n> 注意:\n>\n> * 若修改的是`ref`定义的对象中的属性,`newValue` 和 `oldValue` 都是新值,因为它们是同一个对象。\n>\n> * 若修改整个`ref`定义的对象,`newValue` 是新值, `oldValue` 是旧值,因为不是同一个对象了。\n\n```vue\n<template>\n <div class=\"person\">\n <h1>情况二监视【ref】定义的【对象类型】数据</h1>\n <h2>姓名:{{ person.name }}</h2>\n <h2>年龄:{{ person.age }}</h2>\n <button @click=\"changeName\">修改名字</button>\n <button @click=\"changeAge\">修改年龄</button>\n <button @click=\"changePerson\">修改整个人</button>\n </div>\n</template>\n\n<script lang=\"ts\" setup name=\"Person\">\n import {ref,watch} from \'vue\'\n // 数据\n let person = ref({\n name:\'\',\n age:18\n })\n // 方法\n function changeName(){\n person.value.name += \'~\'\n }\n function changeAge(){\n person.value.age += 1\n }\n function changePerson(){\n person.value = {name:\'\',age:90}\n }\n /* \n 监视情况一监视【ref】定义的【对象类型】数据监视的是对象的地址值若想监视对象内部属性的变化需要手动开启深度监视\n watch的第一个参数是被监视的数据\n watch的第二个参数是监视的回调\n watch的第三个参数是配置对象deep、immediate等等..... \n */\n watch(person,(newValue,oldValue)=>{\n console.log(\'person\',newValue,oldValue)\n },{deep:true})\n \n</script>\n```\n### * 情况三\n监视`reactive`定义的【对象类型】数据,且默认开启了深度监视。\n```vue\n<template>\n <div class=\"person\">\n <h1>情况三监视【reactive】定义的【对象类型】数据</h1>\n <h2>姓名:{{ person.name }}</h2>\n <h2>年龄:{{ person.age }}</h2>\n <button @click=\"changeName\">修改名字</button>\n <button @click=\"changeAge\">修改年龄</button>\n <button @click=\"changePerson\">修改整个人</button>\n <hr>\n <h2>测试:{{obj.a.b.c}}</h2>\n <button @click=\"test\">修改obj.a.b.c</button>\n </div>\n</template>\n\n<script lang=\"ts\" setup name=\"Person\">\n import {reactive,watch} from \'vue\'\n // 数据\n let person = reactive({\n name:\'\',\n age:18\n })\n let obj = reactive({\n a:{\n b:{\n c:666\n }\n }\n })\n // 方法\n function changeName(){\n person.name += \'~\'\n }\n function changeAge(){\n person.age += 1\n }\n function changePerson(){\n Object.assign(person,{name:\'\',age:80})\n }\n function test(){\n obj.a.b.c = 888\n }\n\n // 监视情况三监视【reactive】定义的【对象类型】数据且默认是开启深度监视的\n watch(person,(newValue,oldValue)=>{\n console.log(\'person\',newValue,oldValue)\n })\n watch(obj,(newValue,oldValue)=>{\n console.log(\'Obj\',newValue,oldValue)\n })\n</script>\n```\n### * 情况四\n监视`ref`或`reactive`定义的【对象类型】数据中的**某个属性**,注意点如下:\n\n1. 若该属性值**不是**【对象类型】,需要写成函数形式。\n2. 若该属性值是**依然**是【对象类型】,可直接编,也可写成函数,建议写成函数。\n\n结论监视的要是对象里的属性那么最好写函数式注意点若是对象监视的是地址值需要关注对象内部需要手动开启深度监视。\n\n```vue\n<template>\n <div class=\"person\">\n <h1>情况四监视【ref】或【reactive】定义的【对象类型】数据中的某个属性</h1>\n <h2>姓名:{{ person.name }}</h2>\n <h2>年龄:{{ person.age }}</h2>\n <h2>汽车:{{ person.car.c1 }}、{{ person.car.c2 }}</h2>\n <button @click=\"changeName\">修改名字</button>\n <button @click=\"changeAge\">修改年龄</button>\n <button @click=\"changeC1\">修改第一台车</button>\n <button @click=\"changeC2\">修改第二台车</button>\n <button @click=\"changeCar\">修改整个车</button>\n </div>\n</template>\n\n<script lang=\"ts\" setup name=\"Person\">\n import {reactive,watch} from \'vue\'\n\n // 数据\n let person = reactive({\n name:\'\',\n age:18,\n car:{\n c1:\'\',\n c2:\'\'\n }\n })\n // 方法\n function changeName(){\n person.name += \'~\'\n }\n function changeAge(){\n person.age += 1\n }\n function changeC1(){\n person.car.c1 = \'\'\n }\n function changeC2(){\n person.car.c2 = \'\'\n }\n function changeCar(){\n person.car = {c1:\'\',c2:\'\'}\n }\n\n // 监视,情况四:监视响应式对象中的某个属性,且该属性是基本类型的,要写成函数式\n /* watch(()=> person.name,(newValue,oldValue)=>{\n console.log(\'person.name\',newValue,oldValue)\n }) */\n\n // 监视,情况四:监视响应式对象中的某个属性,且该属性是对象类型的,可以直接写,也能写函数,更推荐写函数\n watch(()=>person.car,(newValue,oldValue)=>{\n console.log(\'person.car\',newValue,oldValue)\n },{deep:true})\n</script>\n```\n### * 情况五\n监视上述的多个数据\n```vue\n<template>\n <div class=\"person\">\n <h1>情况五:监视上述的多个数据</h1>\n <h2>姓名:{{ person.name }}</h2>\n <h2>年龄:{{ person.age }}</h2>\n <h2>汽车:{{ person.car.c1 }}、{{ person.car.c2 }}</h2>\n <button @click=\"changeName\">修改名字</button>\n <button @click=\"changeAge\">修改年龄</button>\n <button @click=\"changeC1\">修改第一台车</button>\n <button @click=\"changeC2\">修改第二台车</button>\n <button @click=\"changeCar\">修改整个车</button>\n </div>\n</template>\n\n<script lang=\"ts\" setup name=\"Person\">\n import {reactive,watch} from \'vue\'\n\n // 数据\n let person = reactive({\n name:\'\',\n age:18,\n car:{\n c1:\'\',\n c2:\'\'\n }\n })\n // 方法\n function changeName(){\n person.name += \'~\'\n }\n function changeAge(){\n person.age += 1\n }\n function changeC1(){\n person.car.c1 = \'\'\n }\n function changeC2(){\n person.car.c2 = \'\'\n }\n function changeCar(){\n person.car = {c1:\'\',c2:\'\'}\n }\n\n // 监视,情况五:监视上述的多个数据\n watch([()=>person.name,person.car],(newValue,oldValue)=>{\n console.log(\'person.car\',newValue,oldValue)\n },{deep:true})\n\n</script>\n```\n## 3.10. 【watchEffect】\n\n* 官网:立即运行一个函数,同时响应式地追踪其依赖,并在依赖更改时重新执行该函数。\n\n* `watch`对比`watchEffect`\n\n > 1. 都能监听响应式数据的变化,不同的是监听数据变化的方式不同\n >\n > 2. `watch`:要明确指出监视的数据\n >\n > 3. `watchEffect`:不用明确指出监视的数据(函数中用到哪些属性,那就监视哪些属性)。\n\n* 示例代码:\n\n ```vue\n <template>\n <div class=\"person\">\n <h1>需求水温达到50℃或水位达到20cm则联系服务器</h1>\n <h2 id=\"demo\">水温:{{temp}}</h2>\n <h2>水位:{{height}}</h2>\n <button @click=\"changePrice\">水温+1</button>\n <button @click=\"changeSum\">水位+10</button>\n </div>\n </template>\n \n <script lang=\"ts\" setup name=\"Person\">\n import {ref,watch,watchEffect} from \'vue\'\n // 数据\n let temp = ref(0)\n let height = ref(0)\n \n // 方法\n function changePrice(){\n temp.value += 10\n }\n function changeSum(){\n height.value += 1\n }\n \n // 用watch实现需要明确的指出要监视temp、height\n watch([temp,height],(value)=>{\n // 从value中获取最新的temp值、height值\n const [newTemp,newHeight] = value\n // 室温达到50℃或水位达到20cm立刻联系服务器\n if(newTemp >= 50 || newHeight >= 20){\n console.log(\'\')\n }\n })\n \n // 用watchEffect实现不用\n const stopWtach = watchEffect(()=>{\n // 室温达到50℃或水位达到20cm立刻联系服务器\n if(temp.value >= 50 || height.value >= 20){\n console.log(document.getElementById(\'demo\')?.innerText)\n console.log(\'\')\n }\n // 水温达到100或水位达到50取消监视\n if(temp.value === 100 || height.value === 50){\n console.log(\'\')\n stopWtach()\n }\n })\n </script>\n ```\n \n \n\n## 3.11. 【标签的 ref 属性】\n\n作用用于注册模板引用。\n\n> * 用在普通`DOM`标签上,获取的是`DOM`节点。\n>\n> * 用在组件标签上,获取的是组件实例对象。\n\n用在普通`DOM`标签上:\n\n```vue\n<template>\n <div class=\"person\">\n <h1 ref=\"title1\">尚硅谷</h1>\n <h2 ref=\"title2\">前端</h2>\n <h3 ref=\"title3\">Vue</h3>\n <input type=\"text\" ref=\"inpt\"> <br><br>\n <button @click=\"showLog\">点我打印内容</button>\n </div>\n</template>\n\n<script lang=\"ts\" setup name=\"Person\">\n import {ref} from \'vue\'\n \n let title1 = ref()\n let title2 = ref()\n let title3 = ref()\n\n function showLog(){\n // 通过id获取元素\n const t1 = document.getElementById(\'title1\')\n // 打印内容\n console.log((t1 as HTMLElement).innerText)\n console.log((<HTMLElement>t1).innerText)\n console.log(t1?.innerText)\n \n /************************************/\n \n // 通过ref获取元素\n console.log(title1.value)\n console.log(title2.value)\n console.log(title3.value)\n }\n</script>\n```\n\n用在组件标签上\n\n```vue\n<!-- 父组件App.vue -->\n<template>\n <Person ref=\"ren\"/>\n <button @click=\"test\">测试</button>\n</template>\n\n<script lang=\"ts\" setup name=\"App\">\n import Person from \'./components/Person.vue\'\n import {ref} from \'vue\'\n\n let ren = ref()\n\n function test(){\n console.log(ren.value.name)\n console.log(ren.value.age)\n }\n</script>\n\n\n<!-- 子组件Person.vue中要使用defineExpose暴露内容 -->\n<script lang=\"ts\" setup name=\"Person\">\n import {ref,defineExpose} from \'vue\'\n // 数据\n let name = ref(\'\')\n let age = ref(18)\n /****************************/\n /****************************/\n // 使用defineExpose将组件中的数据交给外部\n defineExpose({name,age})\n</script>\n```\n\n\n\n## 3.12. 【props】\n\n> ```js\n>// 定义一个接口限制每个Person对象的格式\n> export interface PersonInter {\n> id:string,\n> name:string,\n> age:number\n> }\n> \n> // 定义一个自定义类型Persons\n> export type Persons = Array<PersonInter>\n> ```\n> \n> `App.vue`中代码:\n>\n> ```vue\n><template>\n> <Person :list=\"persons\"/>\n> </template>\n> \n> <script lang=\"ts\" setup name=\"App\">\n> import Person from \'./components/Person.vue\'\n> import {reactive} from \'vue\'\n> import {type Persons} from \'./types\'\n> \n> let persons = reactive<Persons>([\n> {id:\'e98219e12\',name:\'\',age:18},\n> {id:\'e98219e13\',name:\'\',age:19},\n> {id:\'e98219e14\',name:\'\',age:20}\n> ])\n> </script>\n> \n> ```\n> \n> `Person.vue`中代码:\n>\n> ```Vue\n><template>\n> <div class=\"person\">\n> <ul>\n> <li v-for=\"item in list\" :key=\"item.id\">\n> {{item.name}}--{{item.age}}\n> </li>\n> </ul>\n> </div>\n> </template>\n> \n> <script lang=\"ts\" setup name=\"Person\">\n> import {defineProps} from \'vue\'\n> import {type PersonInter} from \'@/types\'\n> \n> // 第一种写法:仅接收\n> // const props = defineProps([\'list\'])\n> \n> // 第二种写法:接收+限制类型\n> // defineProps<{list:Persons}>()\n> \n> // 第三种写法:接收+限制类型+指定默认值+限制必要性\n> let props = withDefaults(defineProps<{list?:Persons}>(),{\n> list:()=>[{id:\'asdasg01\',name:\'\',age:18}]\n> })\n> console.log(props)\n> </script>\n> ```\n> \n\n## 3.13. 【生命周期】\n\n* 概念:`Vue`组件实例在创建时要经历一系列的初始化步骤,在此过程中`Vue`会在合适的时机,调用特定的函数,从而让开发者有机会在特定阶段运行自己的代码,这些特定的函数统称为:生命周期钩子\n\n* 规律:\n\n > 生命周期整体分为四个阶段,分别是:**创建、挂载、更新、销毁**,每个阶段都有两个钩子,一前一后。\n\n* `Vue2`的生命周期\n\n > 创建阶段:`beforeCreate`、`created`\n >\n > 挂载阶段:`beforeMount`、`mounted`\n >\n > 更新阶段:`beforeUpdate`、`updated`\n >\n > 销毁阶段:`beforeDestroy`、`destroyed`\n\n* `Vue3`的生命周期\n\n > 创建阶段:`setup`\n >\n > 挂载阶段:`onBeforeMount`、`onMounted`\n >\n > 更新阶段:`onBeforeUpdate`、`onUpdated`\n >\n > 卸载阶段:`onBeforeUnmount`、`onUnmounted`\n\n* 常用的钩子:`onMounted`(挂载完毕)、`onUpdated`(更新完毕)、`onBeforeUnmount`(卸载之前)\n\n* 示例代码:\n\n ```vue\n <template>\n <div class=\"person\">\n <h2>当前求和为:{{ sum }}</h2>\n <button @click=\"changeSum\">点我sum+1</button>\n </div>\n </template>\n \n <!-- vue3写法 -->\n <script lang=\"ts\" setup name=\"Person\">\n import { \n ref, \n onBeforeMount, \n onMounted, \n onBeforeUpdate, \n onUpdated, \n onBeforeUnmount, \n onUnmounted \n } from \'vue\'\n \n // 数据\n let sum = ref(0)\n // 方法\n function changeSum() {\n sum.value += 1\n }\n console.log(\'setup\')\n // 生命周期钩子\n onBeforeMount(()=>{\n console.log(\'\')\n })\n onMounted(()=>{\n console.log(\'\')\n })\n onBeforeUpdate(()=>{\n console.log(\'\')\n })\n onUpdated(()=>{\n console.log(\'\')\n })\n onBeforeUnmount(()=>{\n console.log(\'\')\n })\n onUnmounted(()=>{\n console.log(\'\')\n })\n </script>\n ```\n\n## 3.14. 【自定义hook】\n\n- 什么是`hook`?—— 本质是一个函数,把`setup`函数中使用的`Composition API`进行了封装,类似于`vue2.x`中的`mixin`。\n\n- 自定义`hook`的优势:复用代码, 让`setup`中的逻辑更清楚易懂。\n\n示例代码\n\n- `useSum.ts`中内容如下:\n\n ```js\n import {ref,onMounted} from \'vue\'\n \n export default function(){\n let sum = ref(0)\n \n const increment = ()=>{\n sum.value += 1\n }\n const decrement = ()=>{\n sum.value -= 1\n }\n onMounted(()=>{\n increment()\n })\n \n //向外部暴露数据\n return {sum,increment,decrement}\n } \n ```\n \n- `useDog.ts`中内容如下:\n\n ```js\n import {reactive,onMounted} from \'vue\'\n import axios,{AxiosError} from \'axios\'\n \n export default function(){\n let dogList = reactive<string[]>([])\n \n // 方法\n async function getDog(){\n try {\n // 发请求\n let {data} = await axios.get(\'https://dog.ceo/api/breed/pembroke/images/random\')\n // 维护数据\n dogList.push(data.message)\n } catch (error) {\n // 处理错误\n const err = <AxiosError>error\n console.log(err.message)\n }\n }\n \n // 挂载钩子\n onMounted(()=>{\n getDog()\n })\n \n //向外部暴露数据\n return {dogList,getDog}\n }\n ```\n\n- 组件中具体使用:\n\n ```vue\n <template>\n <h2>当前求和为:{{sum}}</h2>\n <button @click=\"increment\">点我+1</button>\n <button @click=\"decrement\">点我-1</button>\n <hr>\n <img v-for=\"(u,index) in dogList.urlList\" :key=\"index\" :src=\"(u as string)\"> \n <span v-show=\"dogList.isLoading\">加载中......</span><br>\n <button @click=\"getDog\">再来一只狗</button>\n </template>\n \n <script lang=\"ts\">\n import {defineComponent} from \'vue\'\n \n export default defineComponent({\n name:\'App\',\n })\n </script>\n \n <script setup lang=\"ts\">\n import useSum from \'./hooks/useSum\'\n import useDog from \'./hooks/useDog\'\n \n let {sum,increment,decrement} = useSum()\n let {dogList,getDog} = useDog()\n </script>\n ```\n\n \n\n---\n\n# 4. 路由\n\n## 4.1. 【对路由的理解】\n\n<img src=\"images/image-20231018144351536.png\" alt=\"image-20231018144351536\" style=\"zoom:20%;border-radius:40px\" /> \n\n## 4.2. 【基本切换效果】\n\n- `Vue3`中要使用`vue-router`的最新版本,目前是`4`版本。\n\n- 路由配置文件代码如下:\n\n ```js\n import {createRouter,createWebHistory} from \'vue-router\'\n import Home from \'@/pages/Home.vue\'\n import News from \'@/pages/News.vue\'\n import About from \'@/pages/About.vue\'\n \n const router = createRouter({\n history:createWebHistory(),\n routes:[\n {\n path:\'/home\',\n component:Home\n },\n {\n path:\'/about\',\n component:About\n }\n ]\n })\n export default router\n ```\n* `main.ts`代码如下:\n\n ```js\n import router from \'./router/index\'\n app.use(router)\n \n app.mount(\'#app\')\n ```\n\n- `App.vue`代码如下\n\n ```vue\n <template>\n <div class=\"app\">\n <h2 class=\"title\">Vue路由测试</h2>\n <!-- 导航区 -->\n <div class=\"navigate\">\n <RouterLink to=\"/home\" active-class=\"active\">首页</RouterLink>\n <RouterLink to=\"/news\" active-class=\"active\">新闻</RouterLink>\n <RouterLink to=\"/about\" active-class=\"active\">关于</RouterLink>\n </div>\n <!-- 展示区 -->\n <div class=\"main-content\">\n <RouterView></RouterView>\n </div>\n </div>\n </template>\n \n <script lang=\"ts\" setup name=\"App\">\n import {RouterLink,RouterView} from \'vue-router\' \n </script>\n ```\n\n## 4.3. 【两个注意点】\n\n> 1. 路由组件通常存放在`pages` 或 `views`文件夹,一般组件通常存放在`components`文件夹。\n>\n> 2. 通过点击导航,视觉效果上“消失” 了的路由组件,默认是被**卸载**掉的,需要的时候再去**挂载**。\n\n## 4.4.【路由器工作模式】\n\n1. `history`模式\n\n > 优点:`URL`更加美观,不带有`#`,更接近传统的网站`URL`。\n >\n > 缺点:后期项目上线,需要服务端配合处理路径问题,否则刷新会有`404`错误。\n >\n > ```js\n > const router = createRouter({\n > history:createWebHistory(), //history模式\n > /******/\n > })\n > ```\n\n2. `hash`模式\n\n > 优点:兼容性更好,因为不需要服务器端处理路径。\n >\n > 缺点:`URL`带有`#`不太美观,且在`SEO`优化方面相对较差。\n >\n > ```js\n > const router = createRouter({\n > history:createWebHashHistory(), //hash模式\n > /******/\n > })\n > ```\n\n## 4.5. 【to的两种写法】\n\n```vue\n<!-- 第一种to的字符串写法 -->\n<router-link active-class=\"active\" to=\"/home\">主页</router-link>\n\n<!-- 第二种to的对象写法 -->\n<router-link active-class=\"active\" :to=\"{path:\'/home\'}\">Home</router-link>\n```\n\n## 4.6. 【命名路由】 \n\n作用可以简化路由跳转及传参后面就讲。\n\n给路由规则命名\n\n```js\nroutes:[\n {\n name:\'zhuye\',\n path:\'/home\',\n component:Home\n },\n {\n name:\'xinwen\',\n path:\'/news\',\n component:News,\n },\n {\n name:\'guanyu\',\n path:\'/about\',\n component:About\n }\n]\n```\n\n跳转路由\n\n```vue\n<!--简化前需要写完整的路径to的字符串写法 -->\n<router-link to=\"/news/detail\">跳转</router-link>\n\n<!--简化后直接通过名字跳转to的对象写法配合name属性 -->\n<router-link :to=\"{name:\'guanyu\'}\">跳转</router-link>\n```\n\n\n\n## 4.7. 【嵌套路由】\n\n1. 编写`News`的子路由:`Detail.vue`\n\n2. 配置路由规则,使用`children`配置项:\n\n ```ts\n const router = createRouter({\n history:createWebHistory(),\n routes:[\n {\n name:\'zhuye\',\n path:\'/home\',\n component:Home\n },\n {\n name:\'xinwen\',\n path:\'/news\',\n component:News,\n children:[\n {\n name:\'xiang\',\n path:\'detail\',\n component:Detail\n }\n ]\n },\n {\n name:\'guanyu\',\n path:\'/about\',\n component:About\n }\n ]\n })\n export default router\n ```\n \n3. 跳转路由(记得要加完整路径):\n\n ```vue\n <router-link to=\"/news/detail\">xxxx</router-link>\n <!-- 或 -->\n <router-link :to=\"{path:\'/news/detail\'}\">xxxx</router-link>\n ```\n\n4. 记得去`Home`组件中预留一个`<router-view>`\n\n ```vue\n <template>\n <div class=\"news\">\n <nav class=\"news-list\">\n <RouterLink v-for=\"news in newsList\" :key=\"news.id\" :to=\"{path:\'/news/detail\'}\">\n {{news.name}}\n </RouterLink>\n </nav>\n <div class=\"news-detail\">\n <RouterView/>\n </div>\n </div>\n </template>\n ```\n\n \n\n## 4.8. 【路由传参】\n\n### query参数\n\n 1. 传递参数\n\n ```vue\n <!-- 跳转并携带query参数to的字符串写法 -->\n <router-link to=\"/news/detail?a=1&b=2&content=欢迎你\">\n 跳转\n </router-link>\n \n <!-- 跳转并携带query参数to的对象写法 -->\n <RouterLink \n :to=\"{\n //name:\'xiang\', //用name也可以跳转\n path:\'/news/detail\',\n query:{\n id:news.id,\n title:news.title,\n content:news.content\n }\n }\"\n >\n {{news.title}}\n </RouterLink>\n ```\n\n 2. 接收参数:\n\n ```js\n import {useRoute} from \'vue-router\'\n const route = useRoute()\n // 打印query参数\n console.log(route.query)\n ```\n\n\n### params参数\n\n 1. 传递参数\n\n ```vue\n <!-- 跳转并携带params参数to的字符串写法 -->\n <RouterLink :to=\"`/news/detail/001/新闻001/内容001`\">{{news.title}}</RouterLink>\n \n <!-- 跳转并携带params参数to的对象写法 -->\n <RouterLink \n :to=\"{\n name:\'xiang\', //用name跳转\n params:{\n id:news.id,\n title:news.title,\n content:news.title\n }\n }\"\n >\n {{news.title}}\n </RouterLink>\n ```\n\n 2. 接收参数:\n\n ```js\n import {useRoute} from \'vue-router\'\n const route = useRoute()\n // 打印params参数\n console.log(route.params)\n ```\n\n> 备注1传递`params`参数时,若使用`to`的对象写法,必须使用`name`配置项,不能用`path`。\n>\n> 备注2传递`params`参数时,需要提前在规则中占位。\n\n## 4.9. 【路由的props配置】\n\n作用让路由组件更方便的收到参数可以将路由参数作为`props`传给组件)\n\n```js\n{\n name:\'xiang\',\n path:\'detail/:id/:title/:content\',\n component:Detail,\n\n // props的对象写法作用把对象中的每一组key-value作为props传给Detail组件\n // props:{a:1,b:2,c:3}, \n\n // props的布尔值写法作用把收到了每一组params参数作为props传给Detail组件\n // props:true\n \n // props的函数写法作用把返回的对象中每一组key-value作为props传给Detail组件\n props(route){\n return route.query\n }\n}\n```\n\n## 4.10. 【 replace属性】\n\n 1. 作用:控制路由跳转时操作浏览器历史记录的模式。\n\n 2. 浏览器的历史记录有两种写入方式:分别为```push```和```replace```\n\n - ```push```是追加历史记录(默认值)。\n - `replace`是替换当前记录。\n\n 3. 开启`replace`模式:\n\n ```vue\n <RouterLink replace .......>News</RouterLink>\n ```\n\n## 4.11. 【编程式导航】\n\n路由组件的两个重要的属性`$route`和`$router`变成了两个`hooks`\n\n```js\nimport {useRoute,useRouter} from \'vue-router\'\n\nconst route = useRoute()\nconst router = useRouter()\n\nconsole.log(route.query)\nconsole.log(route.parmas)\nconsole.log(router.push)\nconsole.log(router.replace)\n```\n\n## 4.12. 【重定向】\n\n1. 作用:将特定的路径,重新定向到已有路由。\n\n2. 具体编码:\n\n ```js\n {\n path:\'/\',\n redirect:\'/about\'\n }\n ```\n\n\n\n# 5. pinia \n\n## 5.1【准备一个效果】\n\n<img src=\"./images/pinia_example.gif\" alt=\"pinia_example\" style=\"zoom:30%;border:3px solid\" /> \n\n## 5.2【搭建 pinia 环境】\n\n第一步`npm install pinia`\n\n第二步操作`src/main.ts`\n\n```ts\nimport { createApp } from \'vue\'\nimport App from \'./App.vue\'\n\n/* 引入createPinia用于创建pinia */\nimport { createPinia } from \'pinia\'\n\n/* 创建pinia */\nconst pinia = createPinia()\nconst app = createApp(App)\n\n/* 使用插件 */{}\napp.use(pinia)\napp.mount(\'#app\')\n```\n\n此时开发者工具中已经有了`pinia`选项\n\n<img src=\"https://cdn.nlark.com/yuque/0/2023/png/35780599/1684309952481-c67f67f9-d1a3-4d69-8bd6-2b381e003f31.png\" style=\"zoom:80%;border:1px solid black;border-radius:10px\" />\n\n## 5.3【存储+读取数据】\n\n1. `Store`是一个保存:**状态**、**业务逻辑** 的实体,每个组件都可以**读取**、**写入**它。\n\n2. 它有三个概念:`state`、`getter`、`action`,相当于组件中的: `data`、 `computed` 和 `methods`。\n\n3. 具体编码:`src/store/count.ts`\n\n ```ts\n // 引入defineStore用于创建store\n import {defineStore} from \'pinia\'\n \n // 定义并暴露一个store\n export const useCountStore = defineStore(\'count\',{\n // 动作\n actions:{},\n // 状态\n state(){\n return {\n sum:6\n }\n },\n // 计算\n getters:{}\n })\n ```\n\n4. 具体编码:`src/store/talk.ts`\n\n ```js\n // 引入defineStore用于创建store\n import {defineStore} from \'pinia\'\n \n // 定义并暴露一个store\n export const useTalkStore = defineStore(\'talk\',{\n // 动作\n actions:{},\n // 状态\n state(){\n return {\n talkList:[\n {id:\'yuysada01\',content:\'\'},\n {id:\'yuysada02\',content:\'\'},\n {id:\'yuysada03\',content:\'\'}\n ]\n }\n },\n // 计算\n getters:{}\n })\n ```\n \n5. 组件中使用`state`中的数据\n\n ```vue\n <template>\n <h2>当前求和为:{{ sumStore.sum }}</h2>\n </template>\n \n <script setup lang=\"ts\" name=\"Count\">\n // 引入对应的useXxxxxStore \n import {useSumStore} from \'@/store/sum\'\n \n // 调用useXxxxxStore得到对应的store\n const sumStore = useSumStore()\n </script>\n ```\n\n ```vue\n <template>\n <ul>\n <li v-for=\"talk in talkStore.talkList\" :key=\"talk.id\">\n {{ talk.content }}\n </li>\n </ul>\n </template>\n \n <script setup lang=\"ts\" name=\"Count\">\n import axios from \'axios\'\n import {useTalkStore} from \'@/store/talk\'\n \n const talkStore = useTalkStore()\n </script>\n ```\n\n \n\n## 5.4.【修改数据】(三种方式)\n\n1. 第一种修改方式,直接修改\n\n ```ts\n countStore.sum = 666\n ```\n\n2. 第二种修改方式:批量修改\n\n ```ts\n countStore.$patch({\n sum:999,\n school:\'atguigu\'\n })\n ```\n\n3. 第三种修改方式:借助`action`修改(`action`中可以编写一些业务逻辑)\n\n ```js\n import { defineStore } from \'pinia\'\n \n export const useCountStore = defineStore(\'count\', {\n /*************/\n actions: {\n //加\n increment(value:number) {\n if (this.sum < 10) {\n //操作countStore中的sum\n this.sum += value\n }\n },\n //减\n decrement(value:number){\n if(this.sum > 1){\n this.sum -= value\n }\n }\n },\n /*************/\n })\n ```\n\n4. 组件中调用`action`即可\n\n ```js\n // 使用countStore\n const countStore = useCountStore()\n \n // 调用对应action\n countStore.incrementOdd(n.value)\n ```\n\n\n## 5.5.【storeToRefs】\n\n- 借助`storeToRefs`将`store`中的数据转为`ref`对象,方便在模板中使用。\n- 注意:`pinia`提供的`storeToRefs`只会将数据做转换,而`Vue`的`toRefs`会转换`store`中数据。\n\n```vue\n<template>\n <div class=\"count\">\n <h2>当前求和为:{{sum}}</h2>\n </div>\n</template>\n\n<script setup lang=\"ts\" name=\"Count\">\n import { useCountStore } from \'@/store/count\'\n /* 引入storeToRefs */\n import { storeToRefs } from \'pinia\'\n\n /* 得到countStore */\n const countStore = useCountStore()\n /* 使用storeToRefs转换countStore随后解构 */\n const {sum} = storeToRefs(countStore)\n</script>\n\n```\n\n## 5.6.【getters】\n\n 1. 概念:当`state`中的数据,需要经过处理后再使用时,可以使用`getters`配置。\n\n 2. 追加```getters```配置。\n\n ```js\n // 引入defineStore用于创建store\n import {defineStore} from \'pinia\'\n \n // 定义并暴露一个store\n export const useCountStore = defineStore(\'count\',{\n // 动作\n actions:{\n /************/\n },\n // 状态\n state(){\n return {\n sum:1,\n school:\'atguigu\'\n }\n },\n // 计算\n getters:{\n bigSum:(state):number => state.sum *10,\n upperSchool():string{\n return this. school.toUpperCase()\n }\n }\n })\n ```\n\n 3. 组件中读取数据:\n\n ```js\n const {increment,decrement} = countStore\n let {sum,school,bigSum,upperSchool} = storeToRefs(countStore)\n ```\n\n \n\n## 5.7.【$subscribe】\n\n通过 store 的 `$subscribe()` 方法侦听 `state` 及其变化\n\n```ts\ntalkStore.$subscribe((mutate,state)=>{\n console.log(\'LoveTalk\',mutate,state)\n localStorage.setItem(\'talk\',JSON.stringify(talkList.value))\n})\n```\n\n\n\n## 5.8. 【store组合式写法】\n\n```ts\nimport {defineStore} from \'pinia\'\nimport axios from \'axios\'\nimport {nanoid} from \'nanoid\'\nimport {reactive} from \'vue\'\n\nexport const useTalkStore = defineStore(\'talk\',()=>{\n // talkList就是state\n const talkList = reactive(\n JSON.parse(localStorage.getItem(\'talkList\') as string) || []\n )\n\n // getATalk函数相当于action\n async function getATalk(){\n // 发请求,下面这行的写法是:连续解构赋值+重命名\n let {data:{content:title}} = await axios.get(\'https://api.uomg.com/api/rand.qinghua?format=json\')\n // 把请求回来的字符串,包装成一个对象\n let obj = {id:nanoid(),title}\n // 放到数组中\n talkList.unshift(obj)\n }\n return {talkList,getATalk}\n})\n```\n\n\n\n# 6. 组件通信\n\n**`Vue3`组件通信和`Vue2`的区别:**\n\n* 移出事件总线,使用`mitt`代替。\n\n- `vuex`换成了`pinia`。\n- 把`.sync`优化到了`v-model`里面了。\n- 把`$listeners`所有的东西,合并到`$attrs`中了。\n- `$children`被砍掉了。\n\n**常见搭配形式:**\n\n<img src=\"images/image-20231119185900990.png\" alt=\"image-20231119185900990\" style=\"zoom:60%;\" /> \n\n## 6.1. 【props】\n\n概述`props`是使用频率最高的一种通信方式,常用与 **父 ↔ 子**。\n\n- 若 **父传子**:属性值是**非函数**。\n- 若 **子传父**:属性值是**函数**。\n\n父组件\n\n```vue\n<template>\n <div class=\"father\">\n <h3>父组件,</h3>\n <h4>我的车:{{ car }}</h4>\n <h4>儿子给的玩具:{{ toy }}</h4>\n <Child :car=\"car\" :getToy=\"getToy\"/>\n </div>\n</template>\n\n<script setup lang=\"ts\" name=\"Father\">\n import Child from \'./Child.vue\'\n import { ref } from \"vue\";\n // 数据\n const car = ref(\'\')\n const toy = ref()\n // 方法\n function getToy(value:string){\n toy.value = value\n }\n</script>\n```\n\n子组件\n\n```vue\n<template>\n <div class=\"child\">\n <h3>子组件</h3>\n <h4>我的玩具:{{ toy }}</h4>\n <h4>父给我的车:{{ car }}</h4>\n <button @click=\"getToy(toy)\">玩具给父亲</button>\n </div>\n</template>\n\n<script setup lang=\"ts\" name=\"Child\">\n import { ref } from \"vue\";\n const toy = ref(\'\')\n \n defineProps([\'car\',\'getToy\'])\n</script>\n```\n\n## 6.2. 【自定义事件】\n\n1. 概述:自定义事件常用于:**子 => 父。**\n2. 注意区分好:原生事件、自定义事件。\n\n- 原生事件:\n - 事件名是特定的(`click`、`mosueenter`等等) \n - 事件对象`$event`: 是包含事件相关信息的对象(`pageX`、`pageY`、`target`、`keyCode`\n- 自定义事件:\n - 事件名是任意名称\n - <strong style=\"color:red\">事件对象`$event`: 是调用`emit`时所提供的数据,可以是任意类型!!!</strong >\n\n3. 示例:\n\n ```html\n <!--在父组件中,给子组件绑定自定义事件:-->\n <Child @send-toy=\"toy = $event\"/>\n \n <!--注意区分原生事件与自定义事件中的$event-->\n <button @click=\"toy = $event\">测试</button>\n ```\n\n ```js\n //子组件中,触发事件:\n this.$emit(\'send-toy\', 具体数据)\n ```\n\n## 6.3. 【mitt】\n\n概述与消息订阅与发布`pubsub`)功能类似,可以实现任意组件间通信。\n\n安装`mitt`\n\n```shell\nnpm i mitt\n```\n\n新建文件`src\\utils\\emitter.ts`\n\n```javascript\n// 引入mitt \nimport mitt from \"mitt\";\n\n// 创建emitter\nconst emitter = mitt()\n\n/*\n // 绑定事件\n emitter.on(\'abc\',(value)=>{\n console.log(\'abc\',value)\n })\n emitter.on(\'xyz\',(value)=>{\n console.log(\'xyz\',value)\n })\n\n setInterval(() => {\n // 触发事件\n emitter.emit(\'abc\',666)\n emitter.emit(\'xyz\',777)\n }, 1000);\n\n setTimeout(() => {\n // 清理事件\n emitter.all.clear()\n }, 3000); \n*/\n\n// 创建并暴露mitt\nexport default emitter\n```\n\n接收数据的组件中绑定事件、同时在销毁前解绑事件\n\n```typescript\nimport emitter from \"@/utils/emitter\";\nimport { onUnmounted } from \"vue\";\n\n// 绑定事件\nemitter.on(\'send-toy\',(value)=>{\n console.log(\'send-toy\',value)\n})\n\nonUnmounted(()=>{\n // 解绑事件\n emitter.off(\'send-toy\')\n})\n```\n\n【第三步】提供数据的组件在合适的时候触发事件\n\n```javascript\nimport emitter from \"@/utils/emitter\";\n\nfunction sendToy(){\n // 触发事件\n emitter.emit(\'send-toy\',toy.value)\n}\n```\n\n**注意这个重要的内置关系,总线依赖着这个内置关系**\n\n## 6.4.【v-model】\n\n1. 概述:实现 **父↔子** 之间相互通信。\n\n2. 前序知识 —— `v-model`的本质\n\n ```vue\n <!-- 使用v-model指令 -->\n <input type=\"text\" v-model=\"userName\">\n \n <!-- v-model的本质是下面这行代码 -->\n <input \n type=\"text\" \n :value=\"userName\" \n @input=\"userName =(<HTMLInputElement>$event.target).value\"\n >\n ```\n\n3. 组件标签上的`v-model`的本质:`:moldeValue` `update:modelValue`事件。\n\n ```vue\n <!-- 组件标签上使用v-model指令 -->\n <AtguiguInput v-model=\"userName\"/>\n \n <!-- 组件标签上v-model的本质 -->\n <AtguiguInput :modelValue=\"userName\" @update:model-value=\"userName = $event\"/>\n ```\n\n `AtguiguInput`组件中:\n\n ```vue\n <template>\n <div class=\"box\">\n <!--将接收的value值赋给input元素的value属性目的是为了呈现数据 -->\n <!--给input元素绑定原生input事件触发input事件时进而触发update:model-value事件-->\n <input \n type=\"text\" \n :value=\"modelValue\" \n @input=\"emit(\'update:model-value\',$event.target.value)\"\n >\n </div>\n </template>\n \n <script setup lang=\"ts\" name=\"AtguiguInput\">\n // 接收props\n defineProps([\'modelValue\'])\n // 声明事件\n const emit = defineEmits([\'update:model-value\'])\n </script>\n ```\n\n4. 也可以更换`value`,例如改成`abc`\n\n ```vue\n <!-- 也可以更换value例如改成abc-->\n <AtguiguInput v-model:abc=\"userName\"/>\n \n <!-- 上面代码的本质如下 -->\n <AtguiguInput :abc=\"userName\" @update:abc=\"userName = $event\"/>\n ```\n\n `AtguiguInput`组件中:\n\n ```vue\n <template>\n <div class=\"box\">\n <input \n type=\"text\" \n :value=\"abc\" \n @input=\"emit(\'update:abc\',$event.target.value)\"\n >\n </div>\n </template>\n \n <script setup lang=\"ts\" name=\"AtguiguInput\">\n // 接收props\n defineProps([\'abc\'])\n // 声明事件\n const emit = defineEmits([\'update:abc\'])\n </script>\n ```\n\n5. 如果`value`可以更换,那么就可以在组件标签上多次使用`v-model`\n\n ```vue\n <AtguiguInput v-model:abc=\"userName\" v-model:xyz=\"password\"/>\n ```\n\n \n\n\n## 6.5.【$attrs 】\n\n1. 概述:`$attrs`用于实现**当前组件的父组件**,向**当前组件的子组件**通信(**祖→孙**)。\n\n2. 具体说明:`$attrs`是一个对象,包含所有父组件传入的标签属性。\n\n > 注意:`$attrs`会自动排除`props`中声明的属性(可以认为声明过的 `props` 被子组件自己“消费”了)\n\n父组件\n\n```vue\n<template>\n <div class=\"father\">\n <h3>父组件</h3>\n <Child :a=\"a\" :b=\"b\" :c=\"c\" :d=\"d\" v-bind=\"{x:100,y:200}\" :updateA=\"updateA\"/>\n </div>\n</template>\n\n<script setup lang=\"ts\" name=\"Father\">\n import Child from \'./Child.vue\'\n import { ref } from \"vue\";\n let a = ref(1)\n let b = ref(2)\n let c = ref(3)\n let d = ref(4)\n\n function updateA(value){\n a.value = value\n }\n</script>\n```\n\n子组件\n\n```vue\n<template>\n <div class=\"child\">\n <h3>子组件</h3>\n <GrandChild v-bind=\"$attrs\"/>\n </div>\n</template>\n\n<script setup lang=\"ts\" name=\"Child\">\n import GrandChild from \'./GrandChild.vue\'\n</script>\n```\n\n孙组件\n\n```vue\n<template>\n <div class=\"grand-child\">\n <h3>孙组件</h3>\n <h4>a{{ a }}</h4>\n <h4>b{{ b }}</h4>\n <h4>c{{ c }}</h4>\n <h4>d{{ d }}</h4>\n <h4>x{{ x }}</h4>\n <h4>y{{ y }}</h4>\n <button @click=\"updateA(666)\">点我更新A</button>\n </div>\n</template>\n\n<script setup lang=\"ts\" name=\"GrandChild\">\n defineProps([\'a\',\'b\',\'c\',\'d\',\'x\',\'y\',\'updateA\'])\n</script>\n```\n\n## 6.6. 【$refs、$parent】\n\n1. 概述:\n\n * `$refs`用于 **父→子。**\n * `$parent`用于:**子→父。**\n\n2. 原理如下:\n\n | 属性 | 说明 |\n | --------- | -------------------------------------------------------- |\n | `$refs` | 值为对象,包含所有被`ref`属性标识的`DOM`元素或组件实例。 |\n | `$parent` | 值为对象,当前组件的父组件实例对象。 |\n\n## 6.7. 【provide、inject】\n\n1. 概述:实现**祖孙组件**直接通信\n\n2. 具体使用:\n\n * 在祖先组件中通过`provide`配置向后代组件提供数据\n * 在后代组件中通过`inject`配置来声明接收数据\n\n4. 具体编码:\n\n 【第一步】父组件中,使用`provide`提供数据\n\n ```vue\n <template>\n <div class=\"father\">\n <h3>父组件</h3>\n <h4>资产:{{ money }}</h4>\n <h4>汽车:{{ car }}</h4>\n <button @click=\"money += 1\">资产+1</button>\n <button @click=\"car.price += 1\">汽车价格+1</button>\n <Child/>\n </div>\n </template>\n \n <script setup lang=\"ts\" name=\"Father\">\n import Child from \'./Child.vue\'\n import { ref,reactive,provide } from \"vue\";\n // 数据\n let money = ref(100)\n let car = reactive({\n brand:\'\',\n price:100\n })\n // 用于更新money的方法\n function updateMoney(value:number){\n money.value += value\n }\n // 提供数据\n provide(\'moneyContext\',{money,updateMoney})\n provide(\'car\',car)\n </script>\n ```\n \n > 注意:子组件中不用编写任何东西,是不受到任何打扰的\n \n 【第二步】孙组件中使用`inject`配置项接受数据。\n \n ```vue\n <template>\n <div class=\"grand-child\">\n <h3>我是孙组件</h3>\n <h4>资产:{{ money }}</h4>\n <h4>汽车:{{ car }}</h4>\n <button @click=\"updateMoney(6)\">点我</button>\n </div>\n </template>\n \n <script setup lang=\"ts\" name=\"GrandChild\">\n import { inject } from \'vue\';\n // 注入数据\n let {money,updateMoney} = inject(\'moneyContext\',{money:0,updateMoney:(x:number)=>{}})\n let car = inject(\'car\')\n</script>\n ```\n\n\n## 6.8. 【pinia】\n\n参考之前`pinia`部分的讲解\n\n## 6.9. 【slot】\n\n### 1. 默认插槽\n\n![img](http://49.232.112.44/images/default_slot.png)\n\n```vue\n父组件中\n <Category title=\"今日热门游戏\">\n <ul>\n <li v-for=\"g in games\" :key=\"g.id\">{{ g.name }}</li>\n </ul>\n </Category>\n子组件中\n <template>\n <div class=\"item\">\n <h3>{{ title }}</h3>\n <!-- 默认插槽 -->\n <slot></slot>\n </div>\n </template>\n```\n\n### 2. 具名插槽\n\n```vue\n父组件中\n <Category title=\"今日热门游戏\">\n <template v-slot:s1>\n <ul>\n <li v-for=\"g in games\" :key=\"g.id\">{{ g.name }}</li>\n </ul>\n </template>\n <template #s2>\n <a href=\"\">更多</a>\n </template>\n </Category>\n子组件中\n <template>\n <div class=\"item\">\n <h3>{{ title }}</h3>\n <slot name=\"s1\"></slot>\n <slot name=\"s2\"></slot>\n </div>\n </template>\n```\n\n### 3. 作用域插槽 \n\n1. 理解:<span style=\"color:red\">数据在组件的自身,但根据数据生成的结构需要组件的使用者来决定。</span>(新闻数据在`News`组件中,但使用数据所遍历出来的结构由`App`组件决定)\n\n3. 具体编码:\n\n ```vue\n 父组件中:\n <Game v-slot=\"params\">\n <!-- <Game v-slot:default=\"params\"> -->\n <!-- <Game #default=\"params\"> -->\n <ul>\n <li v-for=\"g in params.games\" :key=\"g.id\">{{ g.name }}</li>\n </ul>\n </Game>\n \n 子组件中:\n <template>\n <div class=\"category\">\n <h2>今日游戏榜单</h2>\n <slot :games=\"games\" a=\"哈哈\"></slot>\n </div>\n </template>\n \n <script setup lang=\"ts\" name=\"Category\">\n import {reactive} from \'vue\'\n let games = reactive([\n {id:\'asgdytsa01\',name:\'\'},\n {id:\'asgdytsa02\',name:\'耀\'},\n {id:\'asgdytsa03\',name:\'\'},\n {id:\'asgdytsa04\',name:\'\'}\n ])\n </script>\n ```\n\n\n\n# 7. 其它 API\n\n## 7.1.【shallowRef 与 shallowReactive 】\n\n### `shallowRef`\n\n1. 作用:创建一个响应式数据,但只对顶层属性进行响应式处理。\n\n2. 用法:\n\n ```js\n let myVar = shallowRef(initialValue);\n ```\n\n3. 特点:只跟踪引用值的变化,不关心值内部的属性变化。\n\n### `shallowReactive`\n\n1. 作用:创建一个浅层响应式对象,只会使对象的最顶层属性变成响应式的,对象内部的嵌套属性则不会变成响应式的\n\n2. 用法:\n\n ```js\n const myObj = shallowReactive({ ... });\n ```\n\n3. 特点:对象的顶层属性是响应式的,但嵌套对象的属性不是。\n\n### 总结\n\n> 通过使用 [`shallowRef()`](https://cn.vuejs.org/api/reactivity-advanced.html#shallowref) 和 [`shallowReactive()`](https://cn.vuejs.org/api/reactivity-advanced.html#shallowreactive) 来绕开深度响应。浅层式 `API` 创建的状态只在其顶层是响应式的,对所有深层的对象不会做任何处理,避免了对每一个内部属性做响应式所带来的性能成本,这使得属性的访问变得更快,可提升性能。\n\n\n\n## 7.2.【readonly 与 shallowReadonly】\n\n### **`readonly`**\n\n1. 作用:用于创建一个对象的深只读副本。\n\n2. 用法:\n\n ```js\n const original = reactive({ ... });\n const readOnlyCopy = readonly(original);\n ```\n\n3. 特点:\n\n * 对象的所有嵌套属性都将变为只读。\n * 任何尝试修改这个对象的操作都会被阻止(在开发模式下,还会在控制台中发出警告)。\n\n4. 应用场景:\n * 创建不可变的状态快照。\n * 保护全局状态或配置不被修改。\n\n### **`shallowReadonly`**\n\n1. 作用:与 `readonly` 类似,但只作用于对象的顶层属性。\n\n2. 用法:\n\n ```js\n const original = reactive({ ... });\n const shallowReadOnlyCopy = shallowReadonly(original);\n ```\n\n3. 特点:\n\n * 只将对象的顶层属性设置为只读,对象内部的嵌套属性仍然是可变的。\n\n * 适用于只需保护对象顶层属性的场景。\n\n \n\n## 7.3.【toRaw 与 markRaw】\n\n### `toRaw`\n\n1. 作用:用于获取一个响应式对象的原始对象, `toRaw` 返回的对象不再是响应式的,不会触发视图更新。\n\n > 官网描述:这是一个可以用于临时读取而不引起代理访问/跟踪开销,或是写入而不触发更改的特殊方法。不建议保存对原始对象的持久引用,请谨慎使用。\n\n > 何时使用? —— 在需要将响应式对象传递给非 `Vue` 的库或外部系统时,使用 `toRaw` 可以确保它们收到的是普通对象\n\n2. 具体编码:\n\n ```js\n import { reactive,toRaw,markRaw,isReactive } from \"vue\";\n \n /* toRaw */\n // 响应式对象\n let person = reactive({name:\'tony\',age:18})\n // 原始对象\n let rawPerson = toRaw(person)\n \n \n /* markRaw */\n let citysd = markRaw([\n {id:\'asdda01\',name:\'\'},\n {id:\'asdda02\',name:\'\'},\n {id:\'asdda03\',name:\'\'},\n {id:\'asdda04\',name:\'\'}\n ])\n // 根据原始对象citys去创建响应式对象citys2 —— 创建失败因为citys被markRaw标记了\n let citys2 = reactive(citys)\n console.log(isReactive(person))\n console.log(isReactive(rawPerson))\n console.log(isReactive(citys))\n console.log(isReactive(citys2))\n ```\n\n### `markRaw`\n\n1. 作用:标记一个对象,使其**永远不会**变成响应式的。\n\n > 例如使用`mockjs`时,为了防止误把`mockjs`变为响应式对象,可以使用 `markRaw` 去标记`mockjs`\n\n2. 编码:\n\n ```js\n /* markRaw */\n let citys = markRaw([\n {id:\'asdda01\',name:\'\'},\n {id:\'asdda02\',name:\'\'},\n {id:\'asdda03\',name:\'\'},\n {id:\'asdda04\',name:\'\'}\n ])\n // 根据原始对象citys去创建响应式对象citys2 —— 创建失败因为citys被markRaw标记了\n let citys2 = reactive(citys)\n ```\n\n## 7.4.【customRef】\n\n作用创建一个自定义的`ref`,并对其依赖项跟踪和更新触发进行逻辑控制。\n\n实现防抖效果`useSumRef.ts`\n\n```typescript\nimport {customRef } from \"vue\";\n\nexport default function(initValue:string,delay:number){\n let msg = customRef((track,trigger)=>{\n let timer:number\n return {\n get(){\n track() // 告诉Vue数据msg很重要要对msg持续关注一旦变化就更新\n return initValue\n },\n set(value){\n clearTimeout(timer)\n timer = setTimeout(() => {\n initValue = value\n trigger() //通知Vue数据msg变化了\n }, delay);\n }\n }\n }) \n return {msg}\n}\n```\n\n组件中使用\n\n\n\n\n\n# 8. Vue3新组件\n\n## 8.1. 【Teleport】\n\n- 什么是Teleport—— Teleport 是一种能够将我们的**组件html结构**移动到指定位置的技术。\n\n```html\n<teleport to=\'body\' >\n <div class=\"modal\" v-show=\"isShow\">\n <h2>我是一个弹窗</h2>\n <p>我是弹窗中的一些内容</p>\n <button @click=\"isShow = false\">关闭弹窗</button>\n </div>\n</teleport>\n```\n\n## 8.2. 【Suspense】\n\n- 等待异步组件时渲染一些额外内容,让应用有更好的用户体验 \n- 使用步骤: \n - 异步引入组件\n - 使用`Suspense`包裹组件,并配置好`default` 与 `fallback`\n\n```tsx\nimport { defineAsyncComponent,Suspense } from \"vue\";\nconst Child = defineAsyncComponent(()=>import(\'./Child.vue\'))\n```\n\n```vue\n<template>\n <div class=\"app\">\n <h3>我是App组件</h3>\n <Suspense>\n <template v-slot:default>\n <Child/>\n </template>\n <template v-slot:fallback>\n <h3>加载中.......</h3>\n </template>\n </Suspense>\n </div>\n</template>\n```\n\n\n\n## 8.3.【全局API转移到应用对象】\n\n- `app.component`\n- `app.config`\n- `app.directive`\n- `app.mount`\n- `app.unmount`\n- `app.use`\n\n## 8.4.【其他】\n\n- 过渡类名 `v-enter` 修改为 `v-enter-from`、过渡类名 `v-leave` 修改为 `v-leave-from`。\n\n\n- `keyCode` 作为 `v-on` 修饰符的支持。\n\n- `v-model` 指令在组件上的使用已经被重新设计,替换掉了 `v-bind.sync。`\n\n- `v-if` 和 `v-for` 在同一个元素身上使用时的优先级发生了变化。\n\n- 移除了`$on`、`$off` 和 `$once` 实例方法。\n\n- 移除了过滤器 `filter`。\n\n- 移除了`$children` 实例 `propert`。\n\n ......\n','2024-12-17 05:38:44','2024-12-17 05:38:44',0),(38,4,'zxczxc','zxczxczzcx','2024-12-22 14:17:44','2024-12-22 14:17:44',0),(39,4,'saas','sadasd','2024-12-22 14:35:41','2024-12-22 14:35:41',0),(40,4,'saas','sadasd','2024-12-22 14:36:02','2024-12-22 14:36:02',0),(41,4,'saas','sadasd','2024-12-22 14:38:00','2024-12-22 14:38:00',0),(42,4,'saas','sadasd','2024-12-22 14:38:08','2024-12-22 14:38:08',0),(43,4,'','','2024-12-22 14:40:43','2024-12-22 14:40:43',0),(44,4,'','zxczxc','2024-12-22 14:41:25','2024-12-22 14:41:25',0),(46,4,'test','xczcsc','2024-12-22 14:42:40','2024-12-22 14:42:40',0),(47,4,'2024年12月28日08:41:37文章示例001','# f\n## f\n### f\n#### f\n##### f\n###### f\n','2024-12-28 00:41:46','2024-12-28 00:41:46',0),(51,4,'sss','ssssss','2024-12-28 07:54:57','2024-12-29 10:26:52',0),(52,4,'md语法展示','# 标题一欢迎来到Markdown的世界\n\n## 标题二了解Markdown的基础语法\n\n### 标题三:文本样式\n\n**粗体文字**可以这样写,而*斜体文字*则是另一种强调的方式。如果我们想要***粗斜体文字***,只需要结合两者。\n\n### 标题四:列表\n\n#### 无序列表\n- 苹果\n- 香蕉\n- 橙子\n\n#### 有序列表\n1. 第一步\n2. 第二步\n3. 第三步\n\n### 标题五:链接与图片\n\n[访问阿里巴巴云](https://www.aliyun.com)获取更多关于云计算的信息。\n\n![Markdown Logo](https://markdown-here.com/img/icon256.png)\n\n### 标题六:代码块\n\n行内`code`使用反引号包裹。\n\n多行代码块使用三个反引号\n```python\ndef hello_world():\n print(\"Hello, world!\")','2024-12-29 13:43:05','2024-12-29 13:43:05',0);
/*!40000 ALTER TABLE `posts` ENABLE KEYS */;
UNLOCK TABLES;
--
-- Table structure for table `reactions`
--
DROP TABLE IF EXISTS `reactions`;
/*!40101 SET @saved_cs_client = @@character_set_client */;
/*!50503 SET character_set_client = utf8mb4 */;
CREATE TABLE `reactions` (
`reaction_id` int NOT NULL AUTO_INCREMENT,
`post_id` int NOT NULL,
`user_id` int NOT NULL,
`type` enum('like','dislike') NOT NULL,
`created_at` timestamp NULL DEFAULT CURRENT_TIMESTAMP,
PRIMARY KEY (`reaction_id`),
UNIQUE KEY `post_id` (`post_id`,`user_id`),
UNIQUE KEY `unique_user_post_reaction` (`post_id`,`user_id`),
KEY `user_id` (`user_id`),
CONSTRAINT `reactions_ibfk_1` FOREIGN KEY (`post_id`) REFERENCES `posts` (`post_id`) ON DELETE CASCADE,
CONSTRAINT `reactions_ibfk_2` FOREIGN KEY (`user_id`) REFERENCES `users` (`user_id`) ON DELETE CASCADE
) ENGINE=InnoDB AUTO_INCREMENT=6 DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_0900_ai_ci;
/*!40101 SET character_set_client = @saved_cs_client */;
--
-- Dumping data for table `reactions`
--
LOCK TABLES `reactions` WRITE;
/*!40000 ALTER TABLE `reactions` DISABLE KEYS */;
INSERT INTO `reactions` VALUES (5,36,4,'like','2024-12-17 05:00:56');
/*!40000 ALTER TABLE `reactions` ENABLE KEYS */;
UNLOCK TABLES;
/*!50003 SET @saved_cs_client = @@character_set_client */ ;
/*!50003 SET @saved_cs_results = @@character_set_results */ ;
/*!50003 SET @saved_col_connection = @@collation_connection */ ;
/*!50003 SET character_set_client = utf8mb4 */ ;
/*!50003 SET character_set_results = utf8mb4 */ ;
/*!50003 SET collation_connection = utf8mb4_0900_ai_ci */ ;
/*!50003 SET @saved_sql_mode = @@sql_mode */ ;
/*!50003 SET sql_mode = 'ONLY_FULL_GROUP_BY,STRICT_TRANS_TABLES,NO_ZERO_IN_DATE,NO_ZERO_DATE,ERROR_FOR_DIVISION_BY_ZERO,NO_ENGINE_SUBSTITUTION' */ ;
DELIMITER ;;
/*!50003 CREATE*/ /*!50017 DEFINER=`root`@`localhost`*/ /*!50003 TRIGGER `update_priority_on_like` AFTER INSERT ON `reactions` FOR EACH ROW BEGIN
IF NEW.type = 'like' THEN
UPDATE posts
SET priority = (SELECT COUNT(*) FROM reactions WHERE post_id = NEW.post_id AND type = 'like') * 2 -
(SELECT COUNT(*) FROM reactions WHERE post_id = NEW.post_id AND type = 'dislike')
WHERE post_id = NEW.post_id;
END IF;
END */;;
DELIMITER ;
/*!50003 SET sql_mode = @saved_sql_mode */ ;
/*!50003 SET character_set_client = @saved_cs_client */ ;
/*!50003 SET character_set_results = @saved_cs_results */ ;
/*!50003 SET collation_connection = @saved_col_connection */ ;
/*!50003 SET @saved_cs_client = @@character_set_client */ ;
/*!50003 SET @saved_cs_results = @@character_set_results */ ;
/*!50003 SET @saved_col_connection = @@collation_connection */ ;
/*!50003 SET character_set_client = utf8mb4 */ ;
/*!50003 SET character_set_results = utf8mb4 */ ;
/*!50003 SET collation_connection = utf8mb4_0900_ai_ci */ ;
/*!50003 SET @saved_sql_mode = @@sql_mode */ ;
/*!50003 SET sql_mode = 'ONLY_FULL_GROUP_BY,STRICT_TRANS_TABLES,NO_ZERO_IN_DATE,NO_ZERO_DATE,ERROR_FOR_DIVISION_BY_ZERO,NO_ENGINE_SUBSTITUTION' */ ;
DELIMITER ;;
/*!50003 CREATE*/ /*!50017 DEFINER=`root`@`localhost`*/ /*!50003 TRIGGER `update_priority_on_dislike` AFTER INSERT ON `reactions` FOR EACH ROW BEGIN
IF NEW.type = 'dislike' THEN
UPDATE posts
SET priority = (SELECT COUNT(*) FROM reactions WHERE post_id = NEW.post_id AND type = 'like') * 2 -
(SELECT COUNT(*) FROM reactions WHERE post_id = NEW.post_id AND type = 'dislike')
WHERE post_id = NEW.post_id;
END IF;
END */;;
DELIMITER ;
/*!50003 SET sql_mode = @saved_sql_mode */ ;
/*!50003 SET character_set_client = @saved_cs_client */ ;
/*!50003 SET character_set_results = @saved_cs_results */ ;
/*!50003 SET collation_connection = @saved_col_connection */ ;
/*!50003 SET @saved_cs_client = @@character_set_client */ ;
/*!50003 SET @saved_cs_results = @@character_set_results */ ;
/*!50003 SET @saved_col_connection = @@collation_connection */ ;
/*!50003 SET character_set_client = utf8mb4 */ ;
/*!50003 SET character_set_results = utf8mb4 */ ;
/*!50003 SET collation_connection = utf8mb4_0900_ai_ci */ ;
/*!50003 SET @saved_sql_mode = @@sql_mode */ ;
/*!50003 SET sql_mode = 'ONLY_FULL_GROUP_BY,STRICT_TRANS_TABLES,NO_ZERO_IN_DATE,NO_ZERO_DATE,ERROR_FOR_DIVISION_BY_ZERO,NO_ENGINE_SUBSTITUTION' */ ;
DELIMITER ;;
/*!50003 CREATE*/ /*!50017 DEFINER=`root`@`localhost`*/ /*!50003 TRIGGER `update_priority_on_delete_like` AFTER DELETE ON `reactions` FOR EACH ROW BEGIN
IF OLD.type = 'like' THEN
UPDATE posts
SET priority = (SELECT COUNT(*) FROM reactions WHERE post_id = OLD.post_id AND type = 'like') * 2 -
(SELECT COUNT(*) FROM reactions WHERE post_id = OLD.post_id AND type = 'dislike')
WHERE post_id = OLD.post_id;
END IF;
END */;;
DELIMITER ;
/*!50003 SET sql_mode = @saved_sql_mode */ ;
/*!50003 SET character_set_client = @saved_cs_client */ ;
/*!50003 SET character_set_results = @saved_cs_results */ ;
/*!50003 SET collation_connection = @saved_col_connection */ ;
/*!50003 SET @saved_cs_client = @@character_set_client */ ;
/*!50003 SET @saved_cs_results = @@character_set_results */ ;
/*!50003 SET @saved_col_connection = @@collation_connection */ ;
/*!50003 SET character_set_client = utf8mb4 */ ;
/*!50003 SET character_set_results = utf8mb4 */ ;
/*!50003 SET collation_connection = utf8mb4_0900_ai_ci */ ;
/*!50003 SET @saved_sql_mode = @@sql_mode */ ;
/*!50003 SET sql_mode = 'ONLY_FULL_GROUP_BY,STRICT_TRANS_TABLES,NO_ZERO_IN_DATE,NO_ZERO_DATE,ERROR_FOR_DIVISION_BY_ZERO,NO_ENGINE_SUBSTITUTION' */ ;
DELIMITER ;;
/*!50003 CREATE*/ /*!50017 DEFINER=`root`@`localhost`*/ /*!50003 TRIGGER `update_priority_on_delete_dislike` AFTER DELETE ON `reactions` FOR EACH ROW BEGIN
IF OLD.type = 'dislike' THEN
UPDATE posts
SET priority = (SELECT COUNT(*) FROM reactions WHERE post_id = OLD.post_id AND type = 'like') * 2 -
(SELECT COUNT(*) FROM reactions WHERE post_id = OLD.post_id AND type = 'dislike')
WHERE post_id = OLD.post_id;
END IF;
END */;;
DELIMITER ;
/*!50003 SET sql_mode = @saved_sql_mode */ ;
/*!50003 SET character_set_client = @saved_cs_client */ ;
/*!50003 SET character_set_results = @saved_cs_results */ ;
/*!50003 SET collation_connection = @saved_col_connection */ ;
--
-- Table structure for table `users`
--
DROP TABLE IF EXISTS `users`;
/*!40101 SET @saved_cs_client = @@character_set_client */;
/*!50503 SET character_set_client = utf8mb4 */;
CREATE TABLE `users` (
`user_id` int NOT NULL AUTO_INCREMENT,
`username` varchar(50) NOT NULL,
`email` varchar(100) NOT NULL,
`password` varchar(255) NOT NULL,
`status` enum('admin','normal','blocked','deactivated','pending') DEFAULT 'pending',
`created_at` timestamp NULL DEFAULT CURRENT_TIMESTAMP,
`updated_at` timestamp NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,
`is_active` tinyint(1) DEFAULT '1',
`activation_token` varchar(255) DEFAULT NULL,
`reset_password_token` varchar(255) DEFAULT NULL,
`reset_password_expires` timestamp NULL DEFAULT NULL,
PRIMARY KEY (`user_id`),
UNIQUE KEY `username` (`username`),
UNIQUE KEY `email` (`email`),
UNIQUE KEY `email_2` (`email`)
) ENGINE=InnoDB AUTO_INCREMENT=10 DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_0900_ai_ci;
/*!40101 SET character_set_client = @saved_cs_client */;
--
-- Dumping data for table `users`
--
LOCK TABLES `users` WRITE;
/*!40000 ALTER TABLE `users` DISABLE KEYS */;
INSERT INTO `users` VALUES (1,'','user1@example.com','x','normal','2024-12-12 11:38:18','2024-12-16 07:42:27',1,NULL,NULL,NULL),(2,'user2','user2@example.com','654321','normal','2024-12-12 11:38:18','2024-12-14 01:53:08',1,NULL,NULL,NULL),(3,'user20','user20@example.com','111111','normal','2024-12-12 11:38:18','2024-12-14 01:53:08',1,NULL,NULL,NULL),(4,'sss','xxs@xxs.com','sss','normal','2024-12-14 16:31:18','2024-12-27 14:54:06',1,NULL,NULL,NULL),(5,'LeJingS','3489749586@qq.com','lejings_de_mima','normal','2024-12-16 05:07:41','2024-12-16 05:07:41',1,NULL,NULL,NULL),(8,'小号','2193629878@qq.com','xiaohao','pending','2024-12-16 07:04:43','2024-12-16 07:04:43',1,NULL,NULL,NULL);
/*!40000 ALTER TABLE `users` ENABLE KEYS */;
UNLOCK TABLES;
/*!40103 SET TIME_ZONE=@OLD_TIME_ZONE */;
/*!40101 SET SQL_MODE=@OLD_SQL_MODE */;
/*!40014 SET FOREIGN_KEY_CHECKS=@OLD_FOREIGN_KEY_CHECKS */;
/*!40014 SET UNIQUE_CHECKS=@OLD_UNIQUE_CHECKS */;
/*!40101 SET CHARACTER_SET_CLIENT=@OLD_CHARACTER_SET_CLIENT */;
/*!40101 SET CHARACTER_SET_RESULTS=@OLD_CHARACTER_SET_RESULTS */;
/*!40101 SET COLLATION_CONNECTION=@OLD_COLLATION_CONNECTION */;
/*!40111 SET SQL_NOTES=@OLD_SQL_NOTES */;
-- Dump completed on 2024-12-30 10:31:04