首页 > 其他分享 >TypeScript+Vue3+组合式API编码前端开发

TypeScript+Vue3+组合式API编码前端开发

时间:2024-09-03 10:52:47浏览次数:6  
标签:TypeScript name vue value API let Vue3 import ref

1.Vue3简介

  • ·2020年9月18日,Vue.js发布版3.0版本,代号:0ne Piece  (海贼王)
  • ·经历了:4800+次提交40+个RFC600+次PR300+贡献者
  • ·官方发版地址:Release v3.0.0 One Piece · vuejs/core
  •  

1.1. 【性能的提升】

  • 打包大小减少41%。
  • 初次渲染快55%, 更新渲染快133%。
  • 内存减少54%。

1.2.【 源码的升级】

  • 使用Proxy代替defineProperty实现响应式。
  • 重写虚拟DOM的实现和Tree-Shaking。

1.3. 【拥抱TypeScript】

  • Vue3可以更好的支持TypeScript。

1.4. 【新的特性】

1.Composition API(组合API):

  1. setup
  2.  ref与reactive
  3.  computed与watch…

2.新的内置组件:

  1.  Fragment
  2. Teleport
  3. Suspense…

3.其他改变:

  1. 新的生命周期钩子
  2. data 选项应始终被声明为一个函数
  3.  移除keyCode支持作为 v-on 的修饰符
     

2.创建Vue3项目

2.1.【基于vue-cli创建】- webpack

点击查看官方文档

备注:目前vue-cli已处于维护模式,官方推荐基于 Vite 创建项目。

 
  1.   ## 查看@vue/cli版本,确保@vue/cli版本在4.5.0以上
  2.   vue --version
  3.    
  4.   ## 安装或者升级你的@vue/cli
  5.   npm install -g @vue/cli
  6.    
  7.   ## 执行创建命令
  8.   vue create vue_test
  9.    
  10.   ## 随后选择3.x
  11.   ## Choose a version of Vue.js that you want to start the project with (Use arrow keys)
  12.   ## > 3.x
  13.   ## 2.x
  14.    
  15.   ## 启动
  16.   cd vue_test
  17.   npm run serve
 
 

 

2.2. 【基于 vite 创建】(推荐)

vite 是新一代前端构建工具,官网地址:https://vitejs.cn,vite的优势如下:

  • 轻量快速的热重载(HMR),能实现极速的服务启动。
  • 对 TypeScript、JSX、CSS 等支持开箱即用。
  • 真正的按需编译,不再等待整个应用编译完成。
  • webpack构建 与 vite构建对比图如下:

 
  1.   ## 1.创建命令
  2.   npm create vue@latest
  3.    
  4.   ## 2.具体配置
  5.   ## 配置项目名称
  6.   √ Project name: vue3_test
  7.   ## 是否添加TypeScript支持
  8.   √ Add TypeScript? Yes
  9.   ## 是否添加JSX支持
  10.   √ Add JSX Support? No
  11.   ## 是否添加路由环境
  12.   √ Add Vue Router for Single Page Application development? No
  13.   ## 是否添加pinia环境
  14.   √ Add Pinia for state management? No
  15.   ## 是否添加单元测试
  16.   √ Add Vitest for Unit Testing? No
  17.   ## 是否添加端到端测试方案
  18.   √ Add an End-to-End Testing Solution? » No
  19.   ## 是否添加ESLint语法检查
  20.   √ Add ESLint for code quality? Yes
  21.   ## 是否添加Prettiert代码格式化
  22.   √ Add Prettier for code formatting? No
 
 

安装官方推荐的vscode插件:

总结:

  • Vite 项目中,index.html 是项目的入口文件,在项目最外层。
  • 加载index.html后,Vite 解析 <script type="module" src="xxx"> 指向的JavaScript。
  • Vue3中是通过 createApp 函数创建一个应用实例。

注:开发中如想在Chrome中使用扩展程序:极简插件-vue

2.3. 【一个简单的效果】

Vue3向下兼容Vue2语法,且Vue3中的模板中可以没有根标签。

 
  1.   <template>
  2.   <div class="Preson">
  3.   <h2>姓名:{{name}}</h2>
  4.   <h2>年龄:{{age}}</h2>
  5.   <button @click="chanageName">修改名字</button>
  6.   <button @click="chanageAge">修改年龄</button>
  7.   <button @click="showTel">查看联系方式</button>
  8.   </div>
  9.   </template>
  10.    
  11.   <script lang="ts">
  12.   export default {
  13.   name: "Preson",
  14.   data() {
  15.   return {
  16.   name: "张三",
  17.   age: 18,
  18.   te1: "13888888888"
  19.   };
  20.   },
  21.   methods: {
  22.   chanageName() {
  23.   this.name = "zhaotongtong";
  24.   },
  25.   chanageAge() {
  26.   this.age += 1
  27.   },
  28.   showTel() {
  29.   alert(this.te1);
  30.   }
  31.   }
  32.   };
  33.   </script>
  34.    
  35.   <style scoped>
  36.   .Preson {
  37.   background-color: skyblue;
  38.   box-shadow: 00 10px;
  39.   border-radius: 10px;
  40.   padding: 20px;
  41.   }
  42.    
  43.   button {
  44.   margin: 0 5px;
  45.   }
  46.   </style>
 
 

3. Vue3核心语法

   3.1. 【OptionsAPI 与 CompositionAPI】

  •    Vue2的API设计是Options(配置)风格的。
  •    Vue3的API设计是Composition(组合)风格的。

Options API 的弊端:

       Options类型的 API,数据、方法、计算属性等,是分散在:data、methods、computed中的,若想新增或者修改一个需求,就需要分别修改:data、methods、computed,不便于维护和复用。

Composition API 的优势:

可以用函数的方式,更加优雅的组织代码,让相关功能的代码更加有序的组织在一起。

3.2. 【拉开序幕的 setup】

setup 概述

setup是Vue3中一个新的配置项,值是一个函数,它是 Composition API “表演的舞台”,组件中所用到的:数据、方法、计算属性、监视…等等,均配置在setup中。

特点如下:

  • setup函数返回的对象中的内容,可直接在模板中使用。
  • setup中访问this是undefined。
  • setup函数会在beforeCreate之前调用,它是“领先”所有钩子执行的。
 
  1.   <template>
  2.   <div class="Preson">
  3.   <h2>姓名:{{name}}</h2>
  4.   <h2>年龄:{{age}}</h2>
  5.   <button @click="chanageName">修改名字</button>
  6.   <button @click="chanageAge">修改年龄</button>
  7.   <button @click="showte1">查看联系方式</button>
  8.   </div>
  9.   </template>
  10.    
  11.   <script lang="ts">
  12.   export default {
  13.   name: "Preson",
  14.   setup() {
  15.   // 数据,原来写在data中(注意:此时的name、age、te1数据都不是响应式数据)
  16.   // console.log('@',this)//setup函数中的this是undefined
  17.   let name = "张三"; //注意此时的name不是响应式的
  18.   let age = 18; //注意此时的age不是响应式的
  19.   let te1 = "13888888888"; //注意此时的te1不是响应式的
  20.    
  21.   //方法,原来写在methods中
  22.   function chanageName() {
  23.   name = "zhaotongtong"; //注意:此时这么修改name页面是不变化的
  24.   }
  25.   function chanageAge() {
  26.   age += 1; //注意:此时这么修改name页面是不变化的
  27.   }
  28.   function showte1() {
  29.   alert(te1);
  30.   }
  31.   return { name, age, te1, chanageName, chanageAge, showte1 };
  32.   }
  33.   };
  34.   </script>
  35.    
  36.   <style scoped>
  37.   .Preson {
  38.   background-color: skyblue;
  39.   box-shadow: 0 0 10px;
  40.   border-radius: 10px;
  41.   padding: 20px;
  42.   }
  43.    
  44.   button {
  45.   margin: 0 5px;
  46.   }
  47.   </style>
 
 

setup 的返回值

  • 若返回一个对象:则对象中的:属性、方法等,在模板中均可以直接使用**(重点关注)。**
  • 若返回一个函数:则可以自定义渲染内容,代码如下:
 
  1.   setup(){
  2.   return ()=> '你好啊!'
  3.   }
 
 

setup 与 Options API 的关系

  • Vue2 的配置(datamethos…)中可以访问到 setup中的属性、方法。
  • 但在setup不能访问到Vue2的配置(datamethos…)。
  • 如果与Vue2冲突,则setup优先。

setup 语法糖

setup函数有一个语法糖,这个语法糖,可以让我们把setup独立出去,代码如下:

 
  1.   <template>
  2.   <div class="Preson">
  3.   <h2>姓名:{{name}}</h2>
  4.   <h2>年龄:{{age}}</h2>
  5.   <button @click="chanageName">修改名字</button>
  6.   <button @click="chanageAge">修改年龄</button>
  7.   <button @click="showte1">查看联系方式</button>
  8.   </div>
  9.   </template>
  10.    
  11.   <script lang="ts">
  12.   export default {
  13.   name: "Preson"
  14.   };
  15.   </script>
  16.    
  17.   <!-- 下面的写法是setup语法糖 -->
  18.   <script lang="ts" setup>
  19.   // 数据,原来写在data中(注意:此时的name、age、te1数据都不是响应式数据)
  20.   // console.log('@',this)//setup函数中的this是undefined
  21.   let name = "张三"; //注意此时的name不是响应式的
  22.   let age = 18; //注意此时的age不是响应式的
  23.   let te1 = "13888888888"; //注意此时的te1不是响应式的
  24.    
  25.   //方法,原来写在methods中
  26.   function chanageName() {
  27.   name = "zhaotongtong"; //注意:此时这么修改name页面是不变化的
  28.   }
  29.   function chanageAge() {
  30.   age += 1; //注意:此时这么修改name页面是不变化的
  31.   }
  32.   function showte1() {
  33.   alert(te1);
  34.   }
  35.   </script>
  36.    
  37.   <style scoped>
  38.   .Preson {
  39.   background-color: skyblue;
  40.   box-shadow: 0 0 10px;
  41.   border-radius: 10px;
  42.   padding: 20px;
  43.   }
  44.    
  45.   button {
  46.   margin: 0 5px;
  47.   }
  48.   </style>
 
 

扩展:上述代码,还需要编写一个不写setupscript标签,去指定组件名字,比较麻烦,我们可以借助vite中的插件简化

  1. 第一步:npm i vite-plugin-vue-setup-extend -D
  2. 第二步:进入到vite.config.ts中引入配置
 
  1.   import { defineConfig } from 'vite'
  2.   import VueSetupExtend from 'vite-plugin-vue-setup-extend'
  3.    
  4.   export default defineConfig({
  5.   plugins: [ VueSetupExtend() ]
  6.   })
 
 

    3.第三步:<script setup lang="ts" name="Person">

 

