当前位置:   article > 正文

Vue3学习笔记(尚硅谷天禹老师)_尚硅谷vue3笔记

尚硅谷vue3笔记

1.Vue3简介

1.1性能的提升

        打包大小减少41%;

        初次渲染快55%,更新渲染快133%;

        内存减少54%。

1.2源码的升级

        使用Proxy代替defineProperty实现响应式

        重写虚拟Dom的实现和Tree-Shaking

1.3拥抱TypeScript

        Vue3可以更好的支持TypeScript

1.4新的特性

1. `Composition API`(组合`API`):

   - `setup`

   - `ref`与`reactive`

   - `computed`与`watch`

2. 新的内置组件:

   - `Fragment`

   - `Teleport`

   - `Suspense`

3. 其他改变:

   - 新的生命周期钩子

   - `data` 选项应始终被声明为一个函数

   - 移除`keyCode`支持作为` v-on` 的修饰符

 2. 创建Vue3工程

2.1. 【基于 vue-cli 创建】

点击查看[官方文档](https://cli.vuejs.org/zh/guide/creating-a-project.html#vue-create)

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

        powershell

## 查看@vue/cli版本,确保@vue/cli版本在4.5.0以上

        vue --version

## 安装或者升级你的@vue/cli

        npm install -g @vue/cli

## 执行创建命令

        vue create vue_test

##  随后选择3.x

##  Choose a version of Vue.js that you want to start the project with (Use arrow keys)

##  > 3.x

##    2.x

## 启动

        cd vue_test

        npm run serve

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

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

- 轻量快速的热重载(`HMR`),能实现极速的服务启动。

- 对 `TypeScript`、`JSX`、`CSS` 等支持开箱即用。

- 真正的按需编译,不再等待整个应用编译完成。

- `webpack`构建 与 `vite`构建对比图如下:


* 具体操作如下(点击查看[官方文档](https://cn.vuejs.org/guide/quick-start.html#creating-a-vue-application))

(如果是后端同学需要先下载node.js插件。直接去官网下载)

2.2.1具体操作

```powershell

## 1.创建命令

npm create vue@latest

## 2.具体配置

## 配置项目名称

        √ Project name: vue3_test

## 是否添加TypeScript支持

        √ Add TypeScript?  Yes

## 是否添加JSX支持

        √ Add JSX Support?  No

## 是否添加路由环境

        √ Add Vue Router for Single Page Application development?  No

## 是否添加pinia环境

        √ Add Pinia for state management?  No

## 是否添加单元测试

        √ Add Vitest for Unit Testing?  No

## 是否添加端到端测试方案

        √ Add an End-to-End Testing Solution? » No

## 是否添加ESLint语法检查

        √ Add ESLint for code quality?  Yes

## 是否添加Prettiert代码格式化

        √ Add Prettier for code formatting?  No

安装完后用vscode打开文件会有如图文件

打开env.d.ts文件,如果提示错误。就要安装依赖

打开终端运行

        npm i  (安装所有依赖)

*******************************************************************注意!!!!!

如果原来vscode中安装的有vetur组件,禁掉禁掉禁掉!这个是Vue2的语法检查,会报错。我受折磨了一天。找了好久        

2.3.编写一个App组件

  1. <template>
  2. <div class="app">
  3. <h1>你好啊!</h1>
  4. </div>
  5. </template>
  6. <script lang="ts">
  7. export default {
  8. name:'App' //组件名
  9. }
  10. </script>
  11. <style>
  12. .app {
  13. background-color: #ddd;
  14. box-shadow: 0 0 10px;
  15. border-radius: 10px;
  16. padding: 20px;
  17. }
  18. </style>

总结:

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

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

2.3.1一个简单的效果

  1. <template>
  2. <div class="person">
  3. <h2>姓名:{{name}}</h2>
  4. <h2>年龄:{{age}}</h2>
  5. <button @click="changeName">修改名字</button>
  6. <button @click="changeAge">年龄+1</button>
  7. <button @click="showTel">点我查看联系方式</button>
  8. </div>
  9. </template>
  10. <script lang="ts">
  11. export default {
  12. name:'App',
  13. data() {
  14. return {
  15. name:'张三',
  16. age:18,
  17. tel:'13888888888'
  18. }
  19. },
  20. methods:{
  21. changeName(){
  22. this.name = 'zhang-san'
  23. },
  24. changeAge(){
  25. this.age += 1
  26. },
  27. showTel(){
  28. alert(this.tel)
  29. }
  30. },
  31. }
  32. </script>

3.Vue三核心语法

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`是`Vue3`中一个新的配置项,值是一个函数,它是 `Composition API` **“表演的舞台**_**”**_,组件中所用到的:数据、方法、计算属性、监视......等等,均配置在`setup`中。

传统选项式>>>

Vue3组合式>>>

  1. setup(){
  2. console.log(this) //setup中的this是undefined,Vue3在弱化this了
  3. // 数据,原来是写在data中的,此时的name、age、tel都不是响应式的数据
  4. let name = '张三'
  5. let age = 18
  6. let tel = '13888888888'
  7. // 方法
  8. function changeName() {
  9. name = 'zhang-san' //注意:这样修改name,页面是没有变化的
  10. console.log(name) //name确实改了,但name不是响应式的
  11. }
  12. function changeAge() {
  13. age += 1 //注意:这样修改age,页面是没有变化的
  14. console.log(age) //age确实改了,但age不是响应式的
  15. }
  16. function showTel() {
  17. alert(tel)
  18. }
  19. // 将数据、方法交出去,模板中才可以使用
  20. return {name,age,tel,changeName,changeAge,showTel}
  21. }

特点如下:

- `setup`函数返回的对象中的内容,可直接在模板中使用。
- `setup`中访问`this`是`undefined`
- `setup`函数会在`beforeCreate`之前调用,它是“领先”所有钩子执行的。

(Vue3中弱化了this)

3.2.1setup的返回值

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

3.2.2setup 与 Options API 的关系

(面试题)

- `Vue2` 的配置(`data`、`methos`)中可以访问到`setup`中的属性、方法。(因为setup处于beforecreate之前)
- 但在`setup`中不能访问到`Vue2`的配置(`data`、`methos`)。
- 如果与`Vue2`冲突,则`setup`优先。

(setup没有this。里面不能写this)

3.3setup 语法糖

下图该代码直接把setup独立出去。相当于在原script标签中的setup中的setup函数

代码:

  1. <template>
  2. <div class="person">
  3. <h2>姓名:{{name}}</h2>
  4. <h2>年龄:{{age}}</h2>
  5. <button @click="changName">修改名字</button>
  6. <button @click="changAge">年龄+1</button>
  7. <button @click="showTel">点我查看联系方式</button>
  8. </div>
  9. </template>
  10. <script lang="ts">
  11. export default {
  12. name:'Person',
  13. }
  14. </script>
  15. <!-- 下面的写法是setup语法糖 -->
  16. <script setup lang="ts">
  17. console.log(this) //undefined
  18. // 数据(注意:此时的name、age、tel都不是响应式数据)
  19. let name = '张三'
  20. let age = 18
  21. let tel = '13888888888'
  22. // 方法
  23. function changName(){
  24. name = '李四'//注意:此时这么修改name页面是不变化的
  25. }
  26. function changAge(){
  27. console.log(age)
  28. age += 1 //注意:此时这么修改age页面是不变化的
  29. }
  30. function showTel(){
  31. alert(tel)
  32. }
  33. </script>

扩展:上述代码,还需要编写一个不写`setup`的`script`标签,去指定组件名字,比较麻烦,我们可以借助`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. export default defineConfig({
  4. plugins: [ VueSetupExtend() ]
  5. })

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

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

(该ref与vue2中的ref不同。该ref是可以使数据变为响应式的,哪个是响应式,就给哪个加)

- 作用:定义响应式变量。
- 语法:`let xxx = ref(初始值)`。
- 返回值:一个`RefImpl`的实例对象,简称`ref对象`或`ref`,`ref`对象的`value`属性是响应式的。
- 注意点:
   - `JS`中操作数据需要:`xxx.value`,但模板中不需要`.value`,直接使用即可。
   - 对于`let name = ref('张三')`来说,`name`不是响应式的,`name.value`是响应式的。(JS中操作ref对象时候需要.value)

  1. <template>
  2. <div class="person">
  3. <h2>姓名:{{name}}</h2>
  4. <h2>年龄:{{age}}</h2>
  5. <button @click="changeName">修改名字</button>
  6. <button @click="changeAge">年龄+1</button>
  7. <button @click="showTel">点我查看联系方式</button>
  8. </div>
  9. </template>
  10. <script setup lang="ts" name="Person">
  11. import {ref} from 'vue'
  12. // name和age是一个RefImpl的实例对象,简称ref对象,它们的value属性是响应式的。
  13. let name = ref('张三')
  14. let age = ref(18)
  15. // tel就是一个普通的字符串,不是响应式的
  16. let tel = '13888888888'
  17. function changeName(){
  18. // JS中操作ref对象时候需要.value
  19. name.value = '李四'
  20. console.log(name.value)
  21. // 注意:name不是响应式的,name.value是响应式的,所以如下代码并不会引起页面的更新。
  22. // name = ref('zhang-san')
  23. }
  24. function changeAge(){
  25. // JS中操作ref对象时候需要.value
  26. age.value += 1
  27. console.log(age.value)
  28. }
  29. function showTel(){
  30. alert(tel)
  31. }
  32. </script>

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

- 作用:定义一个响应式对象(基本类型不要用它,要用`ref`,否则报错)
- 语法:`let 响应式对象= reactive(源对象)`。
- 返回值:一个`Proxy`的实例对象,简称:响应式对象。
- 注意点:`reactive`定义的响应式数据是“深层次”的。

(Shift + Alt + 上/下箭头 选中代码往上/下面复制)

  1. <template>
  2. <div class="person">
  3. <h2>汽车信息:一台{{ car.brand }}汽车,价值{{ car.price }}万</h2>
  4. <h2>游戏列表:</h2>
  5. <ul>
  6. <li v-for="g in games" :key="g.id">{{ g.name }}</li>
  7. </ul>
  8. <h2>测试:{{obj.a.b.c.d}}</h2>
  9. <button @click="changeCarPrice">修改汽车价格</button>
  10. <button @click="changeFirstGame">修改第一游戏</button>
  11. <button @click="test">测试</button>
  12. </div>
  13. </template>
  14. <script lang="ts" setup name="Person">
  15. import { reactive } from 'vue'
  16. // 数据
  17. let car = reactive({ brand: '奔驰', price: 100 })
  18. let games = reactive([
  19. { id: 'ahsgdyfa01', name: '英雄联盟' },
  20. { id: 'ahsgdyfa02', name: '王者荣耀' },
  21. { id: 'ahsgdyfa03', name: '原神' }
  22. ])
  23. let obj = reactive({
  24. a:{
  25. b:{
  26. c:{
  27. d:666
  28. }
  29. }
  30. }
  31. })
  32. function changeCarPrice() {
  33. car.price += 10
  34. }
  35. function changeFirstGame() {
  36. games[0].name = '流星蝴蝶剑'
  37. }
  38. function test(){
  39. obj.a.b.c.d = 999
  40. }
  41. </script>

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

- 其实`ref`接收的数据可以是:基本类型、对象类型。
- 若`ref`接收的是对象类型,内部其实也是调用了`reactive`函数

(reactive只能定义对象类型的响应式数据)

  1. <template>
  2. <div class="person">
  3. <h2>汽车信息:一台{{ car.brand }}汽车,价值{{ car.price }}万</h2>
  4. <h2>游戏列表:</h2>
  5. <ul>
  6. <li v-for="g in games" :key="g.id">{{ g.name }}</li>
  7. </ul>
  8. <h2>测试:{{obj.a.b.c.d}}</h2>
  9. <button @click="changeCarPrice">修改汽车价格</button>
  10. <button @click="changeFirstGame">修改第一游戏</button>
  11. <button @click="test">测试</button>
  12. </div>
  13. </template>
  14. <script lang="ts" setup name="Person">
  15. import { ref } from 'vue'
  16. // 数据
  17. let car = ref({ brand: '奔驰', price: 100 })
  18. let games = ref([
  19. { id: 'ahsgdyfa01', name: '英雄联盟' },
  20. { id: 'ahsgdyfa02', name: '王者荣耀' },
  21. { id: 'ahsgdyfa03', name: '原神' }
  22. ])
  23. let obj = ref({
  24. a:{
  25. b:{
  26. c:{
  27. d:666
  28. }
  29. }
  30. }
  31. })
  32. console.log(car)
  33. function changeCarPrice() {
  34. car.value.price += 10
  35. }
  36. function changeFirstGame() {
  37. games.value[0].name = '流星蝴蝶剑'
  38. }
  39. function test(){
  40. obj.value.a.b.c.d = 999
  41. }
  42. </script>

3.7. 【ref 对比 reactive】

宏观角度看:

> 1. `ref`用来定义:基本类型数据、对象类型数据;
> 2. `reactive`用来定义:对象类型数据。

区别:
> 1. `ref`创建的变量必须使用`.value`(可以使用`volar`插件自动添加`.value`)。
> 2. `reactive`重新分配一个新对象,会失去响应式(可以使用`Object.assign`去整体替换)。

- 使用原则:
> 1. 若需要一个基本类型的响应式数据,必须使用`ref`。
> 2. 若需要一个响应式对象,层级不深,`ref`、`reactive`都可以。
> 3. 若需要一个响应式对象,且层级较深,推荐使用`reactive`。做表单相关的数据,推荐使用reactive。

3.8. 【toRefs 与 toRef】

- 作用:将一个响应式对象中的每一个属性,转换为`ref`对象。
- 备注:`toRefs`与`toRef`功能一致,但`toRefs`可以批量转换。 

-toRefs是把整个reactive所定义的对象的每一组key-value都拿出来形成一个新的对象并且还具备响应式的能力
- 语法如下:

  1. <template>
  2. <div class="person">
  3. <h2>姓名:{{person.name}}</h2>
  4. <h2>年龄:{{person.age}}</h2>
  5. <h2>性别:{{person.gender}}</h2>
  6. <button @click="changeName">修改名字</button>
  7. <button @click="changeAge">修改年龄</button>
  8. <button @click="changeGender">修改性别</button>
  9. </div>
  10. </template>
  11. <script lang="ts" setup name="Person">
  12. import {ref,reactive,toRefs,toRef} from 'vue'
  13. // 数据
  14. let person = reactive({name:'张三', age:18, gender:'男'})
  15. // 通过toRefs将person对象中的n个属性批量取出,且依然保持响应式的能力
  16. let {name,gender} = toRefs(person)
  17. // 通过toRef将person对象中的gender属性取出,且依然保持响应式的能力
  18. let age = toRef(person,'age')
  19. // 方法
  20. function changeName(){
  21. name.value += '~'
  22. }
  23. function changeAge(){
  24. age.value += 1
  25. }
  26. function changeGender(){
  27. gender.value = '女'
  28. }
  29. </script>

3.8. 【computed计算属性】

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

底层借助了object.defineproperty方法提供的getter和setter

  1. <template>
  2. <div class="person">
  3. 姓:<input type="text" v-model="firstName"> <br>
  4. 名:<input type="text" v-model="lastName"> <br>
  5. 全名:<span>{{fullName}}</span> <br>
  6. <button @click="changeFullName">全名改为:li-si</button>
  7. </div>
  8. </template>
  9. <script setup lang="ts" name="App">
  10. import {ref,computed} from 'vue'
  11. let firstName = ref('zhang')
  12. let lastName = ref('san')
  13. // 计算属性——只读取,不修改
  14. /* let fullName = computed(()=>{
  15. return firstName.value + '-' + lastName.value
  16. }) */
  17. // 计算属性——既读取又修改
  18. let fullName = computed({
  19. // 读取
  20. get(){
  21. return firstName.value + '-' + lastName.value
  22. },
  23. // 修改
  24. set(val){
  25. console.log('有人修改了fullName',val)
  26. firstName.value = val.split('-')[0]
  27. lastName.value = val.split('-')[1]
  28. }
  29. })
  30. function changeFullName(){
  31. fullName.value = 'li-si'
  32. }
  33. </script>

3.9.【watch监听】

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

(官方文档

watch() 默认是懒侦听的,即仅在侦听源发生变化时才执行回调函数。

第一个参数是侦听器的。这个来源可以是以下几种:

  • 一个函数,返回一个值
  • 一个 ref
  • 一个响应式对象
  • ...或是由以上类型的值组成的数组

)

我们在`Vue3`中使用`watch`的时候,通常会遇到以下几种情况:
 * *情况一
监视`ref`定义的【基本类型】数据:直接写数据名即可,监视的是其`value`值的改变。

  1. <template>
  2. <div class="person">
  3. <h1>情况一:监视【ref】定义的【基本类型】数据</h1>
  4. <h2>当前求和为:{{sum}}</h2>
  5. <button @click="changeSum">点我sum+1</button>
  6. </div>
  7. </template>
  8. <script lang="ts" setup name="Person">
  9. import {ref,watch} from 'vue'
  10. // 数据
  11. let sum = ref(0)
  12. // 方法
  13. function changeSum(){
  14. sum.value += 1
  15. }
  16. // 监视,情况一:监视【ref】定义的【基本类型】数据
  17. const stopWatch = watch(sum,(newValue,oldValue)=>{ //不写sum.value,因为sum才是ref定义的数据。而不是sum.value
  18. console.log('sum变化了',newValue,oldValue)
  19. if(newValue >= 10){
  20. stopWatch() //回调一个停止监视的函数,此处是当newValue>10时,停止监视。
  21. }
  22. })
  23. </script>

**情况二

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

 注意:
> 若修改的是`ref`定义的对象中的属性,`newValue` 和 `oldValue` 都是新值,因为它们是同一个对象。
> 若修改整个`ref`定义的对象,`newValue` 是新值, `oldValue` 是旧值,因为不是同一个对象了。

>监视,情况一:监视【ref】定义的【对象类型】数据,监视的是对象的地址值,只有整个对象被改变的时候才会被监视到。

    若想监视对象内部属性的变化,需要手动开启深度监视

    watch的第一个参数是:被监视的数据

    watch的第二个参数是:监视的回调

    watch的第三个参数是:配置对象(deep、immediate等等.....)qing

  1. <template>
  2. <div class="person">
  3. <h1>情况二:监视【ref】定义的【对象类型】数据</h1>
  4. <h2>姓名:{{ person.name }}</h2>
  5. <h2>年龄:{{ person.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" setup name="Person">
  12. import {ref,watch} from 'vue'
  13. // 数据
  14. let person = ref({
  15. name:'张三',
  16. age:18
  17. })
  18. // 方法
  19. function changeName(){
  20. person.value.name += '~'
  21. }
  22. function changeAge(){
  23. person.value.age += 1
  24. }
  25. function changePerson(){
  26. person.value = {name:'李四',age:90}
  27. }
  28. /*
  29. 监视,情况一:监视【ref】定义的【对象类型】数据,监视的是对象的地址值,只有整个对象被改变的时候才会被监视到。
  30. 若想监视对象内部属性的变化,需要手动开启深度监视
  31. watch的第一个参数是:被监视的数据
  32. watch的第二个参数是:监视的回调
  33. watch的第三个参数是:配置对象(deep、immediate等等.....)
  34. */
  35. watch(person,(newValue,oldValue)=>{
  36. console.log('person变化了',newValue,oldValue)
  37. },{deep:true})
  38. </script>

  * *情况三

监视`reactive`定义的【对象类型】数据,且默认开启了深度监视。

(reactiv隐式创建深层监听,并且该深度监视没法关闭)

对比ref与reactiv修改整个对象的方法

代码:

  1. <template>
  2. <div class="person">
  3. <h1>情况三:监视【reactive】定义的【对象类型】数据</h1>
  4. <h2>姓名:{{ person.name }}</h2>
  5. <h2>年龄:{{ person.age }}</h2>
  6. <button @click="changeName">修改名字</button>
  7. <button @click="changeAge">修改年龄</button>
  8. <button @click="changePerson">修改整个人</button>
  9. <hr>
  10. <h2>测试:{{obj.a.b.c}}</h2>
  11. <button @click="test">修改obj.a.b.c</button>
  12. </div>
  13. </template>
  14. <script lang="ts" setup name="Person">
  15. import {reactive,watch} from 'vue'
  16. // 数据
  17. let person = reactive({
  18. name:'张三',
  19. age:18
  20. })
  21. let obj = reactive({
  22. a:{
  23. b:{
  24. c:666
  25. }
  26. }
  27. })
  28. // 方法
  29. function changeName(){
  30. person.name += '~'
  31. }
  32. function changeAge(){
  33. person.age += 1
  34. }
  35. function changePerson(){
  36. Object.assign(person,{name:'李四',age:80})
  37. }
  38. function test(){
  39. obj.a.b.c = 888
  40. }
  41. // 监视,情况三:监视【reactive】定义的【对象类型】数据,且默认是开启深度监视的
  42. watch(person,(newValue,oldValue)=>{
  43. console.log('person变化了',newValue,oldValue)
  44. })
  45. watch(obj,(newValue,oldValue)=>{
  46. console.log('Obj变化了',newValue,oldValue)
  47. })
  48. </script>

* *情况四

监视`ref`或`reactive`定义的【对象类型】数据中的某个属性,(遇到该情况,反手就用函数)注意点如下:

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

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

代码:

  1. <template>
  2. <div class="person">
  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" setup name="Person">
  15. import {reactive,watch} from 'vue'
  16. // 数据
  17. let person = reactive({
  18. name:'张三',
  19. age:18,
  20. car:{
  21. c1:'奔驰',
  22. c2:'宝马'
  23. }
  24. })
  25. // 方法
  26. function changeName(){
  27. person.name += '~'
  28. }
  29. function changeAge(){
  30. person.age += 1
  31. }
  32. function changeC1(){
  33. person.car.c1 = '奥迪'
  34. }
  35. function changeC2(){
  36. person.car.c2 = '大众'
  37. }
  38. function changeCar(){
  39. person.car = {c1:'雅迪',c2:'爱玛'}
  40. }
  41. // 监视,情况四:监视响应式对象中的某个属性,且该属性是基本类型的,要写成函数式
  42. /* watch(()=> person.name,(newValue,oldValue)=>{
  43. console.log('person.name变化了',newValue,oldValue)
  44. }) */
  45. // 监视,情况四:监视响应式对象中的某个属性,且该属性是对象类型的,可以直接写,也能写函数,更推荐写函数
  46. watch(()=>person.car,(newValue,oldValue)=>{
  47. console.log('person.car变化了',newValue,oldValue)
  48. },{deep:true})
  49. </script>

** 情况五
监视上述的多个数据

  1. <template>
  2. <div class="person">
  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" setup name="Person">
  15. import {reactive,watch} from 'vue'
  16. // 数据
  17. let person = reactive({
  18. name:'张三',
  19. age:18,
  20. car:{
  21. c1:'奔驰',
  22. c2:'宝马'
  23. }
  24. })
  25. // 方法
  26. function changeName(){
  27. person.name += '~'
  28. }
  29. function changeAge(){
  30. person.age += 1
  31. }
  32. function changeC1(){
  33. person.car.c1 = '奥迪'
  34. }
  35. function changeC2(){
  36. person.car.c2 = '大众'
  37. }
  38. function changeCar(){
  39. person.car = {c1:'雅迪',c2:'爱玛'}
  40. }
  41. // 监视,情况五:监视上述的多个数据
  42. watch([()=>person.name,person.car],(newValue,oldValue)=>{
  43. console.log('person.car变化了',newValue,oldValue)
  44. },{deep:true})
  45. </script>

3.10. 【watchEffect】

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

 `watch`对比`watchEffect`

  > 1. 都能监听响应式数据的变化,不同的是监听数据变化的方式不同
  > 2. `watch`:要明确指出监视的数据,watch是惰性的,你让他监视谁才监视谁
  > 3. `watchEffect`:不用明确指出监视的数据(函数中用到哪些属性,那就监视哪些属性)。

  1. <template>
  2. <div class="person">
  3. <h2>需求:当水温达到60度,或水位达到80cm时,给服务器发请求</h2>
  4. <h2>当前水温:{{temp}}℃</h2>
  5. <h2>当前水位:{{height}}cm</h2>
  6. <button @click="changeTemp">水温+10</button>
  7. <button @click="changeHeight">水位+10</button>
  8. </div>
  9. </template>
  10. <script lang="ts" setup name="Person">
  11. import {ref,watch,watchEffect} from 'vue'
  12. // 数据
  13. let temp = ref(10)
  14. let height = ref(0)
  15. // 方法
  16. function changeTemp(){
  17. temp.value += 10
  18. }
  19. function changeHeight(){
  20. height.value += 10
  21. }
  22. // 监视 -- watch实现 watch是惰性的,你让他监视谁才监视谁
  23. /* watch([temp,height],(value)=>{
  24. // 从value中获取最新的水温(newTemp)、最新的水位(newHeight)
  25. let [newTemp,newHeight] = value
  26. // 逻辑
  27. if(newTemp >= 60 || newHeight >= 80){
  28. console.log('给服务器发请求')
  29. }
  30. }) */
  31. // 监视 -- watchEffect实现
  32. watchEffect(()=>{
  33. if(temp.value >= 60 || height.value >= 80){
  34. console.log('给服务器发请求')
  35. }
  36. })
  37. </script>

3.11. 【标签的 ref 属性】

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

> * 用在普通`DOM`标签上,获取的是`DOM`节点。
> * 用在组件标签上,获取的是组件实例对象。

用在普通`DOM`标签上:

  1. <template>
  2. <div class="person">
  3. <h1 ref="title1">尚硅谷</h1>
  4. <h2 ref="title2">前端</h2>
  5. <h3 ref="title3">Vue</h3>
  6. <input type="text" ref="inpt"> <br><br>
  7. <button @click="showLog">点我打印内容</button>
  8. </div>
  9. </template>
  10. <script lang="ts" setup name="Person">
  11. import {ref} from 'vue'
  12. let title1 = ref()
  13. let title2 = ref()
  14. let title3 = ref()
  15. function showLog(){
  16. // 通过id获取元素
  17. const t1 = document.getElementById('title1')
  18. // 打印内容
  19. console.log((t1 as HTMLElement).innerText)
  20. console.log((<HTMLElement>t1).innerText)
  21. console.log(t1?.innerText)
  22. //
  23. // 通过ref获取元素
  24. console.log(title1.value)
  25. console.log(title2.value)
  26. console.log(title3.value)
  27. }
  28. </script>

用在组件标签上:

  1. <!-- 父组件App.vue -->
  2. <template>
  3. <Person ref="ren"/>
  4. <button @click="test">测试</button>
  5. </template>
  6. <script lang="ts" setup name="App">
  7. import Person from './components/Person.vue'
  8. import {ref} from 'vue'
  9. let ren = ref()
  10. function test(){
  11. console.log(ren.value.name)
  12. console.log(ren.value.age)
  13. }
  14. </script>
  15. <!-- 子组件Person.vue中要使用defineExpose暴露内容 -->
  16. <script lang="ts" setup name="Person">
  17. import {ref,defineExpose} from 'vue'
  18. // 数据
  19. let name = ref('张三')
  20. let age = ref(18)
  21. //
  22. //
  23. // 使用defineExpose将组件中的数据交给外部
  24. defineExpose({name,age})
  25. </script>

3.12. 【props】

路径别名报错注意:

如果在引入"@"符号以下报错的时候,出现

这个错误通常是由于找不到所需的模块或类型声明文件引起的。在 Vue 3 项目中,使用@引入根目录是一个常见的路径别名设置。

要解决这个问题,你需要在你的项目中进行一些配置:

  1. 在项目的根目录下找到 tsconfig.json 文件(如果没有,请创建一个)。这是 TypeScript 的配置文件。

  2. tsconfig.json 文件中,添加一个 "baseUrl" 属性,指定你的根目录的路径

  3. 如果你使用了路径别名,比如 "@",你还需要添加一个 "paths" 属性来映射路径别名到实际的文件路径。

  4. tsconfig.json 文件代码如下:

    1. {
    2. "files": [],
    3. "references": [
    4. {
    5. "path": "./tsconfig.node.json"
    6. },
    7. {
    8. "path": "./tsconfig.app.json"
    9. }
    10. ],
    11. "compilerOptions": {
    12. "baseUrl": ".",
    13. "paths": {
    14. "@/*": ["src/*"]
    15. },
    16. // 其他选项...
    17. }
    18. }

index.ts代码:

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

 App.vue代码:

  1. <template>
  2. <!-- 务必看懂下面这一行代码 -->
  3. <!-- <h2 a="1+1" :b="1+1" c="x" :d="x" ref="qwe">测试</h2> -->
  4. <Person a="哈哈" />
  5. </template>
  6. <script lang="ts" setup name="App">
  7. import Person from './components/Person.vue'
  8. import {reactive} from 'vue'
  9. import {type Persons} from '@/types'
  10. let x = 9
  11. let personList = reactive<Persons>([
  12. {id:'asudfysafd01',name:'张三',age:18},
  13. {id:'asudfysafd02',name:'李四',age:20},
  14. {id:'asudfysaf)d03',name:'王五',age:22}
  15. ])
  16. </script>

Person.vue`中代码:

  1. <template>
  2. <div class="person">
  3. <ul>
  4. <li v-for="p in list" :key="p.id">
  5. {{p.name}} -- {{p.age}}
  6. </li>
  7. </ul>
  8. </div>
  9. </template>
  10. <script lang="ts" setup name="Person">
  11. import {withDefaults} from 'vue'
  12. import {type Persons} from '@/types'
  13. // 只接收list
  14. // defineProps(['list'])
  15. // 接收list + 限制类型
  16. // defineProps<{list:Persons}>()
  17. // 接收list + 限制类型 + 限制必要性 + 指定默认值
  18. withDefaults(defineProps<{list?:Persons}>(),{
  19. list:()=> [{id:'ausydgyu01',name:'康师傅·王麻子·特仑苏',age:19}]
  20. })
  21. // 接收list,同时将props保存起来
  22. /* let x = defineProps(['list'])
  23. console.log(x.list) */
  24. </script>

3.13. 【生命周期】

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

* 规律:

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

* `Vue2`的生命周期

  > 创建阶段:`beforeCreate`、`created`
  > 挂载阶段:`beforeMount`、`mounted`
  > 更新阶段:`beforeUpdate`、`updated`
  > 销毁阶段:`beforeDestroy`、`destroyed`

* `Vue3`的生命周期

  > 创建阶段:`setup`
  > 挂载阶段:`onBeforeMount`、`onMounted`
  > 更新阶段:`onBeforeUpdate`、`onUpdated`
  > 卸载阶段:`onBeforeUnmount`、`onUnmounted`

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

(注意:在组件中,需要先把子组件挂载完毕,最后再挂载父组件App,因为父组件的最上面就是引用的一些子组件,必须先把所有的子组件挂载完毕以后。才会挂载父组件。有点像深度优先遍历)

代码:

  1. <template>
  2. <div class="person">
  3. <h2>当前求和为:{{ sum }}</h2>
  4. <button @click="add">点我sum+1</button>
  5. </div>
  6. </template>
  7. <script lang="ts" setup name="Person">
  8. import {ref,onBeforeMount,onMounted,onBeforeUpdate,onUpdated,onBeforeUnmount,onUnmounted} from 'vue'
  9. // 数据
  10. let sum = ref(0)
  11. // 方法
  12. function add(){
  13. sum.value += 1
  14. }
  15. // 创建
  16. console.log('创建')
  17. // 挂载前
  18. onBeforeMount(()=>{
  19. console.log('挂载前')
  20. })
  21. // 挂载完毕
  22. onMounted(()=>{
  23. console.log('挂载后');
  24. })
  25. // 更新前
  26. onBeforeUpdate(()=>{
  27. console.log('更新前')
  28. })
  29. // 更新后
  30. onUpdated(()=>{
  31. console.log('更新完毕')
  32. })
  33. // 卸载前
  34. onBeforeUnmount(()=>{
  35. console.log('卸载前')
  36. })
  37. // 卸载完毕
  38. onUnmounted(()=>{
  39. console.log('卸载完毕')
  40. })
  41. </script>

3.14. 【自定义hook】

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

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

`useSum.ts`中内容如下:

  1. import {ref,onMounted} from 'vue'
  2. export default function(){
  3. let sum = ref(0)
  4. const increment = ()=>{
  5. sum.value += 1
  6. }
  7. const decrement = ()=>{
  8. sum.value -= 1
  9. }
  10. onMounted(()=>{
  11. increment()
  12. })
  13. //向外部暴露数据
  14. return {sum,increment,decrement}
  15. }

`useDog.ts`中内容如下:

  1. import {reactive,onMounted} from 'vue'
  2. import axios,{AxiosError} from 'axios'
  3. export default function(){
  4. let dogList = reactive<string[]>([])
  5. // 方法
  6. async function getDog(){
  7. try {
  8. // 发请求
  9. let {data} = await axios.get('https://dog.ceo/api/breed/pembroke/images/random')
  10. // 维护数据
  11. dogList.push(data.message)
  12. } catch (error) {
  13. // 处理错误
  14. const err = <AxiosError>error
  15. console.log(err.message)
  16. }
  17. }
  18. // 挂载钩子
  19. onMounted(()=>{
  20. getDog()
  21. })
  22. //向外部暴露数据
  23. return {dogList,getDog}
  24. }

组件中具体使用:

  1. <template>
  2. <h2>当前求和为:{{sum}}</h2>
  3. <button @click="increment">点我+1</button>
  4. <button @click="decrement">点我-1</button>
  5. <hr>
  6. <img v-for="(u,index) in dogList.urlList" :key="index" :src="(u as string)">
  7. <span v-show="dogList.isLoading">加载中......</span><br>
  8. <button @click="getDog">再来一只狗</button>
  9. </template>
  10. <script lang="ts">
  11. import {defineComponent} from 'vue'
  12. export default defineComponent({
  13. name:'App',
  14. })
  15. </script>
  16. <script setup lang="ts">
  17. import useSum from './hooks/useSum'
  18. import useDog from './hooks/useDog'
  19. let {sum,increment,decrement} = useSum()
  20. let {dogList,getDog} = useDog()
  21. </script>

4. 路由

4.1. 【对路由的理解】

4.2. 【基本切换效果】

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

        npm i vue-router

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

index.ts代码:

  1. // 创建一个路由器并且暴漏出去
  2. // 第一步,引入createRouter
  3. import {createRouter,createWebHistory} from 'vue-router'
  4. // 引入一个个的路由组件
  5. import Home from '@/components/Home.vue'
  6. import About from '@/components/About.vue'
  7. import News from '@/components/News.vue'
  8. // 第二步,创建路由求
  9. const router = createRouter({
  10. history:createWebHistory(),
  11. routes:[ //一个个的路由规则
  12. {
  13. path:'/Home',
  14. component:Home
  15. },
  16. {
  17. path:'/News',
  18. component:News
  19. },
  20. {
  21. path:'/About',
  22. component:About
  23. },
  24. ]
  25. })
  26. // 暴漏出去router
  27. export default router

main.ts代码:

  1. // 引入createApp用于创建应用
  2. import {createApp} from 'vue'
  3. // 引入App根组件
  4. import App from './App.vue'
  5. // 引入路由器
  6. import router from './router'
  7. // 创建一个应用
  8. const app = createApp(App)
  9. // 使用路由器
  10. app.use(router)
  11. // 挂载整个应用到app容器中
  12. 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="xiaozhupeiqi">首页</RouterLink>
  7. <RouterLink to="/news" active-class="xiaozhupeiqi">新闻</RouterLink>
  8. <RouterLink to="/about" active-class="xiaozhupeiqi">关于</RouterLink>
  9. </div>
  10. <!-- 展示区 -->
  11. <div class="main-content">
  12. <RouterView></RouterView>
  13. </div>
  14. </div>
  15. </template>
  16. <script lang="ts" setup name="App">
  17. import {RouterView,RouterLink} from 'vue-router'
  18. </script>

注意:需要在App组件中引入RouterLink,RouterLink是标签,把a标签改成RouterLink,里面的href也改成to。激活高亮是active-class

4.3. 【两个注意点】

> 1. 路由组件通常存放在`pages` 或 `views`文件夹,一般组件通常存放在`components`文件夹。
> 2. 通过点击导航,视觉效果上“消失” 了的路由组件,默认是被卸载掉的,需要的时候再去挂载。

4.4.【路由器工作模式】

1. `history`模式

   > 优点:`URL`更加美观,不带有`#`,更接近传统的网站`URL`。
   > 缺点:后期项目上线,需要服务端配合处理路径问题,否则刷新会有`404`错误。

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

2. `hash`模式

   > 优点:兼容性更好,因为不需要服务器端处理路径。
   > 缺点:`URL`带有`#`不太美观,且在`SEO`优化方面相对较差。

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

 4.5. 【to的两种写法】

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

4.6. 【命名路由】

给路由规则命名:

  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. <!--简化后:直接通过名字跳转(to的对象写法配合name属性) -->
  4. <router-link :to="{name:'guanyu'}">跳转</router-link>

4.7. 【嵌套路由】

1. 编写`News`的子路由:`Detail.vue`

2. 配置路由规则,使用`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

3. 跳转路由(记得要加完整路径):

  1. <router-link to="/news/detail">xxxx</router-link>
  2. <!-- 或 -->
  3. <router-link :to="{path:'/news/detail'}">xxxx</router-link>

4. 记得去`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.8. 【路由传参】

query参数
query参数必须是key=value的形式,多种keyvalue之间还需要一个&做衔接

1.传递参数:

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

2.接收参数:

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

params参数
可以直接写/,但是必须在index.ts规则中占好位,在最后加上?就是配置参数的必要性。可传参可不传
传参时路径不能用path了,只能用name

1.传递参数:

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

2.接收参数:

  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.9. 【路由的props配置】

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

  1. {
  2. name:'xiang',
  3. path:'detail/:id/:title/:content',
  4. component:Detail,
  5. // props的对象写法,作用:把对象中的每一组key-value作为props传给Detail组件
  6. // props:{a:1,b:2,c:3},
  7. // props的布尔值写法,作用:把收到了每一组params参数,作为props传给Detail组件
  8. // props:true
  9. // props的函数写法,作用:把返回的对象中每一组key-value作为props传给Detail组件
  10. props(route){
  11. return route.query
  12. }
  13. }

4.10. 【 replace属性】

1. 作用:控制路由跳转时操作浏览器历史记录的模式。(在历史记录中返回不去)

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

   - ```push```是追加历史记录(默认值)。
   - `replace`是替换当前记录。

3. 开启`replace`模式:
   <RouterLink replace .......>News</RouterLink>

4.11. 【编程式导航】

脱离RouterLink实现路由跳转
使用场景:1.只有符合某些条件再跳转,不是用户一点就跳转,到了时间自动跳,只有登陆成功,跳转到个人中心等
                 2:鼠标滑过一个东西再跳转

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

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

4.12. 【重定向】

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

2.代码(在规则里直接加):

  1. {
  2. path:'/',
  3. redirect:'/home'
  4. }
  5. ]

5. pinia 

5.1【准备一个效果】

vue2中用的是vuex,vue3用的是pinia。就是集中式状态管理

hooks 只是把代码集中在一起,pinia是各个组件之间共享数据

5.2【搭建 pinia 环境】

第一步:`npm i pinia`

第二步:操作`src/main.ts`

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

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

5.3【存储+读取数据】

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

2. 它有三个概念:`state`、`getter`、`action`,相当于组件中的: `data`、 `computed` 和 `methods`。

3. 具体编码:`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. })

4. 具体编码:`src/store/talk.ts`

  1. // 引入defineStore用于创建store
  2. import {defineStore} from 'pinia'
  3. // 定义并暴露一个store
  4. export const useTalkStore = defineStore('talk',{
  5. // 动作
  6. actions:{},
  7. // 状态
  8. state(){
  9. return {
  10. talkList:[
  11. {id:'yuysada01',content:'你今天有点怪,哪里怪?怪好看的!'},
  12. {id:'yuysada02',content:'草莓、蓝莓、蔓越莓,你想我了没?'},
  13. {id:'yuysada03',content:'心里给你留了一块地,我的死心塌地'}
  14. ]
  15. }
  16. },
  17. // 计算
  18. getters:{}
  19. })

5. 组件中使用`state`中的数据

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

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

1. 第一种修改方式,直接修改(与vue2不同,pinia可以直接修改)

countStore.sum = 666

2. 第二种修改方式:批量修改

  1. countStore.$patch({
  2. sum:999,
  3. school:'atguigu'
  4. })

3. 第三种修改方式:借助`action`修改(`action`中可以编写一些业务逻辑)

  1. import { defineStore } from 'pinia'
  2. export const useCountStore = defineStore('count', {
  3. /*/
  4. actions: {
  5. //加
  6. increment(value:number) {
  7. if (this.sum < 10) {
  8. //操作countStore中的sum
  9. this.sum += value
  10. }
  11. },
  12. //减
  13. decrement(value:number){
  14. if(this.sum > 1){
  15. this.sum -= value
  16. }
  17. }
  18. },
  19. /*/
  20. })

4. 组件中调用`action`即可

  1. // 使用countStore
  2. const countStore = useCountStore()
  3. // 调用对应action
  4. countStore.incrementOdd(n.value)

5.5.【storeToRefs】

- 借助`storeToRefs`将`store`中的数据转为`ref`对象,方便在模板中使用。
- 注意:`pinia`提供的`storeToRefs`只会将数据做转换,而不关注’store’里的方法。而`Vue`的`toRefs`会转换`store`中的所有数据包括方法。

 直接解构赋值的话会丢失响应式。 

 用torefs包裹的话,会把store中所有的数据和方法都转化为ref

 pinia`提供的`storeToRefs`只会将数据做转换,而不关注’store’里的方法

示例代码:

  1. <template>
  2. <div class="count">
  3. <h2>当前求和为:{{sum}}</h2>
  4. </div>
  5. </template>
  6. <script setup lang="ts" name="Count">
  7. import { useCountStore } from '@/store/count'
  8. /* 引入storeToRefs */
  9. import { storeToRefs } from 'pinia'
  10. /* 得到countStore */
  11. const countStore = useCountStore()
  12. /* 使用storeToRefs转换countStore,随后解构 */
  13. const {sum} = storeToRefs(countStore)
  14. </script>

5.6.【getters】

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

2. 追加```getters```配置。

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

3. 组件中读取数据:

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

5.7.【$subscribe】

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

  1. talkStore.$subscribe((mutate,state)=>{
  2. console.log('LoveTalk',mutate,state)
  3. localStorage.setItem('talk',JSON.stringify(talkList.value))
  4. })

5.8. 【store组合式写法】

  1. import {defineStore} from 'pinia'
  2. import axios from 'axios'
  3. import {nanoid} from 'nanoid'
  4. import {reactive} from 'vue'
  5. export const useTalkStore = defineStore('talk',()=>{
  6. // talkList就是state
  7. const talkList = reactive(
  8. JSON.parse(localStorage.getItem('talkList') as string) || []
  9. )
  10. // getATalk函数相当于action
  11. async function getATalk(){
  12. // 发请求,下面这行的写法是:连续解构赋值+重命名
  13. let {data:{content:title}} = await axios.get('https://api.uomg.com/api/rand.qinghua?format=json')
  14. // 把请求回来的字符串,包装成一个对象
  15. let obj = {id:nanoid(),title}
  16. // 放到数组中
  17. talkList.unshift(obj)
  18. }
  19. return {talkList,getATalk}
  20. })

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. <script setup lang="ts" name="Father">
  10. import Child from './Child.vue'
  11. import { ref } from "vue";
  12. // 数据
  13. const car = ref('奔驰')
  14. const toy = ref()
  15. // 方法
  16. function getToy(value:string){
  17. toy.value = value
  18. }
  19. </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. <script setup lang="ts" name="Child">
  10. import { ref } from "vue";
  11. const toy = ref('奥特曼')
  12. defineProps(['car','getToy'])
  13. </script>

6.2. 【自定义事件】

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

- 原生事件:
  - 事件名是特定的(`click`、`mosueenter`等等)    
  - 事件对象`$event`: 是包含事件相关信息的对象(`pageX`、`pageY`、`target`、`keyCode`)
- 自定义事件:
  - 事件名是任意名称
  - <strong style="color:red">事件对象`$event`: 是调用`emit`时所提供的数据,可以是任意类型!!!</strong >

3. 示例:

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

        

6.3. 【mitt】

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

 安装`mitt`

                npm i mitt

>1.新建文件:`src\utils\emitter.ts`:

  1. // 引入mitt
  2. import mitt from "mitt";
  3. // 创建emitter
  4. const emitter = mitt()
  5. /*
  6. // 绑定事件
  7. emitter.on('abc',(value)=>{
  8. console.log('abc事件被触发',value)
  9. })
  10. emitter.on('xyz',(value)=>{
  11. console.log('xyz事件被触发',value)
  12. })
  13. setInterval(() => {
  14. // 触发事件
  15. emitter.emit('abc',666)
  16. emitter.emit('xyz',777)
  17. }, 1000);
  18. setTimeout(() => {
  19. // 清理事件
  20. emitter.all.clear()
  21. }, 3000);
  22. */
  23. // 创建并暴露mitt
  24. export default emitter

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

  1. import emitter from "@/utils/emitter";
  2. import { onUnmounted } from "vue";
  3. // 绑定事件
  4. emitter.on('send-toy',(value)=>{
  5. console.log('send-toy事件被触发',value)
  6. })
  7. onUnmounted(()=>{
  8. // 解绑事件
  9. emitter.off('send-toy')
  10. })

>3.提供数据的组件,在合适的时候触发事件

  1. import emitter from "@/utils/emitter";
  2. function sendToy(){
  3. // 触发事件
  4. emitter.emit('send-toy',toy.value)
  5. }

6.4.【v-model】

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

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

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

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

  1. <!-- 组件标签上使用v-model指令 -->
  2. <AtguiguInput v-model="userName"/>
  3. <!-- 组件标签上v-model的本质 -->
  4. <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. <script setup lang="ts" name="AtguiguInput">
  13. // 接收props
  14. defineProps(['modelValue'])
  15. // 声明事件
  16. const emit = defineEmits(['update:model-value'])
  17. </script>

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

  1. <!-- 也可以更换value,例如改成abc-->
  2. <AtguiguInput v-model:abc="userName"/>
  3. <!-- 上面代码的本质如下 -->
  4. <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. <script setup lang="ts" name="AtguiguInput">
  11. // 接收props
  12. defineProps(['abc'])
  13. // 声明事件
  14. const emit = defineEmits(['update:abc'])
  15. </script>

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

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

6.5.【$attrs 】

1. 概述:`$attrs`用于实现当前组件的父组件(相当于一个中间人),向当前组件的子组件通信(祖→孙)。

2. 具体说明:`$attrs`是一个对象,包含所有父组件传入的标签属性。

   >  注意:`$attrs`会自动排除`props`中声明的属性(可以认为声明过的 `props` 被子组件自己“消费”了)

父组件:

  1. <template>
  2. <div class="father">
  3. <h3>父组件</h3>
  4. <Child :a="a" :b="b" :c="c" :d="d" v-bind="{x:100,y:200}" :updateA="updateA"/>
  5. </div>
  6. </template>
  7. <script setup lang="ts" name="Father">
  8. import Child from './Child.vue'
  9. import { ref } from "vue";
  10. let a = ref(1)
  11. let b = ref(2)
  12. let c = ref(3)
  13. let d = ref(4)
  14. function updateA(value){
  15. a.value = value
  16. }
  17. </script>

子组件:

  1. <template>
  2. <div class="child">
  3. <h3>子组件</h3>
  4. <GrandChild v-bind="$attrs"/>
  5. </div>
  6. </template>
  7. <script setup lang="ts" name="Child">
  8. import GrandChild from './GrandChild.vue'
  9. </script>

孙组件:

  1. <template>
  2. <div class="grand-child">
  3. <h3>孙组件</h3>
  4. <h4>a:{{ a }}</h4>
  5. <h4>b:{{ b }}</h4>
  6. <h4>c:{{ c }}</h4>
  7. <h4>d:{{ d }}</h4>
  8. <h4>x:{{ x }}</h4>
  9. <h4>y:{{ y }}</h4>
  10. <button @click="updateA(666)">点我更新A</button>
  11. </div>
  12. </template>
  13. <script setup lang="ts" name="GrandChild">
  14. defineProps(['a','b','c','d','x','y','updateA'])
  15. </script>

6.6. 【$refs、$parent】

1. 概述:

   * `$refs`用于 :父→子。
   * `$parent`用于:子→父。

2. 原理如下:
        `$refs`  :值为对象,包含所有被`ref`属性标识的`DOM`元素或组件实例。
        `$parent`:值为对象,当前组件的父组件实例对象。     

6.7. 【provide、inject】

1. 概述:实现祖孙组件直接通信

2. 具体使用:

   * 在祖先组件中通过`provide`配置向后代组件提供数据
   * 在后代组件中通过`inject`配置来声明接收数据

3. 具体编码:

   【第一步】父组件中,使用`provide`提供数据

  1. <template>
  2. <div class="father">
  3. <h3>父组件</h3>
  4. <h4>资产:{{ money }}</h4>
  5. <h4>汽车:{{ car }}</h4>
  6. <button @click="money += 1">资产+1</button>
  7. <button @click="car.price += 1">汽车价格+1</button>
  8. <Child/>
  9. </div>
  10. </template>
  11. <script setup lang="ts" name="Father">
  12. import Child from './Child.vue'
  13. import { ref,reactive,provide } from "vue";
  14. // 数据
  15. let money = ref(100)
  16. let car = reactive({
  17. brand:'奔驰',
  18. price:100
  19. })
  20. // 用于更新money的方法
  21. function updateMoney(value:number){
  22. money.value += value
  23. }
  24. // 提供数据
  25. provide('moneyContext',{money,updateMoney})
  26. provide('car',car)
  27. </script>

   > 注意:子组件中不用编写任何东西,是不受到任何打扰的

 【第二步】孙组件中使用`inject`配置项接受数据。

  1. <template>
  2. <div class="grand-child">
  3. <h3>我是孙组件</h3>
  4. <h4>资产:{{ money }}</h4>
  5. <h4>汽车:{{ car }}</h4>
  6. <button @click="updateMoney(6)">点我</button>
  7. </div>
  8. </template>
  9. <script setup lang="ts" name="GrandChild">
  10. import { inject } from 'vue';
  11. // 注入数据
  12. let {money,updateMoney} = inject('moneyContext',{money:0,updateMoney:(x:number)=>{}})
  13. let car = inject('car')
  14. </script>

6.8. 【pinia】

参考之前`pinia`部分的讲解

6.9. 【slot】

6.9.1 默认插槽        

  就是在父组件中的子组件的双标签中写入标签,在子组件中需要的地方插入slot,子组件就会接收到父组件中传输过来的数据。

子组件的插槽相当于一个“坑”,父组件的数据相当于“土”。如果在子组件中的插槽有几对,就会显示几次数据。

默认插槽的name是name:dafault

  1. 父组件中:
  2. <Category title="今日热门游戏">
  3. <ul>
  4. <li v-for="g in games" :key="g.id">{{ g.name }}</li>
  5. </ul>
  6. </Category>
  7. 子组件中:
  8. <template>
  9. <div class="item">
  10. <h3>{{ title }}</h3>
  11. <!-- 默认插槽 -->
  12. <slot></slot>
  13. </div>
  14. </template>

6.9.2具名插槽

在子组件中的插槽中给name,父组件中的name与子组件中插槽的name相同,会把数据插入到相同name的插槽中。简单来说,就是把数据插入到指定的位置

在父组件中的组件标签中写入template标签,把要插入子组件的数据放入到template标签中,给template标签name

父组件中:

  1. <Category title="今日热门游戏">
  2. <template v-slot:s1>
  3. <ul>
  4. <li v-for="g in games" :key="g.id">{{ g.name }}</li>
  5. </ul>
  6. </template>
  7. <template #s2>
  8. <a href="">更多</a>
  9. </template>
  10. </Category>

子组件中:

  1. <template>
  2. <div class="item">
  3. <h3>{{ title }}</h3>
  4. <slot name="s1"></slot>
  5. <slot name="s2"></slot>
  6. </div>
  7. </template>

6.9.33. 作用域插槽 

1. 理解:<span style="color:red">数据在组件的自身,但根据数据生成的结构需要组件的使用者来决定。</span>(新闻数据在`News`组件中,但使用数据所遍历出来的结构由`App`组件决定)

2.作用于插槽也有name

3.具体编码:

父组件中:

  1. <Game v-slot="params">
  2. <!-- <Game v-slot:default="params"> -->
  3. <!-- <Game #default="params"> -->
  4. <ul>
  5. <li v-for="g in params.games" :key="g.id">{{ g.name }}</li>
  6. </ul>
  7. </Game>

 子组件中:

  1. <template>
  2. <div class="category">
  3. <h2>今日游戏榜单</h2>
  4. <slot :games="games" a="哈哈"></slot>
  5. </div>
  6. </template>
  7. <script setup lang="ts" name="Category">
  8. import {reactive} from 'vue'
  9. let games = reactive([
  10. {id:'asgdytsa01',name:'英雄联盟'},
  11. {id:'asgdytsa02',name:'王者荣耀'},
  12. {id:'asgdytsa03',name:'红色警戒'},
  13. {id:'asgdytsa04',name:'斗罗大陆'}
  14. ])
  15. </script>

7. 其它 API

7.1.【shallowRef 与 shallowReactive 】

7.1.1shallowRef

1. 作用:创建一个响应式数据,但只对顶层属性进行响应式处理。只能处理第一层的数据。如person.value.name,这就属于二层数据。

2. 用法: 

        let myVar = shallowRef(initialValue);
        如果想关注的是整体修改,用的是shallowRef。用ref会把被包裹住的所有属性都变成响应式的。

3. 特点:只跟踪引用值的变化,不关心值内部的属性变化。

7.1.2shallowReactive

1. 作用:创建一个浅层响应式对象,只会使对象的最顶层属性变成响应式的,对象内部的嵌套属性则不会变成响应式的

2. 用法:

        const myObj = shallowReactive({ ... });

3. 特点:对象的顶层属性是响应式的,但嵌套对象的属性不是。

总结:

7.2.【readonly 与 shallowReadonly】 

7.2.1`readonly`

1. 作用:用于创建一个对象的深只读副本。对数据进行保护,限制只有某人能修改等

2. 用法:

  1. const original = reactive({ ... });
  2. const readOnlyCopy = readonly(original);

3. 特点:

   * 对象的所有嵌套属性都将变为只读。
   * 任何尝试修改这个对象的操作都会被阻止(在开发模式下,还会在控制台中发出警告)。

 4. 应用场景:

   * 创建不可变的状态快照。
   * 保护全局状态或配置不被修改。

7.2.2`shallowReadonly`

1. 作用:与 `readonly` 类似,但只作用于对象的顶层属性。

2. 用法:

  1. const original = reactive({ ... });
  2. const shallowReadOnlyCopy = shallowReadonly(original);

3. 特点:

   * 只将对象的顶层属性设置为只读,对象内部的嵌套属性仍然是可变的。只限制第一层数据,内层数据不限制
   * 适用于只需保护对象顶层属性的场景。

7.3.【toRaw 与 markRaw】

7.3.1`toRaw`

1. 作用:用于获取一个响应式对象的原始对象, `toRaw` 返回的对象不再是响应式的,不会触发视图更新。

2. 具体编码:

  1. import { reactive,toRaw,markRaw,isReactive } from "vue";
  2. /* toRaw */
  3. // 响应式对象
  4. let person = reactive({name:'tony',age:18})
  5. // 原始对象
  6. let rawPerson = toRaw(person)
  7. /* markRaw */
  8. let citysd = markRaw([
  9. {id:'asdda01',name:'北京'},
  10. {id:'asdda02',name:'上海'},
  11. {id:'asdda03',name:'天津'},
  12. {id:'asdda04',name:'重庆'}
  13. ])
  14. // 根据原始对象citys去创建响应式对象citys2 —— 创建失败,因为citys被markRaw标记了
  15. let citys2 = reactive(citys)
  16. console.log(isReactive(person))
  17. console.log(isReactive(rawPerson))
  18. console.log(isReactive(citys))
  19. console.log(isReactive(citys2))

 7.3.2`markRaw`

1. 作用:标记一个对象,使其永远不会变成响应式的。

   > 例如使用`mockjs`时,为了防止误把`mockjs`变为响应式对象,可以使用 `markRaw` 去标记`mockjs`

2. 编码:

  1. /* markRaw */
  2. // 如中国就这些城市,不会发生改变,所以直接使其用markRaw标记
  3. let citys = markRaw([
  4. {id:'asdda01',name:'北京'},
  5. {id:'asdda02',name:'上海'},
  6. {id:'asdda03',name:'天津'},
  7. {id:'asdda04',name:'重庆'}
  8. ])
  9. // 根据原始对象citys去创建响应式对象citys2 —— 创建失败,因为citys被markRaw标记了
  10. let citys2 = reactive(citys)

7.4.【customRef】

作用:创建一个自定义的`ref`,并对其依赖项跟踪和更新触发进行逻辑控制。

实现防抖效果(`useSumRef.ts`):

  1. import { customRef } from "vue";
  2. export default function(initValue:string,delay:number){
  3. // 使用Vue提供的customRef定义响应式数据
  4. let timer:number
  5. // track(跟踪)、trigger(触发)
  6. let msg = customRef((track,trigger)=>{
  7. return {
  8. // get何时调用?—— msg被读取时
  9. get(){
  10. track() //告诉Vue数据msg很重要,你要对msg进行持续关注,一旦msg变化就去更新
  11. return initValue
  12. },
  13. // set何时调用?—— msg被修改时
  14. set(value){
  15. clearTimeout(timer)
  16. timer = setTimeout(() => {
  17. initValue = value
  18. trigger() //通知Vue一下数据msg变化了
  19. }, delay);
  20. }
  21. }
  22. })
  23. return {msg}
  24. }

组件中:

  1. <template>
  2. <div class="app">
  3. <h2>{{ msg }}</h2>
  4. <input type="text" v-model="msg">
  5. </div>
  6. </template>
  7. <script setup lang="ts" name="App">
  8. import {ref} from 'vue'
  9. import useMsgRef from './useMsgRef'
  10. // 使用Vue提供的默认ref定义响应式数据,数据一变,页面就更新
  11. // let msg = ref('你好')
  12. // 使用useMsgRef来定义一个响应式数据且有延迟效果
  13. let {msg} = useMsgRef('你好',2000)
  14. </script>

注意:customRef中最核心的就是track和trigger,track是持续跟踪,trigger是通知你完成了。

8. Vue3新组件 

 8.1. 【Teleport】

- 什么是Teleport?—— Teleport 是一种能够将我们的组件html结构移动到指定位置的技术。

  1. <teleport to='body' > //to就是将下列代码转移到那个里面
  2. <div class="modal" v-show="isShow">
  3. <h2>我是一个弹窗</h2>
  4. <p>我是弹窗中的一些内容</p>
  5. <button @click="isShow = false">关闭弹窗</button>
  6. </div>
  7. </teleport>
  8. ```

8.2. 【Suspense】

-  等待异步组件时渲染一些额外内容,让应用有更好的用户体验 
-  使用步骤: 
   -  异步引入组件
   -  使用`Suspense`包裹组件,并配置好`default` 与 `fallback`

  1. import { defineAsyncComponent,Suspense } from "vue";
  2. const Child = defineAsyncComponent(()=>import('./Child.vue'))
  1. <template>
  2. <div class="app">
  3. <h3>我是App组件</h3>
  4. <Suspense>
  5. <template v-slot:default>
  6. <Child/>
  7. </template>
  8. <template v-slot:fallback>
  9. <h3>加载中.......</h3>
  10. </template>
  11. </Suspense>
  12. </div>
  13. </template>

8.3.【全局API转移到应用对象】

- `app.component`
- `app.config`
- `app.directive`
- `app.mount`
- `app.unmount`
- `app.use`

就是将vue2中原来是Vue.xxx的改成app.xxx

 8.4.【其他】

(干开发的最好去官网看看vue3中的非兼容性改变,面试问)

- 过渡类名 `v-enter` 修改为 `v-enter-from`、过渡类名 `v-leave` 修改为 `v-leave-from`。

- `keyCode` 作为 `v-on` 修饰符的支持。

- `v-model` 指令在组件上的使用已经被重新设计,替换掉了 `v-bind.sync。`

- `v-if` 和 `v-for` 在同一个元素身上使用时的优先级发生了变化。

- 移除了`$on`、`$off` 和 `$once` 实例方法。

- 移除了过滤器 `filter`。

- 移除了`$children` 实例 `propert`。

  ......
                                    (完结撒花,共勉,一起加油)

声明:本文内容由网友自发贡献,不代表【wpsshop博客】立场,版权归原作者所有,本站不承担相应法律责任。如您发现有侵权的内容,请联系我们。转载请注明出处:https://www.wpsshop.cn/w/不正经/article/detail/441058
推荐阅读
相关标签
  

闽ICP备14008679号