3.3. 【ref 创建:基本类型的响应式数据】

  • **作用:**定义响应式变量。
  • 语法:let xxx = ref(初始值)。
  • **返回值:**一个RefImpl的实例对象,简称ref对象或ref,ref对象的value属性是响应式的。
  • 注意点:
  1. JS中操作数据需要:xxx.value,但模板中不需要.value,直接使用即可。
  2. 对于let name = ref('zhaotongtong')来说,name不是响应式的,name.value是响应式的。
 
  1.   <template>
  2.   <div class="Preson">
  3.   <h2>姓名:{{name}}</h2>
  4.   <h2>年龄:{{age}}</h2>
  5.   <button @click="chanageName">修改名字</button>
  6.   <button @click="chanageAge">修改年龄</button>
  7.   <button @click="showte1">查看联系方式</button>
  8.   </div>
  9.   </template>
  10.    
  11.   <script lang="ts">
  12.   export default {
  13.   name: "Preson"
  14.   };
  15.   </script>
  16.    
  17.   <!-- 下面的写法是setup语法糖 -->
  18.   <script lang="ts" setup>
  19.   import { ref } from "vue";
  20.    
  21.   // 数据
  22.   // console.log('@',this)//setup函数中的this是undefined
  23.   // name和age是一个RefImpl的实例对象,简称ref对象,它们的value属性是响应式的。
  24.   let name = ref("张三");
  25.   let age = ref(18);
  26.   let te1 = "13888888888";
  27.    
  28.   //方法,原来写在methods中
  29.   function chanageName() {
  30.   name.value = "zhaotongtong"; // JS中操作ref对象时候需要.value
  31.    
  32.   // 注意:name不是响应式的,name.value是响应式的,所以如下代码并不会引起页面的更新。
  33.   // name = ref('zhang-san')
  34.   }
  35.   function chanageAge() {
  36.   age.value += 1; // JS中操作ref对象时候需要.value
  37.   }
  38.   function showte1() {
  39.   alert(te1);
  40.   }
  41.   </script>
  42.    
  43.   <style scoped>
  44.   .Preson {
  45.   background-color: skyblue;
  46.   box-shadow: 0 0 10px;
  47.   border-radius: 10px;
  48.   padding: 20px;
  49.   }
  50.    
  51.   button {
  52.   margin: 0 5px;
  53.   }
  54.   </style>
 
 

3.4. 【reactive 创建:对象类型的响应式数据】

  • 作用:定义一个响应式对象(基本类型不要用它,要用ref,否则报错)
  • 语法:let 响应式对象= reactive(源对象)。
  • **返回值:**一个Proxy的实例对象,简称:响应式对象。
  • 注意点:reactive定义的响应式数据是“深层次”的。
 
  1.   <template>
  2.   <div class="Preson">
  3.   <h2>汽车信息:一辆{{ car.brand }}汽车,价值{{ car.price }}万元</h2>
  4.   <button @click="changePrice">修改汽车售价</button>
  5.   <br />
  6.   <br />
  7.   <h2>游戏列表:</h2>
  8.   <ul>
  9.   <li v-for="(item,index) in games" :key="index">{{ item.name }}</li>
  10.   </ul>
  11.   <button @click="changegames">修改游戏</button>
  12.    
  13.   <br />
  14.   <h2>测试:{{ obj.a.b.c }}</h2>
  15.   <button @click="changeObj">修改游戏</button>
  16.   </div>
  17.   </template>
  18.    
  19.   <script lang="ts">
  20.   export default {
  21.   name: "Preson"
  22.   };
  23.   </script>
  24.    
  25.   <!-- 下面的写法是setup语法糖 -->
  26.   <script lang="ts" setup>
  27.   import { ref, reactive } from "vue";
  28.    
  29.   // 数据
  30.    
  31.   // 初识使用reactive
  32.   let car = reactive({
  33.   brand: "宝马",
  34.   price: 666
  35.   });
  36.    
  37.   // 修改数组数据
  38.   let games = reactive([
  39.   {
  40.   name: "穿越火线"
  41.   },
  42.   {
  43.   name: "元神"
  44.   },
  45.   {
  46.   name: "三国志"
  47.   }
  48.   ]);
  49.    
  50.   //多层次对象
  51.   let obj = reactive({
  52.   a: {
  53.   b: {
  54.   c: "喜羊羊"
  55.   }
  56.   }
  57.   });
  58.    
  59.   //方法
  60.   function changePrice() {
  61.   car.price += 888;
  62.   }
  63.    
  64.   function changegames() {
  65.   games[1].name = "刺激战场";
  66.   }
  67.    
  68.   function changeObj() {
  69.   obj.a.b.c = obj.a.b.c == "喜羊羊" ? "灰太狼" : "喜羊羊";
  70.   }
  71.   </script>
  72.    
  73.   <style scoped>
  74.   .Preson {
  75.   background-color: skyblue;
  76.   box-shadow: 0 0 10px;
  77.   border-radius: 10px;
  78.   padding: 20px;
  79.   }
  80.    
  81.   button {
  82.   margin: 0 5px;
  83.   }
  84.   </style>
 
 

 

3.5. 【ref 创建:对象类型的响应式数据】

  • 其实ref接收的数据可以是:基本类型对象类型
  • ref接收的是对象类型,内部其实也是调用了reactive函数。
 
  1.   <template>
  2.   <div class="Preson">
  3.   <h2>汽车信息:一辆{{ car.brand }}汽车,价值{{ car.price }}万元</h2>
  4.   <button @click="changePrice">修改汽车售价</button>
  5.   <br />
  6.   <br />
  7.   <h2>游戏列表:</h2>
  8.   <ul>
  9.   <li v-for="(item,index) in games" :key="index">{{ item.name }}</li>
  10.   </ul>
  11.   <button @click="changegames">修改游戏</button>
  12.   </div>
  13.   </template>
  14.    
  15.   <script lang="ts">
  16.   export default {
  17.   name: "Preson"
  18.   };
  19.   </script>
  20.    
  21.   <!-- 下面的写法是setup语法糖 -->
  22.   <script lang="ts" setup>
  23.   import { ref } from "vue";
  24.    
  25.   // 数据
  26.    
  27.   // 初识使用reactive
  28.   let car = ref({
  29.   brand: "宝马",
  30.   price: 666
  31.   });
  32.    
  33.   // 修改数组数据
  34.   let games = ref([
  35.   {
  36.   name: "穿越火线"
  37.   },
  38.   {
  39.   name: "元神"
  40.   },
  41.   {
  42.   name: "三国志"
  43.   }
  44.   ]);
  45.    
  46.   //方法
  47.   function changePrice() {
  48.   car.value.price += 888;
  49.   }
  50.    
  51.   function changegames() {
  52.   games.value[1].name = "刺激战场";
  53.   }
  54.   </script>
  55.    
  56.   <style scoped>
  57.   .Preson {
  58.   background-color: skyblue;
  59.   box-shadow: 0 0 10px;
  60.   border-radius: 10px;
  61.   padding: 20px;
  62.   }
  63.    
  64.   button {
  65.   margin: 0 5px;
  66.   }
  67.   </style>
 
 

3.6. 【ref 对比 reactive】

宏观角度看:

  1. ref用来定义:基本类型数据对象类型数据
  2. reactive用来定义:对象类型数据
  • 区别:
  1. ref创建的变量必须使用.value(可以使用volar插件自动添加.value)。

  2. reactive重新分配一个新对象,会失去响应式(可以使用Object.assign去整体替换)。

  • 使用原则:
  1. 若需要一个基本类型的响应式数据,必须使用ref
  2. 若需要一个响应式对象,层级不深,refreactive都可以。
  3. 若需要一个响应式对象,且层级较深,推荐使用reactive

3.7. 【toRefs 与 toRef】

  • 作用:将一个响应式对象中的每一个属性,转换为ref对象。
  • 备注:toRefstoRef功能一致,但toRefs可以批量转换。
  • 语法如下:
 
  1.   <template>
  2.   <div class="Preson">
  3.   <h2>姓名:{{ name }}</h2>
  4.   <h2>年龄:{{ age }},toRef_age:{{ age_big }}</h2>
  5.   <button @click="changeName">修改名字</button>
  6.   <button @click="changeAge">修改年龄</button>
  7.   </div>
  8.   </template>
  9.   <script lang="ts">
  10.   export default {
  11.   name: "Preson",
  12.   };
  13.   </script>
  14.   <!-- 下面的写法是setup语法糖 -->
  15.   <script lang="ts" setup>
  16.   import { reactive, toRefs, toRef } from "vue";
  17.   // 数据
  18.   let person = reactive({
  19.   name: "zhaotongtong",
  20.   age: 18,
  21.   });
  22.    
  23.   // 通过toRefs将person对象中的n个属性批量取出,且依然保持响应式的能力
  24.   let { name, age } = toRefs(person);
  25.   // 通过toRef将person对象中的age属性取出,且依然保持响应式的能力
  26.   let age_big = toRef(person, "age");
  27.    
  28.   //方法
  29.   //点击修改名字
  30.   function changeName() {
  31.   name.value += "~";
  32.   }
  33.    
  34.   // 点击修改年龄
  35.   function changeAge() {
  36.   age.value += 1;
  37.   age_big.value += 1;
  38.   }
  39.   </script>
  40.   <style scoped>
  41.   .Preson {
  42.   background-color: skyblue;
  43.   box-shadow: 0 0 10px;
  44.   border-radius: 10px;
  45.   padding: 20px;
  46.   }
  47.    
  48.   button {
  49.   margin: 0 5px;
  50.   }
  51.   </style>
 
 

3.8. 【computed】

作用:根据已有数据计算出新数据(和Vue2中的computed作用一致)。

 
  1.   <template>
  2.   <div class="Preson">
  3.   <h2>姓:<input type="text" v-model="firstName" /></h2>
  4.   <h2>名:<input type="text" v-model="lastName" /></h2>
  5.   <button @click="changeFullName">将全名改为li-si</button>
  6.   <h1>全名:{{ fullName }}</h1>
  7.   </div>
  8.   </template>
  9.   <script lang="ts">
  10.   export default {
  11.   name: "Preson",
  12.   };
  13.   </script>
  14.   <!-- 下面的写法是setup语法糖 -->
  15.   <script lang="ts" setup>
  16.   import { ref, computed } from "vue";
  17.   let firstName = ref("张");
  18.   let lastName = ref("三");
  19.    
  20.   // 这么定义的fullName是一入计算属性,且是只读的
  21.   // let fullName = computed(() => {
  22.   // return firstName.value + lastName.value;
  23.   // });
  24.    
  25.   // 这么定义的fullName是一入计算属性,且可读可写的
  26.   let fullName = computed({
  27.   get() {
  28.   return firstName.value + lastName.value;
  29.   },
  30.   set(val) {
  31.   let [str1, str2] = val.split("-");
  32.   firstName.value = str1;
  33.   lastName.value = str2;
  34.   console.log(val);
  35.   },
  36.   });
  37.    
  38.   function changeFullName() {
  39.   fullName.value = "li-si";
  40.   }
  41.   </script>
  42.    
  43.   <style scoped>
  44.   .Preson {
  45.   background-color: skyblue;
  46.   box-shadow: 0 0 10px;
  47.   border-radius: 10px;
  48.   padding: 20px;
  49.   }
  50.   input {
  51.   width: 320px;
  52.   height: 42px;
  53.   border: none;
  54.   font-size: 19px;
  55.   }
  56.    
  57.   button {
  58.   margin: 0 5px;
  59.   }
  60.   </style>
 
 

 

3.9.【watch】

  • 作用:监视数据的变化(和Vue2中的watch作用一致)
  • 特点:Vue3中的watch只能监视以下四种数据
  1. ref定义的数据。
  2. reactive定义的数据。
  3. 函数返回一个值(getter函数)。
  4. 一个包含上述内容的数组。

我们在Vue3中使用watch的时候,通常会遇到以下几种情况:

*情况一

监视ref定义的【基本类型】数据:直接写数据名即可,监视的是其value值的改变。

 
  1.   <template>
  2.   <div class="Preson">
  3.   <h1>情况一:监视【ref】定义的【基本类型】数据</h1>
  4.   <h2>当前求和为:{{ sum }}</h2>
  5.   <button @click="changeSum">点我sum加1</button>
  6.   </div>
  7.   </template>
  8.   <script lang="ts">
  9.   export default {
  10.   name: "Preson",
  11.   };
  12.   </script>
  13.   <!-- 下面的写法是setup语法糖 -->
  14.   <script lang="ts" setup>
  15.   import { ref, watch } from "vue";
  16.    
  17.   //数据
  18.   let sum = ref(0);
  19.    
  20.   //方法
  21.   function changeSum() {
  22.   sum.value += 1;
  23.   }
  24.    
  25.   //监视
  26.   // 情况一:监视【ref】定义的【基本类型】数据
  27.   let stopWatch = watch(sum, (newVal, oldVal) => {
  28.   console.log(newVal, oldVal);
  29.   if (newVal >= 10) {
  30.   //当前监视的newVal值大于等于10时,停止进行sum的监视
  31.   stopWatch();
  32.   }
  33.   });
  34.   </script>
  35.    
  36.   <style scoped>
  37.   .Preson {
  38.   background-color: skyblue;
  39.   box-shadow: 0 0 10px;
  40.   border-radius: 10px;
  41.   padding: 20px;
  42.   }
  43.   </style>
 
 

*情况二

监视ref定义的【对象类型】数据:直接写数据名,监视的是对象的【地址值】,若想监视对象内部的数据,要手动开启深度监视。

注意:

  • 若修改的是ref定义的对象中的属性,newValue 和 oldValue 都是新值,因为它们是同一个对象。

  • 若修改整个ref定义的对象,newValue 是新值, oldValue 是旧值,因为不是同一个对象了。

 
  1.   <template>
  2.   <div class="preson">
  3.   <h1>情况二:监视【ref】定义的【对象类型】数据</h1>
  4.   <h2>姓名:{{ preson.name }}</h2>
  5.   <h2>年龄:{{ preson.age }}</h2>
  6.   <button @click="changeName">修改名字</button>
  7.   <button @click="changeAge">修改年龄</button>
  8.   <button @click="changePerson">修改全部信息</button>
  9.   </div>
  10.   </template>
  11.   <script lang="ts">
  12.   export default {
  13.   name: "preson",
  14.   };
  15.   </script>
  16.   <!-- 下面的写法是setup语法糖 -->
  17.   <script lang="ts" setup>
  18.   import { ref, watch } from "vue";
  19.    
  20.   //数据
  21.   let preson = ref({
  22.   name: "zhaotong",
  23.   age: 18,
  24.   });
  25.    
  26.   //方法
  27.   function changeName() {
  28.   preson.value.name += "~";
  29.   }
  30.   function changeAge() {
  31.   preson.value.age += 1;
  32.   }
  33.   function changePerson() {
  34.   preson.value = {
  35.   name: "zhaotongtong",
  36.   age: 24,
  37.   };
  38.   }
  39.   //监视
  40.   // 情况二:监视【ref】定义的【对象类型】数据,监视的是对象的地址值,若想监视对象内部属性的变化需要手动开启深度监视
  41.   // deep:true 深度监听
  42.   // immediate:true 初始立即监听
  43.    
  44.   //watch的第一个参数是:被监视的数据
  45.   //watch的第二个参数是:监视的回调
  46.   // watchi的第三个参数是:配置对象(deep、immediate等)
  47.   watch(
  48.   preson,
  49.   (newVal, oldVal) => {
  50.   console.log(newVal, oldVal);
  51.   },
  52.   { deep: true, immediate: true }
  53.   );
  54.   </script>
  55.    
  56.   <style scoped>
  57.   .preson {
  58.   background-color: skyblue;
  59.   box-shadow: 0 0 10px;
  60.   border-radius: 10px;
  61.   padding: 20px;
  62.   }
  63.    
  64.   button {
  65.   margin: 0px 12px;
  66.   }
  67.   </style>
 
 

*情况三

监视reactive定义的【对象类型】数据,且默认开启了深度监视(不可关闭深度监听)。

 
  1.   <template>
  2.   <div class="preson">
  3.   <h1>情况三:监视reactive定义的【对象类型】数据,且默认开启了深度监视。</h1>
  4.   <h2>姓名:{{ preson.name }}</h2>
  5.   <h2>年龄:{{ preson.age }}</h2>
  6.   <button @click="changeName">修改名字</button>
  7.   <button @click="changeAge">修改年龄</button>
  8.   <button @click="changePerson">修改全部信息</button>
  9.   </div>
  10.   </template>
  11.   <script lang="ts">
  12.   export default {
  13.   name: "preson",
  14.   };
  15.   </script>
  16.   <!-- 下面的写法是setup语法糖 -->
  17.   <script lang="ts" setup>
  18.   import { reactive, watch } from "vue";
  19.    
  20.   //数据
  21.   let preson = reactive({
  22.   name: "zhaotong",
  23.   age: 18,
  24.   });
  25.    
  26.   //方法
  27.   function changeName() {
  28.   preson.name += "~";
  29.   }
  30.   function changeAge() {
  31.   preson.age += 1;
  32.   }
  33.   function changePerson() {
  34.   preson = Object.assign(preson, {
  35.   name: "zhaotongtong",
  36.   age: 24,
  37.   });
  38.   }
  39.   //监视
  40.   // 情况三:监视reactive定义的【对象类型】数据,且默认开启了深度监视。
  41.   watch(preson, (newVal, oldVal) => {
  42.   console.log(newVal, oldVal);
  43.   });
  44.   </script>
  45.    
  46.   <style scoped>
  47.   .preson {
  48.   background-color: skyblue;
  49.   box-shadow: 0 0 10px;
  50.   border-radius: 10px;
  51.   padding: 20px;
  52.   }
  53.    
  54.   button {
  55.   margin: 0px 12px;
  56.   }
  57.   </style>
 
 

*情况四

监视ref或reactive定义的【对象类型】数据中的某个属性,注意点如下:

  1. 若该属性值不是【对象类型】,需要写成函数形式。
  2. 若该属性值是依然是【对象类型】,可直接编,也可写成函数,建议写成函数。

结论:监视的要是对象里的属性,那么最好写函数式,注意点:若是对象监视的是地址值,需要关注对象内部,需要手动开启深度监视。

 
  1.   <template>
  2.   <div class="preson">
  3.   <h1>情况四:监视【ref】或【reactive】定义的【对象类型】数据中的某个属性</h1>
  4.   <h2>姓名:{{ person.name }}</h2>
  5.   <h2>年龄:{{ person.age }}</h2>
  6.   <h2>汽车:{{ person.car.c1 }}、{{ person.car.c2 }}</h2>
  7.   <button @click="changeName">修改名字</button>
  8.   <button @click="changeAge">修改年龄</button>
  9.   <button @click="changeC1">修改第一台车</button>
  10.   <button @click="changeC2">修改第二台车</button>
  11.   <button @click="changeCar">修改全部车</button>
  12.   </div>
  13.   </template>
  14.   <script lang="ts">
  15.   export default {
  16.   name: "preson",
  17.   };
  18.   </script>
  19.   <!-- 下面的写法是setup语法糖 -->
  20.   <script lang="ts" setup>
  21.   import { reactive, watch } from "vue";
  22.    
  23.   //数据
  24.   let person = reactive({
  25.   name: "zhaotong",
  26.   age: 18,
  27.   car: {
  28.   c1: "帕萨特",
  29.   c2: "途岳",
  30.   },
  31.   });
  32.    
  33.   //方法
  34.   function changeName() {
  35.   person.name += "~";
  36.   }
  37.   function changeAge() {
  38.   person.age += 1;
  39.   }
  40.   function changeC1() {
  41.   person.car.c1 = "奔驰";
  42.   }
  43.   function changeC2() {
  44.   person.car.c2 = "宝马";
  45.   }
  46.   function changeCar() {
  47.   person.car = { c1: "自行车", c2: "电动车" };
  48.   }
  49.    
  50.   //监视
  51.   // 情况四:监视响应式对象中的某个属性,且该属性时基本类型的,要写成函数式
  52.   // watch(
  53.   // () => person.name,
  54.   // (newVal, oldVal) => {
  55.   // console.log(newVal, oldVal);
  56.   // }
  57.   // );
  58.    
  59.   // 情况四:监视响应式对象中的某个属性,且该属性是对象类型的,可以直接写,也能写函数,更推荐写函数
  60.   watch(
  61.   () => person.car,
  62.   (newVal, oldVal) => {
  63.   console.log(newVal, oldVal);
  64.   },
  65.   {
  66.   deep: true,
  67.   }
  68.   );
  69.   </script>
  70.    
  71.   <style scoped>
  72.   .preson {
  73.   background-color: skyblue;
  74.   box-shadow: 0 0 10px;
  75.   border-radius: 10px;
  76.   padding: 20px;
  77.   }
  78.    
  79.   button {
  80.   margin: 0px 12px;
  81.   }
  82.   </style>
 
 

 

*情况五

监视上述的多个数据

 
  1.   <template>
  2.   <div class="preson">
  3.   <h1>情况五:监视上述的多个数据</h1>
  4.   <h2>姓名:{{ person.name }}</h2>
  5.   <h2>年龄:{{ person.age }}</h2>
  6.   <h2>汽车:{{ person.car.c1 }}、{{ person.car.c2 }}</h2>
  7.   <button @click="changeName">修改名字</button>
  8.   <button @click="changeAge">修改年龄</button>
  9.   <button @click="changeC1">修改第一台车</button>
  10.   <button @click="changeC2">修改第二台车</button>
  11.   <button @click="changeCar">修改全部车</button>
  12.   </div>
  13.   </template>
  14.   <script lang="ts">
  15.   export default {
  16.   name: "preson",
  17.   };
  18.   </script>
  19.   <!-- 下面的写法是setup语法糖 -->
  20.   <script lang="ts" setup>
  21.   import { reactive, watch } from "vue";
  22.    
  23.   //数据
  24.   let person = reactive({
  25.   name: "zhaotong",
  26.   age: 18,
  27.   car: {
  28.   c1: "帕萨特",
  29.   c2: "途岳",
  30.   },
  31.   });
  32.    
  33.   //方法
  34.   function changeName() {
  35.   person.name += "~";
  36.   }
  37.   function changeAge() {
  38.   person.age += 1;
  39.   }
  40.   function changeC1() {
  41.   person.car.c1 = "奔驰";
  42.   }
  43.   function changeC2() {
  44.   person.car.c2 = "宝马";
  45.   }
  46.   function changeCar() {
  47.   person.car = { c1: "自行车", c2: "电动车" };
  48.   }
  49.    
  50.   //监视
  51.   //情况五:监视上述的多个数据
  52.   watch(
  53.   [() => person.name, () => person.car],
  54.   (newVal, oldVal) => {
  55.   console.log(newVal, oldVal);
  56.   },
  57.   {
  58.   deep: true,
  59.   }
  60.   );
  61.   </script>
  62.    
  63.   <style scoped>
  64.   .preson {
  65.   background-color: skyblue;
  66.   box-shadow: 0 0 10px;
  67.   border-radius: 10px;
  68.   padding: 20px;
  69.   }
  70.    
  71.   button {
  72.   margin: 0px 12px;
  73.   }
  74.   </style>
 
 

 

3.10. 【watchEffect】

  • 官网:立即运行一个函数,同时响应式地追踪其依赖,并在依赖更改时重新执行该函数。

  • watch对比watchEffect

  1. 都能监听响应式数据的变化,不同的是监听数据变化的方式不同

  2. watch:要明确指出监视的数据

  3. watchEffect:不用明确指出监视的数据(函数中用到哪些属性, 那就监视哪些属性)。

  • 示例代码:
 
  1.   <template>
  2.   <div class="preson">
  3.   <h2>需求:当水温达到60度,或水位达到80cm时,给服务器发请求</h2>
  4.   <h2>当前水温:{{ temp }}℃</h2>
  5.   <h2>当前水位:{{ height }}cm</h2>
  6.   <button @click="changeTemp">水温加十</button>
  7.   <button @click="changeHeiht">水温加十</button>
  8.   </div>
  9.   </template>
  10.   <script lang="ts">
  11.   export default {
  12.   name: "preson",
  13.   };
  14.   </script>
  15.   <!-- 下面的写法是setup语法糖 -->
  16.   <script lang="ts" setup>
  17.   import { ref, watch, watchEffect } from "vue";
  18.    
  19.   //数据
  20.   let temp = ref(10);
  21.   let height = ref(0);
  22.    
  23.   //方法
  24.   function changeTemp() {
  25.   temp.value += 10;
  26.   }
  27.   function changeHeiht() {
  28.   height.value += 10;
  29.   }
  30.    
  31.   //监视
  32.    
  33.   // watch监视该需求 当水温达到60度,或水位达到80cm时,给服务器发请求
  34.   // watch([temp, height], (value) => {
  35.   // let [newTemp, newHeight] = value;
  36.   // if (newTemp >= 60 || newHeight >= 80) {
  37.   // console.log("给服务器发送请求");
  38.   // }
  39.   // });
  40.    
  41.   // watchEffect监视该需求 当水温达到60度,或水位达到80cm时,给服务器发请求
  42.   watchEffect(() => {
  43.   if (temp.value >= 60 || height.value >= 80) {
  44.   console.log("给服务器发送请求");
  45.   }
  46.   });
  47.   </script>
  48.    
  49.   <style scoped>
  50.   .preson {
  51.   background-color: skyblue;
  52.   box-shadow: 0 0 10px;
  53.   border-radius: 10px;
  54.   padding: 20px;
  55.   }
  56.    
  57.   button {
  58.   margin: 0px 12px;
  59.   }
  60.   </style>
 
 

 

3.11. 【标签的 ref 属性】

作用:用于注册模板引用。

  • 用在普通DOM标签上,获取的是DOM节点。

  • 用在组件标签上,获取的是组件实例对象。

  1. 用在普通DOM标签上:
 
  1.   <template>
  2.   <div class="preson">
  3.   <h1 ref="title1">中国</h1>
  4.   <h2 ref="title2">北京</h2>
  5.   <h3 ref="title3">河北</h3>
  6.   <button @click="showLog">点我输出这两个元素</button>
  7.   </div>
  8.   </template>
  9.   <script lang="ts">
  10.   export default {
  11.   name: "preson",
  12.   };
  13.   </script>
  14.   <!-- 下面的写法是setup语法糖 -->
  15.   <script lang="ts" setup>
  16.   import { ref, watch, watchEffect } from "vue";
  17.    
  18.   let title1 = ref();
  19.   let title2 = ref();
  20.   let title3 = ref();
  21.    
  22.   //方法
  23.   function showLog() {
  24.   console.log(title1.value);
  25.   console.log(title2.value);
  26.   console.log(title3.value);
  27.   }
  28.   </script>
  29.    
  30.   <style scoped>
  31.   .preson {
  32.   background-color: skyblue;
  33.   box-shadow: 0 0 10px;
  34.   border-radius: 10px;
  35.   padding: 20px;
  36.   }
  37.    
  38.   button {
  39.   margin: 0px 12px;
  40.   }
  41.   </style>
 
 
  1. 用在组件标签上:
 
  1.   子组件:
  2.    
  3.   <template>
  4.   <div class="preson">
  5.   <h1 ref="title1">中国</h1>
  6.   <h2 ref="title2">北京</h2>
  7.   <h3 ref="title3">河北</h3>
  8.   <button @click="showLog">点我输出这两个元素</button>
  9.   </div>
  10.   </template>
  11.   <script lang="ts">
  12.   export default {
  13.   name: "preson",
  14.   };
  15.   </script>
  16.   <!-- 下面的写法是setup语法糖 -->
  17.   <script lang="ts" setup>
  18.   import { ref, defineExpose } from "vue";
  19.    
  20.   //数据
  21.   let title1 = ref();
  22.   let title2 = ref();
  23.   let title3 = ref();
  24.   let name = ref("张三");
  25.   let age = ref(18);
  26.    
  27.   // 使用defineExpose将组件中的数据交给外部
  28.   defineExpose({ name, age,title1 });
  29.    
  30.   //方法
  31.   function showLog() {
  32.   console.log(title1.value);
  33.   console.log(title2.value);
  34.   console.log(title3.value);
  35.   }
  36.   </script>
  37.    
  38.   <style scoped>
  39.   .preson {
  40.   background-color: skyblue;
  41.   box-shadow: 0 0 10px;
  42.   border-radius: 10px;
  43.   padding: 20px;
  44.   }
  45.    
  46.   button {
  47.   margin: 0px 12px;
  48.   }
  49.   </style>
  50.    
  51.    
  52.    
  53.    
  54.    
  55.    
  56.   父组件:
  57.    
  58.   <template>
  59.   <Preson ref="ren"></Preson>
  60.   <button @click="test">测试</button>
  61.   </template>
  62.   <script lang="ts">
  63.   import Preson from "./components/Preson/index.vue";
  64.   export default {
  65.   name: "App",
  66.   components: { Preson },
  67.   };
  68.   </script>
  69.   <script lang="ts" setup>
  70.   import { ref } from "vue";
  71.    
  72.   let ren = ref();
  73.    
  74.   function test() {
  75.   console.log(ren.value.name);
  76.   console.log(ren.value.age);
  77.   console.log(ren.value.title1);
  78.   }
  79.   </script>
  80.   <style>
  81.   </style>
  82.    
 
 

 

3.12. 回顾TS中的接口泛型自定义类型

注:在 src 路径下创建 types/index.ts 进行接口泛型自定义类型的放置。

types/index.ts

 
  1.   // 定义一个接口,用于限制person对象的具体属性
  2.   export interface PersonInter {
  3.   id: number;
  4.   name: string;
  5.   age: number;
  6.   }
  7.    
  8.    
  9.   // 定义一个自定义类型Persons
  10.   // export type Persons = Array<PersonInter>
  11.   export type Persons PersonInter[]
 
 

index.ts(此处也就是使用接口泛型自定义类型的页面模块)

 
  1.   <template>
  2.   <div class="preson"></div>
  3.   </template>
  4.   <script lang="ts">
  5.   export default {
  6.   name: "preson",
  7.   };
  8.   </script>
  9.   <!-- 下面的写法是setup语法糖 -->
  10.   <script lang="ts" setup>
  11.   import { reactive, ref } from "vue";
  12.   import { type PersonInter, type Persons } from "@/types/index";
  13.    
  14.   //一个对象数据限制
  15.   // let person:PersonInter = reactive({
  16.   // id: 1,
  17.   // name: "zhaotongtong",
  18.   // age: 18,
  19.   // });
  20.    
  21.   // //对数组中多个对象进行限制
  22.   // let personList: Array<PersonInter> = reactive([
  23.   // {
  24.   // id: 1,
  25.   // name: "zhaotongtong",
  26.   // age: 18,
  27.   // },
  28.   // {
  29.   // id: 2,
  30.   // name: "zha",
  31.   // age: 28,
  32.   // },
  33.   // {
  34.   // id: 3,
  35.   // name: "lisi",
  36.   // age: 38,
  37.   // },
  38.   // ]);
  39.    
  40.   //自定义Persons 对数组中多个对象进行限制
  41.   let personList: Persons = reactive([
  42.   {
  43.   id: 1,
  44.   name: "zhaotongtong",
  45.   age: 18,
  46.   },
  47.   {
  48.   id: 2,
  49.   name: "zha",
  50.   age: 28,
  51.   },
  52.   {
  53.   id: 3,
  54.   name: "lisi",
  55.   age: 38,
  56.   },
  57.   ]);
  58.   </script>
  59.    
  60.   <style scoped>
  61.   .preson {
  62.   background-color: skyblue;
  63.   box-shadow: 0 0 10px;
  64.   border-radius: 10px;
  65.   padding: 20px;
  66.   }
  67.    
  68.   button {
  69.   margin: 0px 12px;
  70.   }
  71.   </style>
 
 

 

3.13. 【props】(父传子)

 
  1.   // 定义一个接口,用于限制person对象的具体属性
  2.   export interface PersonInter {
  3.   id: number;
  4.   name: string;
  5.   age: number;
  6.   }
  7.    
  8.    
  9.   // 定义一个自定义类型Persons
  10.   // export type Persons = Array<PersonInter>
  11.   export type Persons PersonInter[]
 
 

App.vue中代码(父组件):

 
  1.   <template>
  2.   <Preson :list="persons"></Preson>
  3.   </template>
  4.   <script lang="ts">
  5.   import Preson from "./components/Preson/index.vue";
  6.   export default {
  7.   name: "App",
  8.   components: { Preson },
  9.   };
  10.   </script>
  11.   <script lang="ts" setup>
  12.   import { reactive } from "vue";
  13.   import { type Persons } from "./types/index";
  14.    
  15.   let persons = reactive<Persons>([
  16.   { id: 0, name: "张三", age: 18 },
  17.   { id: 1, name: "李四", age: 19 },
  18.   { id: 2, name: "王五", age: 20 },
  19.   ]);
  20.   </script>
  21.   <style>
  22.   </style>
 
 

Preson.vue中的代码(子组件):

 
  1.   <template>
  2.   <div class="preson">
  3.   <ul>
  4.   <li v-for="item in list" :key="item.id">
  5.   姓名:{{ item.name }} / 年龄:{{ item.age }}
  6.   </li>
  7.   </ul>
  8.   </div>
  9.   </template>
  10.   <script lang="ts">
  11.   export default {
  12.   name: "preson",
  13.   };
  14.   </script>
  15.   <!-- 下面的写法是setup语法糖 -->
  16.   <script lang="ts" setup>
  17.   import { reactive, ref, defineProps, withDefaults } from "vue";
  18.   import { type Persons } from "../../types/index";
  19.    
  20.   //接收list
  21.   // defineProps(["list"]);
  22.    
  23.   //接收list+限制类型
  24.   // defineProps<{ list: Persons }>();
  25.    
  26.   //在父组件未传list参数时候出发 默认值
  27.   // list? 加 '?' 号 意思是可传可不传
  28.   //接收list+限制类型+限制必要性+指定默认值
  29.   withDefaults(defineProps<{ list?: Persons }>(), {
  30.   list: () => [{ id: 0, name: "哇哈哈", age: 6 }],
  31.   });
  32.    
  33.   //接收list,同时将list保存起来
  34.   // let names = defineProps(["list"]);
  35.   // console.log(names.list);
  36.   </script>
  37.    
  38.   <style scoped>
  39.   .preson {
  40.   background-color: skyblue;
  41.   box-shadow: 0 0 10px;
  42.   border-radius: 10px;
  43.   padding: 20px;
  44.   }
  45.    
  46.   button {
  47.   margin: 0px 12px;
  48.   }
  49.   </style>
 
 

 

 

3.14. 【生命周期】

  • 概念:Vue组件实例在创建时要经历一系列的初始化步骤,在此过程中Vue会在合适的时机,调用特定的函数,从而让开发者有机会在特定阶段运行自己的代码,这些特定的函数统称为:生命周期钩子

  • 规律:

生命周期整体分为四个阶段,分别是:创建、挂载、更新、销毁,每个阶段都有两个钩子,一前一后。

  • vue2生命周期

创建阶段:beforeCreatecreated

挂载阶段:beforeMountmounted

更新阶段:beforeUpdateupdated

销毁阶段:beforeDestroydestroyed

  • vue3生命周期

创建阶段:setup

挂载阶段:onBeforeMountonMounted

更新阶段:onBeforeUpdateonUpdated

卸载阶段:onBeforeUnmountonUnmounted

  • 常用的钩子:onMounted(挂载完毕)、onUpdated(更新完毕)、onBeforeUnmount(卸载之前)

  • vue2到vue3生命周期的改变:
 
  1.   beforeCreate -> use setup()
  2.    
  3.   created -> use setup()
  4.    
  5.   beforeMount -> onBeforeMount
  6.    
  7.   mounted -> onMounted
  8.    
  9.   beforeUpdate -> onBeforeUpdate
  10.    
  11.   updated -> onUpdated
  12.    
  13.   beforeUnmount -> onBeforeUnmount
  14.    
  15.   unmounted -> onUnmounted
  16.    
  17.   errorCaptured -> one rrorCaptured
  18.    
  19.   renderTracked -> onRenderTracked
  20.    
  21.   renderTriggered -> onRenderTriggered
  22.    
  23.   activated -> onActivated
  24.    
  25.   deactivated -> onDeactivated
 
 

vue2:


vue3

 

  • 示例代码:
 
  1.   <template>
  2.   <div class="preson">
  3.   <h2>当前求和为:{{ sum }}</h2>
  4.   <button @click="changeSum">点我sum+1</button>
  5.   </div>
  6.   </template>
  7.    
  8.   <!-- vue3写法 -->
  9.   <script lang="ts" setup name="Person">
  10.   import {
  11.   ref,
  12.   onBeforeMount,
  13.   onMounted,
  14.   onBeforeUpdate,
  15.   onUpdated,
  16.   onBeforeUnmount,
  17.   onUnmounted,
  18.   } from "vue";
  19.    
  20.   // 数据
  21.   let sum = ref(0);
  22.   // 方法
  23.   function changeSum() {
  24.   sum.value += 1;
  25.   }
  26.   //创建
  27.   console.log("setup",'创建');
  28.   // 生命周期钩子
  29.   onBeforeMount(() => {
  30.   console.log("挂载之前");
  31.   });
  32.   onMounted(() => {
  33.   console.log("挂载完毕");
  34.   });
  35.   onBeforeUpdate(() => {
  36.   console.log("更新之前");
  37.   });
  38.   onUpdated(() => {
  39.   console.log("更新完毕");
  40.   });
  41.   onBeforeUnmount(() => {
  42.   console.log("卸载之前");
  43.   });
  44.   onUnmounted(() => {
  45.   console.log("卸载完毕");
  46.   });
  47.   </script>
  48.    
  49.    
  50.   <style scoped>
  51.   .preson {
  52.   background-color: skyblue;
  53.   box-shadow: 0 0 10px;
  54.   border-radius: 10px;
  55.   padding: 20px;
  56.   }
  57.    
  58.   button {
  59.   margin: 0px 12px;
  60.   }
  61.   </style>
 
 

 

3.15. 【自定义hook】

  • 什么是hook?—— 本质是一个函数,把setup函数中使用的Composition API进行了封装,类似于vue2.x中的mixin

  • 自定义hook的优势:复用代码, 让setup中的逻辑更清楚易懂。

示例代码(src路径下创建hooks文件夹 hooks中创建useSum.ts与useDog.ts):

  • useSum.ts中内容如下:

 
  1.   import { ref, computed,onMounted} from "vue";
  2.    
  3.   export default function () {
  4.   //数据
  5.   let sum = ref(0);
  6.    
  7.   // 方法
  8.   function add() {
  9.   sum.value += 1;
  10.   }
  11.   //hooks中支持使用计算属性等
  12.   let bigSum = computed(() => {
  13.   return sum.value * 10;
  14.   });
  15.    
  16.   //hooks中支持使用钩子函数
  17.   onMounted(()=>{
  18.   add()
  19.   })
  20.    
  21.   // 向外部提供东西
  22.   return { sum, add, bigSum };
  23.   }
 
 
  • useDog.ts中内容如下:
 
  1.   import axios from "axios";
  2.   import { reactive, onMounted } from "vue";
  3.    
  4.   export default function () {
  5.   //数据
  6.   let dogList = reactive([
  7.   "https://images.dog.ceo/breeds/pembroke/n02113023_7243.jpg",
  8.   ]);
  9.   // 方法
  10.    
  11.   async function addDog() {
  12.   try {
  13.   let result = await axios.get(
  14.   "https://dog.ceo/api/breed/pembroke/images/random"
  15.   );
  16.    
  17.   dogList.push(result.data.message);
  18.   } catch (error) {
  19.   alert(error);
  20.   }
  21.   }
  22.    
  23.   //hooks中支持使用钩子函数
  24.   // 上来挂载完毕获取一个小狗照片
  25.   onMounted(() => {
  26.   addDog();
  27.   });
  28.    
  29.   // 向外部提供东西
  30.   return {
  31.   addDog,
  32.   dogList,
  33.   };
  34.   }
 
 
  • 组件中具体使用:
 
  1.   <template>
  2.   <div class="Preson">
  3.   <h2>当前求和为:{{ sum }},sum乘以十倍为:{{ bigSum }}</h2>
  4.    
  5.   <button @click="add">点我sum加一</button>
  6.   <hr />
  7.   <img v-for="(dog, index) in dogList" :key="index" :src="dog" alt="" />
  8.   <button @click="addDog">再来一只小狗</button>
  9.   </div>
  10.   </template>
  11.   <script lang="ts">
  12.   export default {
  13.   name: "Preson",
  14.   };
  15.   </script>
  16.   <script lang="ts" setup>
  17.   import useSum from "@/hooks/useSum";
  18.   import useDog from "@/hooks/useDog";
  19.    
  20.   const {sum,add,bigSum} = useSum();
  21.   const {dogList,addDog} = useDog();
  22.   </script>
  23.    
  24.   <style scoped>
  25.   .Preson {
  26.   background-color: skyblue;
  27.   box-shadow: 0 0 10px;
  28.   border-radius: 10px;
  29.   padding: 20px;
  30.   }
  31.    
  32.   img {
  33.   height: 100px;
  34.   margin-right: 6px;
  35.   }
  36.   </style>
 
 

 

4:路由

前述:【对路由的理解】

4.1:【路由基本切换效果】

  • Vue3中要使用vue-router的最新版本,目前是4版本。

  • 路由配置文件代码如下:

 
  1.   import {createRouter,createWebHistory} from 'vue-router'
  2.   import Home from '@/pages/Home.vue'
  3.   import News from '@/pages/News.vue'
  4.   import About from '@/pages/About.vue'
  5.    
  6.   const router = createRouter({
  7.   history:createWebHistory(),
  8.   routes:[
  9.   {
  10.   path:'/home',
  11.   component:Home
  12.   },
  13.   {
  14.   path:'/about',
  15.   component:About
  16.   }
  17.   ]
  18.   })
  19.   export default router
 
 
  • main.ts代码如下:

 
  1.   import router from './router/index'
  2.   app.use(router)
  3.    
  4.   app.mount('#app')
 
 
  • App.vue代码如下

 
  1.   <template>
  2.   <div class="app">
  3.   <h2 class="title">Vue路由测试</h2>
  4.   <!-- 导航区 -->
  5.   <div class="navigate">
  6.   <RouterLink to="/home" active-class="active">首页</RouterLink>
  7.   <RouterLink to="/news" active-class="active">新闻</RouterLink>
  8.   <RouterLink to="/about" active-class="active">关于</RouterLink>
  9.   </div>
  10.   <!-- 展示区 -->
  11.   <div class="main-content">
  12.   <RouterView></RouterView>
  13.   </div>
  14.   </div>
  15.   </template>
  16.    
  17.   <script lang="ts" setup name="App">
  18.   import {RouterLink,RouterView} from 'vue-router'
  19.   </script>
 
 

 

4.2. 【两个注意点】

  1. 路由组件通常存放在pages 或 views文件夹,一般组件通常存放在components文件夹。

  2. 通过点击导航,视觉效果上“消失” 了的路由组件,默认是被卸载掉的,需要的时候再去挂载

4.3.【路由器工作模式】

  • history模式

优点:URL更加美观,不带有#,更接近传统的网站URL

缺点:后期项目上线,需要服务端配合处理路径问题,否则刷新会有404错误。

 
  1.   const router = createRouter({
  2.   history:createWebHistory(), //history模式
  3.   /******/
  4.   })
 
 

 

  • hash模式

优点:兼容性更好,因为不需要服务器端处理路径。

缺点:URL带有#不太美观,且在SEO优化方面相对较差。

 
  1.   const router = createRouter({
  2.   history:createWebHashHistory(), //hash模式
  3.   /******/
  4.   })
 
 

 

 

4.4. 【to的两种写法】

 
  1.   <!-- 第一种:to的字符串写法 -->
  2.   <router-link active-class="active" to="/home">主页</router-link>
  3.    
  4.   <!-- 第二种:to的对象写法 -->
  5.   <router-link active-class="active" :to="{path:'/home'}">Home</router-link>
 
 

 

4.5. 【命名路由】

作用:可以简化路由跳转及传参。

给路由规则命名:

 
  1.   routes:[
  2.   {
  3.   name:'zhuye',
  4.   path:'/home',
  5.   component:Home
  6.   },
  7.   {
  8.   name:'xinwen',
  9.   path:'/news',
  10.   component:News,
  11.   },
  12.   {
  13.   name:'guanyu',
  14.   path:'/about',
  15.   component:About
  16.   }
  17.   ]
 
 

路由跳转:

 
  1.   <!--简化前:需要写完整的路径(to的字符串写法) -->
  2.   <router-link to="/news/detail">跳转</router-link>
  3.    
  4.   <!--简化后:直接通过名字跳转(to的对象写法配合name属性) -->
  5.   <router-link :to="{name:'guanyu'}">跳转</router-link>
 
 

 

4.6. 【嵌套路由】

  • 编写News的子路由:Detail.vue

  • 配置路由规则,使用children配置项:

 
  1.   const router = createRouter({
  2.   history:createWebHistory(),
  3.   routes:[
  4.   {
  5.   name:'zhuye',
  6.   path:'/home',
  7.   component:Home
  8.   },
  9.   {
  10.   name:'xinwen',
  11.   path:'/news',
  12.   component:News,
  13.   children:[
  14.   {
  15.   name:'xiang',
  16.   path:'detail',
  17.   component:Detail
  18.   }
  19.   ]
  20.   },
  21.   {
  22.   name:'guanyu',
  23.   path:'/about',
  24.   component:About
  25.   }
  26.   ]
  27.   })
  28.   export default router
 
 
  • 跳转路由(记得要加完整路径):

 
  1.   <router-link to="/news/detail">xxxx</router-link>
  2.   <!-- 或 -->
  3.   <router-link :to="{path:'/news/detail'}">xxxx</router-link>
 
 
  • 记得去Home组件中预留一个<router-view>

 
  1.   <template>
  2.   <div class="news">
  3.   <nav class="news-list">
  4.   <RouterLink v-for="news in newsList" :key="news.id" :to="{path:'/news/detail'}">
  5.   {{news.name}}
  6.   </RouterLink>
  7.   </nav>
  8.   <div class="news-detail">
  9.   <RouterView/>
  10.   </div>
  11.   </div>
  12.   </template>
 
 

 

4.7. 【路由传参】

query参数
  • 传递参数

 
  1.   <!-- 跳转并携带query参数(to的字符串写法) -->
  2.   <router-link to="/news/detail?a=1&b=2&content=欢迎你">
  3.   跳转
  4.   </router-link>
  5.    
  6.   <!-- 跳转并携带query参数(to的对象写法) -->
  7.   <RouterLink
  8.   :to="{
  9.   //name:'xiang', //用name也可以跳转
  10.   path:'/news/detail',
  11.   query:{
  12.   id:news.id,
  13.   title:news.title,
  14.   content:news.content
  15.   }
  16.   }"
  17.   >
  18.   {{news.title}}
  19.   </RouterLink>
 
 
  • 接收参数

 
  1.   import {useRoute} from 'vue-router'
  2.   const route = useRoute()
  3.   // 打印query参数
  4.   console.log(route.query)
 
 

 

params参数
  • 传递参数

 
  1.   <!-- 跳转并携带params参数(to的字符串写法) -->
  2.   <RouterLink :to="`/news/detail/001/新闻001/内容001`">{{news.title}}</RouterLink>
  3.    
  4.   <!-- 跳转并携带params参数(to的对象写法) -->
  5.   <RouterLink
  6.   :to="{
  7.   name:'xiang', //用name跳转
  8.   params:{
  9.   id:news.id,
  10.   title:news.title,
  11.   content:news.title
  12.   }
  13.   }"
  14.   >
  15.   {{news.title}}
  16.   </RouterLink>
 
 
  • 接收参数

 
  1.   import {useRoute} from 'vue-router'
  2.   const route = useRoute()
  3.   // 打印params参数
  4.   console.log(route.params)
 
 

备注1:传递params参数时,若使用to的对象写法,必须使用name配置项,不能用path

备注2:传递params参数时,需要提前在规则中占位。

4.8. 【路由的props配置】

作用:让路由组件更方便的收到参数(可以将路由参数作为props传给组件)

 
  1.   {
  2.   name:'xiang',
  3.   path:'detail/:id/:title/:content',
  4.   component:Detail,
  5.    
  6.   // props的对象写法,作用:把对象中的每一组key-value作为props传给Detail组件
  7.   // props:{a:1,b:2,c:3},
  8.    
  9.   // props的布尔值写法,作用:把收到了每一组params参数,作为props传给Detail组件
  10.   // props:true
  11.    
  12.   // props的函数写法,作用:把返回的对象中每一组key-value作为props传给Detail组件
  13.   props(route){
  14.   return route.query
  15.   }
  16.   }
  17.    
  18.    
  19.    
  20.    
  21.   .vue中:
  22.    
  23.   在子组件使用参数时候:
  24.   根据 defineProps进行参数接收使用
  25.   如:defineProps(['id','title',content'])
 
 

 

4.9. 【 replace属性】

  1. 作用:控制路由跳转时操作浏览器历史记录的模式。

  2. 浏览器的历史记录有两种写入方式:分别为pushreplace

    • push是追加历史记录(默认值)。
    • replace是替换当前记录。
  3. 开启replace模式:

<RouterLink replace .......>News</RouterLink>
 

 

4.10. 【编程式导航】

路由组件的两个重要的属性:$route$router变成了两个hooks

 
  1.   import {useRoute,useRouter} from 'vue-router'
  2.    
  3.   const route = useRoute()
  4.   const router = useRouter()
  5.    
  6.   console.log(route.query)
  7.   console.log(route.parmas)
  8.   console.log(router.push)
  9.   console.log(router.replace)
 
 

 

4.12. 【重定向】

  1. 作用:将特定的路径,重新定向到已有路由。

  2. 具体编码:

 
  1.   {
  2.   path:'/',
  3.   redirect:'/about'
  4.   }
 
 

 

 

5. pinia

5.1【准备一个效果】

5.2【搭建 pinia 环境】

第一步:npm install pinia

第二步:操作src/main.ts

 
  1.   import { createApp } from 'vue'
  2.   import App from './App.vue'
  3.    
  4.   /* 引入createPinia,用于创建pinia */
  5.   import { createPinia } from 'pinia'
  6.    
  7.   /* 创建pinia */
  8.   const pinia = createPinia()
  9.   const app = createApp(App)
  10.    
  11.   /* 使用插件 */{}
  12.   app.use(pinia)
  13.   app.mount('#app')
 
 

此时开发者工具中已经有了pinia选项:

5.3【存储+读取数据】

  • Store是一个保存:状态业务逻辑 的实体,每个组件都可以读取写入它。

  • 它有三个概念:stategetteraction,相当于组件中的: data、 computed 和 methods

  • 具体编码:src/store/count.ts

 
  1.   // 引入defineStore用于创建store
  2.   import { defineStore } from "pinia";
  3.   // 定义并暴露一个store
  4.   export const useCountStore = defineStore("count", {
  5.   // 动作
  6.   actions: {},
  7.   //状态 真正存储数据的地方
  8.   state() {
  9.   return {
  10.   sum: 6,
  11.   };
  12.   },
  13.   // 计算
  14.   getters: {},
  15.   });
 
 
  • 具体编码:src/store/loveTalk.ts
 
  1.   // 引入defineStore用于创建store
  2.   import { defineStore } from "pinia";
  3.   // 定义并暴露一个store
  4.   export const useTalkStore = defineStore("loveTalk", {
  5.   // 动作
  6.   actions: {},
  7.   //状态 真正存储数据的地方
  8.   state() {
  9.   return {
  10.   talkList: [
  11.   { id: "yuysada01", title: "你今天有点怪,哪里怪?怪好看的!" },
  12.   { id: "yuysada02", title: "草莓、蓝莓、蔓越莓,你想我了没?" },
  13.   { id: "yuysada03", title: "心里给你留了一块地,我的死心塌地" },
  14.   ],
  15.   };
  16.   },
  17.   // 计算
  18.   getters: {},
  19.   });
 
 
  • 组件中使用state中的数据

count.vue

 
  1.   <template>
  2.   <h2>当前求和为:{{ sumStore.sum }}</h2>
  3.   </template>
  4.    
  5.   <script setup lang="ts" name="Count">
  6.   // 引入对应的useXxxxxStore
  7.   import {useSumStore} from '@/store/sum'
  8.    
  9.   // 调用useXxxxxStore得到对应的store
  10.   const sumStore = useSumStore()
  11.   </script>
 
 

loveTalk.vue

 
  1.   <template>
  2.   <ul>
  3.   <li v-for="talk in talkStore.talkList" :key="talk.id">
  4.   {{ talk.content }}
  5.   </li>
  6.   </ul>
  7.   </template>
  8.    
  9.   <script setup lang="ts" name="Count">
  10.   import axios from 'axios'
  11.   import {useTalkStore} from '@/store/loveTalk'
  12.    
  13.   const talkStore = useTalkStore()
  14.   </script>
 
 

5.4.【修改数据】(三种方式)

  • 第一种修改方式,直接修改
countStore.sum = 666
 
  • 第二种修改方式:批量修改
 
  1.   //第二种 批量修改
  2.   // countStore.$patch({
  3.   // sum:666,
  4.   // school:"wahh",
  5.   // address:"河北"
  6.   // });
 
 

 

  • 第三种修改方式:借助action修改(action中可以编写一些业务逻辑)
 
  1.   count.vue
  2.    
  3.   <template>
  4.   <div class="count">
  5.   <h2>当前求和为:{{ countStore.sum }}</h2>
  6.   <h2>欢迎来到:{{ countStore.school }},地址在:{{ countStore.address }}</h2>
  7.   <select v-model.number="n">
  8.   <option value="1">1</option>
  9.   <option value="2">2</option>
  10.   <option value="3">3</option>
  11.   </select>
  12.   <button @click="add">加</button>
  13.   <button @click="minus">减</button>
  14.   </div>
  15.   </template>
  16.    
  17.   <script lang="ts" setup name="count">
  18.   import { ref } from "vue";
  19.   //引入countStore
  20.   import { useCountStore } from "@/store/count";
  21.    
  22.   //使用useCountstore,得到一个专门保存count相关的sore
  23.   const countStore = useCountStore();
  24.   // 以下两种方式都可以拿到state中的数据
  25.   // console.log(countStore.sum);
  26.   // console.log(countStore.$state.sum);
  27.    
  28.   let n = ref(1); //用户选择的数字
  29.    
  30.   //加
  31.   function add() {
  32.   //第一种修改
  33.   // countStore.sum += 1;
  34.    
  35.   //第二种 批量修改
  36.   // countStore.$patch({
  37.   // sum:666,
  38.   // school:"wahh",
  39.   // address:"河北"
  40.   // });
  41.    
  42.   //第三种 借助actions修改
  43.   countStore.increment(n.value);
  44.   }
  45.    
  46.   //减
  47.   function minus() {
  48.   //第一种修改
  49.   // countStore.sum -= 1;
  50.   }
  51.   </script>
  52.    
  53.   <style>
  54.   .count {
  55.   background-color: skyblue;
  56.   padding: 10px;
  57.   border-radius: 10px;
  58.   box-shadow: 0 0 10px;
  59.   }
  60.    
  61.   select {
  62.   width: 120px;
  63.   height: 32px;
  64.   }
  65.    
  66.   button {
  67.   width: 62px;
  68.   height: 32px;
  69.   cursor: pointer;
  70.   margin-left: 12px;
  71.   cursor: pointer;
  72.   }
  73.   </style>
  74.    
  75.    
  76.    
  77.    
  78.    
  79.    
  80.    
  81.   store/count.ts
  82.    
  83.   // 引入defineStore用于创建store
  84.   import { defineStore } from "pinia";
  85.   // 定义并暴露一个store
  86.   export const useCountStore = defineStore("count", {
  87.   // actions.里面放置的是一个一个的方法,用于响应组件中的“动付”
  88.   actions: {
  89.   increment(value) {
  90.   if (this.sum < 10) {
  91.   console.log("increment被调用了", value);
  92.   //修改数据 (this是当前的store)
  93.   this.sum += value;
  94.   }
  95.   },
  96.   },
  97.   //状态 真正存储数据的地方
  98.   state() {
  99.   return {
  100.   sum: 6,
  101.   school: "zhongguodaxue",
  102.   address: "北京",
  103.   };
  104.   },
  105.   // 计算
  106.   getters: {},
  107.   });
 
 

 

5.5.【storeToRefs】

  • 借助storeToRefsstore中的数据转为ref对象,方便在模板中使用。
  • 注意:pinia提供的storeToRefs只会将数据做转换,而VuetoRefs会转换store中数据。
 
  1.   <template>
  2.   <div class="count">
  3.   <h2>当前求和为:{{sum}}</h2>
  4.   </div>
  5.   </template>
  6.    
  7.   <script setup lang="ts" name="Count">
  8.   import { useCountStore } from '@/store/count'
  9.   /* 引入storeToRefs */
  10.   import { storeToRefs } from 'pinia'
  11.    
  12.   /* 得到countStore */
  13.   const countStore = useCountStore()
  14.   /* 使用storeToRefs转换countStore,随后解构 */
  15.   const {sum} = storeToRefs(countStore)
  16.   </script>
  17.    
 
 

 

5.6.【getters】

  1. 概念:当state中的数据,需要经过处理后再使用时,可以使用getters配置。

  2. 追加getters配置。

     
    1.   // 引入defineStore用于创建store
    2.   import {defineStore} from 'pinia'
    3.    
    4.   // 定义并暴露一个store
    5.   export const useCountStore = defineStore('count',{
    6.   // 动作
    7.   actions:{
    8.   /************/
    9.   },
    10.   // 状态
    11.   state(){
    12.   return {
    13.   sum:1,
    14.   school:'atguigu'
    15.   }
    16.   },
    17.   // 计算
    18.   getters:{
    19.   bigSum:(state):number => state.sum *10,
    20.   upperSchool():string{
    21.   return this. school.toUpperCase()
    22.   }
    23.   }
    24.   })
     
     
  3. 组件中读取数据:

     
    1.   const {increment,decrement} = countStore
    2.   let {sum,school,bigSum,upperSchool} = storeToRefs(countStore)
     
     

     

5.7.【$subscribe】

通过 store 的 $subscribe() 方法侦听 state 及其变化

loveTalk.vue

 
  1.   <template>
  2.   <div class="talk">
  3.   <button @click="getLoveTalk">获取一句土味情话</button>
  4.   <ul>
  5.   <li v-for="(item, index) in talkList" :key="index">
  6.   {{ item.title }}
  7.   </li>
  8.   </ul>
  9.   </div>
  10.   </template>
  11.   <script lang="ts" setup name="loveTalk">
  12.   import { useTalkStore } from "@/store/loveTalk";
  13.   import { storeToRefs } from "pinia";
  14.    
  15.   // 1:下载
  16.   // npm i nanoid
  17.   // 2:引入
  18.   import { nanoid } from "nanoid"; //随机生成一个id
  19.   // 3:使用
  20.   // console.log(nanoid());
  21.    
  22.   // 以下两种方式都可以拿到state中的数据
  23.   const talkStore = useTalkStore();
  24.   /* 使用storeToRefs转换countStore,随后解构 */
  25.   // storeToRefs只会关注sotre中数据,不会对方法进行ref包裹
  26.   const { talkList } = storeToRefs(talkStore);
  27.    
  28.   // 通过 store 的 $subscribe() 方法侦听 state 及其变化
  29.   talkStore.$subscribe((mutate, state) => {
  30.   console.log(mutate, state, "talkStore里面某个参数发生变化");
  31.   localStorage.setItem("talkStore", JSON.stringify(state.talkList));
  32.   });
  33.    
  34.   function getLoveTalk() {
  35.   talkStore.getATalk();
  36.   }
  37.   </script>
  38.    
  39.   <style scoped>
  40.   .talk {
  41.   background-color: skyblue;
  42.   padding: 10px;
  43.   border-radius: 10px;
  44.   box-shadow: 0 0 10px;
  45.   margin-top: 12px;
  46.   }
  47.   button {
  48.   width: 162px;
  49.   height: 32px;
  50.   cursor: pointer;
  51.   margin-left: 12px;
  52.   cursor: pointer;
  53.   }
  54.   </style>
 
 

store/loveTalk.ts

 
  1.   // 引入defineStore用于创建store
  2.   import { defineStore } from "pinia";
  3.   import axios from "axios";
  4.   import { nanoid } from "nanoid";
  5.    
  6.   // 定义并暴露一个store
  7.   export const useTalkStore = defineStore("loveTalk", {
  8.   // 动作
  9.   actions: {
  10.   async getATalk() {
  11.   //发请求,下面这行的写法是:连续解构赋值+重命名
  12.   let {
  13.   data: { content: title },
  14.   } = await axios.get("https://api.uomg.com/api/rand.qinghua?format=json");
  15.   // 将请求到的数据 放置到talkList中展示
  16.   this.talkList.unshift({ id: nanoid(), title });
  17.   },
  18.   },
  19.   //状态 真正存储数据的地方
  20.   state() {
  21.   return {
  22.   // talkList: [
  23.   // { id: "yuysada01", title: "你今天有点怪,哪里怪?怪好看的!" },
  24.   // { id: "yuysada02", title: "草莓、蓝莓、蔓越莓,你想我了没?" },
  25.   // { id: "yuysada03", title: "心里给你留了一块地,我的死心塌地" },
  26.   // ],
  27.   talkList: JSON.parse(localStorage.getItem("talkStore") as string) || [],
  28.   };
  29.   },
  30.   // 计算
  31.   getters: {},
  32.   });
 
 

 

5.8. 【store组合式写法】

store/loveTalk.ts

 
  1.   // 引入defineStore用于创建store
  2.   import { defineStore } from "pinia";
  3.   import axios from "axios";
  4.   import { nanoid } from "nanoid";
  5.    
  6.   // 定义并暴露一个store
  7.   // export const useTalkStore = defineStore("loveTalk", {
  8.   // // 动作
  9.   // actions: {
  10.   // async getATalk() {
  11.   // //发请求,下面这行的写法是:连续解构赋值+重命名
  12.   // let {
  13.   // data: { content: title },
  14.   // } = await axios.get("https://api.uomg.com/api/rand.qinghua?format=json");
  15.   // // 将请求到的数据 放置到talkList中展示
  16.   // this.talkList.unshift({ id: nanoid(), title });
  17.   // },
  18.   // },
  19.   // //状态 真正存储数据的地方
  20.   // state() {
  21.   // return {
  22.   // // talkList: [
  23.   // // { id: "yuysada01", title: "你今天有点怪,哪里怪?怪好看的!" },
  24.   // // { id: "yuysada02", title: "草莓、蓝莓、蔓越莓,你想我了没?" },
  25.   // // { id: "yuysada03", title: "心里给你留了一块地,我的死心塌地" },
  26.   // // ],
  27.   // talkList: JSON.parse(localStorage.getItem("talkStore") as string) || [],
  28.   // };
  29.   // },
  30.   // // 计算
  31.   // getters: {},
  32.   // });
  33.    
  34.    
  35.    
  36.   // ⬆
  37.   // 比较
  38.   // ⬇
  39.    
  40.   //组合式store
  41.   import { reactive } from "vue";
  42.   export const useTalkStore = defineStore("loveTalk", () => {
  43.   //数据 talkList就是state
  44.   let talkList = reactive(
  45.   JSON.parse(localStorage.getItem("talkStore") as string) || []
  46.   );
  47.    
  48.   // getATalk函数相当于action
  49.   async function getATalk() {
  50.   //发请求,下面这行的写法是:连续解构赋值+重命名
  51.   let {
  52.   data: { content: title },
  53.   } = await axios.get("https://api.uomg.com/api/rand.qinghua?format=json");
  54.   // 将请求到的数据 放置到talkList中展示
  55.   talkList.unshift({ id: nanoid(), title });
  56.   }
  57.    
  58.   return {
  59.   talkList,
  60.   getATalk,
  61.   };
  62.   });
 
 

 

 

6. 组件通信

Vue3组件通信和Vue2的区别:

  • 移出事件总线,使用mitt代替。
  • vuex换成了pinia
  • .sync优化到了v-model里面了。
  • $listeners所有的东西,合并到$attrs中了。
  • $children被砍掉了。

常见搭配形式:

6.1. 【props】

概述:props是使用频率最高的一种通信方式,常用与 :父 ↔ 子

  • 若 父传子:属性值是非函数
  • 若 子传父:属性值是函数

父组件:

 
  1.   <template>
  2.   <div class="father">
  3.   <h3>父组件,</h3>
  4.   <h4>我的车:{{ car }}</h4>
  5.   <h4>儿子给的玩具:{{ toy }}</h4>
  6.   <Child :car="car" :getToy="getToy"/>
  7.   </div>
  8.   </template>
  9.    
  10.   <script setup lang="ts" name="Father">
  11.   import Child from './Child.vue'
  12.   import { ref } from "vue";
  13.   // 数据
  14.   const car = ref('奔驰')
  15.   const toy = ref()
  16.   // 方法
  17.   function getToy(value:string){
  18.   toy.value = value
  19.   }
  20.   </script>
 
 

子组件:

 
  1.   <template>
  2.   <div class="child">
  3.   <h3>子组件</h3>
  4.   <h4>我的玩具:{{ toy }}</h4>
  5.   <h4>父给我的车:{{ car }}</h4>
  6.   <button @click="getToy(toy)">玩具给父亲</button>
  7.   </div>
  8.   </template>
  9.    
  10.   <script setup lang="ts" name="Child">
  11.   import { ref } from "vue";
  12.   const toy = ref('奥特曼')
  13.    
  14.   defineProps(['car','getToy'])
  15.   </script>
 
 

 

6.2. 【自定义事件】

  1. 概述:自定义事件常用于:子 => 父。
  2. 注意区分好:原生事件、自定义事件。
  • 原生事件:

            (1):事件名是特定的(click、mosueenter等等)
            (2):事件对象$event: 是包含事件相关信息的对象(pageX、pageY、target、keyCode)

  • 自定义事件:

            (1):事件名是任意名称
            (2):事件对象$event: 是调用emit时所提供的数据,可以是任意类型!!!

示例1:

 
  1.   <!--在父组件中,给子组件绑定自定义事件:-->
  2.   <Child @send-toy="toy = $event"/>
  3.    
  4.   <!--注意区分原生事件与自定义事件中的$event-->
  5.   <button @click="toy = $event">测试</button>
 
 
 
  1.   //子组件中,触发事件:
  2.   this.$emit('send-toy', 具体数据)
 
 

示例2:

父组件:

 
  1.   <template>
  2.   <div class="father">
  3.   <h3>父组件,</h3>
  4.   <h4 v-show="toy">儿子给的玩具:{{ toy }}</h4>
  5.   <Child @send-toy="saveToy"/>
  6.   </div>
  7.   </template>
  8.    
  9.   <script setup lang="ts" name="Father">
  10.   import Child from './Child.vue'
  11.   import { ref } from "vue";
  12.    
  13.   //数据
  14.   let toy=ref('');
  15.    
  16.   function saveToy(value:string){
  17.   console.log(value);
  18.   toy.value=value;
  19.   }
  20.   </script>
 
 

子组件:

 
  1.   <template>
  2.   <div class="child">
  3.   <h3>子组件</h3>
  4.   <h4>玩具:{{ toy }}</h4>
  5.   <button @click="emit('send-toy', toy)">测试</button>
  6.   </div>
  7.   </template>
  8.    
  9.   <script setup lang="ts" name="Child">
  10.   import { ref } from "vue";
  11.   //数据
  12.   let toy = ref("奥特曼");
  13.   //声明事件
  14.   const emit = defineEmits(["send-toy"]);
  15.   </script>
 
 

 

6.3. 【mitt】

概述:与消息订阅与发布(pubsub)功能类似,可以实现任意组件间通信。

安装mitt

npm i mitt
 

新建文件:src\utils\emitter.ts

 
  1.   //引入mitt
  2.   import mitt from "mitt";
  3.   //调用mitt得到emitter,emitter可以绑定事件、触发事件
  4.   const emitter = mitt();
  5.    
  6.   // 绑定事件
  7.   // emitter.on('test1',()=>{
  8.   // console.log('test1被调用了');
  9.   // })
  10.   // emitter.on('test2',()=>{
  11.   // console.log('test2被调用了');
  12.   // })
  13.    
  14.   //调用事件
  15.   // setInterval(()=>{
  16.   // emitter.emit('test1')
  17.   // emitter.emit('test2')
  18.   // },1000)
  19.    
  20.   //解绑事件
  21.   // setTimeout(()=>{
  22.   // //单独解绑
  23.   // emitter.off('test1')
  24.   // emitter.off('test2')
  25.    
  26.   // //清空所有绑定事件
  27.   // emitter.all.clear()
  28.   // },3000)
  29.    
  30.   // 暴露emitter
  31.   export default emitter;
 
 

接收数据的组件中:绑定事件、同时在销毁前解绑事件:

兄弟组件1:

 
  1.   <template>
  2.   <div class="child">
  3.   <h3>子组件</h3>
  4.   <h4>玩具:{{ toy }}</h4>
  5.   <button @click="emitter.emit('send-toy', toy)">玩具给弟弟玩</button>
  6.   </div>
  7.   </template>
  8.    
  9.   <script setup lang="ts" name="Child">
  10.   import { ref } from "vue";
  11.   import emitter from "./utils/emitter";
  12.    
  13.   // 数据
  14.   let toy = ref("奥特曼");
  15.   </script>
 
 

兄弟组件2:

 
  1.   <template>
  2.   <div class="child">
  3.   <h3>子组件1</h3>
  4.   <h4>我有:{{ computer }}</h4>
  5.   <h4>我哥给我的玩具:{{ toy }}</h4>
  6.   </div>
  7.   </template>
  8.    
  9.   <script setup lang="ts" name="Child">
  10.   import { ref, onUnmounted } from "vue";
  11.   import emitter from "./utils/emitter";
  12.   //数据
  13.   let computer = ref("电脑");
  14.   let toy = ref("");
  15.    
  16.   //给emitter绑定send-toy事件
  17.   emitter.on("send-toy", (value: any) => {
  18.   // console.log("send-toy", value);
  19.   toy.value = value;
  20.   });
  21.    
  22.   //组件卸载时候 进行解绑send-toy事件
  23.   onUnmounted(() => {
  24.   emitter.off("send-toy");
  25.   });
  26.   </script>
 
 

注意这个重要的内置关系,总线依赖着这个内置关系

 

【v-model】

    1. 概述:实现 父↔子 之间相互通信。

    2. 前序知识 —— v-model的本质

 
  1.   <!-- 使用v-model指令 -->
  2.   <input type="text" v-model="userName">
  3.    
  4.   <!-- v-model的本质是下面这行代码 -->
  5.   <input
  6.   type="text"
  7.   :value="userName"
  8.   @input="userName =(<HTMLInputElement>$event.target).value"
  9.   >
 
 

     3. 组件标签上的v-model的本质::moldeValue + update:modelValue事件。

 
  1.   <!-- 组件标签上使用v-model指令 -->
  2.   <AtguiguInput v-model="userName"/>
  3.    
  4.   <!-- 组件标签上v-model的本质 -->
  5.   <AtguiguInput :modelValue="userName" @update:model-value="userName = $event"/>
 
 

   AtguiguInput组件中:

 
  1.   <template>
  2.   <div class="box">
  3.   <!--将接收的value值赋给input元素的value属性,目的是:为了呈现数据 -->
  4.   <!--给input元素绑定原生input事件,触发input事件时,进而触发update:model-value事件-->
  5.   <input
  6.   type="text"
  7.   :value="modelValue"
  8.   @input="emit('update:model-value',$event.target.value)"
  9.   >
  10.   </div>
  11.   </template>
  12.    
  13.   <script setup lang="ts" name="AtguiguInput">
  14.   // 接收props
  15.   defineProps(['modelValue'])
  16.   // 声明事件
  17.   const emit = defineEmits(['update:model-value'])
  18.   </script>
 
 

      4. 也可以更换value,例如改成abc

 
  1.   <!-- 也可以更换value,例如改成abc-->
  2.   <AtguiguInput v-model:abc="userName"/>
  3.    
  4.   <!-- 上面代码的本质如下 -->
  5.   <AtguiguInput :abc="userName" @update:abc="userName = $event"/>
 
 

    AtguiguInput组件中:

 
  1.   <template>
  2.   <div class="box">
  3.   <input
  4.   type="text"
  5.   :value="abc"
  6.   @input="emit('update:abc',$event.target.value)"
  7.   >
  8.   </div>
  9.   </template>
  10.    
  11.   <script setup lang="ts" name="AtguiguInput">
  12.   // 接收props
  13.   defineProps(['abc'])
  14.   // 声明事件
  15.   const emit = defineEmits(['update:abc'])
  16.   </script>
 
 

       5. 如果value可以更换,那么就可以在组件标签上多次使用v-model

<AtguiguInput v-model:abc="userName" v-model:xyz="password"/>
 

 

 

 

 

 

 

标签:TypeScript,name,vue,value,API,let,Vue3,import,ref
From: https://www.cnblogs.com/web1123/p/18394139

相关文章

  • Vue3.0《小兔鲜儿》企业级电商项目开发实战,市场主流热门技术和业务解决方案
    请描述一下这个系统?【回答技巧】从3个方面来回答这个问题:|–系统背景及系统概述|–系统包括的业务模块及主业务流程|–责任模块【回答示例】第一个方面:系统背景及系统概述优购时尚商城是香港上市公司百丽国际公司为拓宽旗下运动品牌服饰市场而开发的一个专业销售购物网站......
  • 基于外卖业务的数据可视化入门到精通-Vue3.0+EChart4.0
    基于外卖业务的数据可视化入门到精通-Vue3.0+EChart4.0项目分辨率响应式创建 项目顶部信息条创建 页面主体创建  接项目搭建与初始化之后继续对项目进行部署工作项目展示:        技术栈:1.vue3.0+vue-router4.0+axios2.flex布局3.LESS4.rem屏......
  • Vue3+NestJS+Vite4+TS4+Mysql8+Nginx全栈开发企业级管理后台
    vite打包快的原因:冷启动1.esbuild构建依赖,go语言编写多线程打包。2.原生的esm方式提供源码,浏览器分担了一部分工作。HMR热更新1.缓存机制,利用浏览器http头部,源码模块请求根据304协商缓存和依赖模块请求通过强缓存(cache-control:max-age=315360000,public,immutable)只是模块......
  • Vue3+TypeScript+Vite+Pinia+ElementPlus开发项目在线医疗服务平台
     前言随着vue3.0的越来越受欢迎,开始有许多公司和个人开始学习并使用vue3开发项目。我从接触学习并使用vue2,到现在的vue3,工作中也一直在使用vue。vue3也发布很长时间了,目前vue3+vite+ts再结合一些优秀的UI框架,如Elementplus,Antdesign,NaiveUI,移动端的VantUI,成为了较为流......
  • 话费接口API对接流程是什么?又有哪些优势?
    话费接口API对接流程前期准备找一家专业做话费充值的公司,联系其商务了解对接的具体情况,包括合作模式、话费价格、消耗及打款金额是否可以开票、对接时是否有技术配合等开户与对接确定合作后在话费充值平台进行开户,获取账户参数及接口文档开始对接接口测试对接完成后进......
  • Postman完美平替!免费开源、轻量级、快速且美观的 API 调试工具Hoppscotch
    Hoppscotch:开源API的智能伙伴,让开发更简单、更高效。 - 精选真开源,释放新价值。概览Hoppscotch是一个开源的API开发工具,它为开发者提供了一个全面而直观的平台来构建、测试和监控API。作为一个开源项目,它具有高度的可定制性和灵活性,允许用户根据自己的需求进行调整。Hoppscotc......
  • 快速开店新策略:新手如何利用API接口开启电商之旅
    随着电子商务的蓬勃发展,越来越多的新手卖家希望建立自己的在线商店。但是,对于没有技术背景的新手来说,如何快速、高效地开设网店是一个挑战。幸运的是,API接口提供了一种解决方案,可以帮助您轻松实现网店的搭建和管理。本文将向您展示如何利用API接口快速开设网店。一、了解API接口......
  • vue3 pinia 的基本用法
    ‌Pinia是Vue3的状态管理器,用于跨组件或页面共享状态。以下是使用Pinia的基本步骤:‌安装Pinia‌:首先,你需要在项目中安装Pinia。你可以使用npm或yarn进行安装。例如,使用npm,你可以运行 npminstallpinia 命令来安装Pinia。‌创建Store‌:在Vue3中,你可以使用......
  • vue3 vue-router 的基本使用和配置方法
    在 vue3 中使用 vue-router 的基本步骤如下:1.安装vue-router:npminstallvue-router@42.创建一个 vue-router 实例并定义路由:import{createRouter,createWebHistory}from'vue-router';importHomefrom'./components/Home.vue';importAboutfrom'./com......
  • javascript基础从小白到高手系列一千九百二十四:Fetch API
    FetchAPI能够执行XMLHttpRequest对象的所有任务,但更容易使用,接口也更现代化,能够在Web工作线程等现代Web工具中使用。XMLHttpRequest可以选择异步,而FetchAPI则必须是异步。FetchAPI是WHATWG的一个“活标准”(livingstandard),用规范原文说,就是“Fetch标准定义请......