文章简介:
(1)简介:
在 Vue3 中编码规范如下:
- 编码语言: JavaScript
- 代码风格: 组合式API选项式、API
- 简写形式: setup语法糖
(2)复习内容:
1.核心: ref、reactive、computed、watch、生命周期
2.常用: hooks、自定义ref、路由、pinia、mitt···
3.面试: 组件通信、响应式相关API.
目录
①监视 ref 定义的【基本类型】数据:直接写数据名即可,监视的是其 value 值的改变
②监视 ref 定义的【对象类型】数据: 直接写数据名,监视的是对象的【地址值】,若想监视对象内部的数据,要手动开启深度监视 deep。
③定义的 reactive 【对象类型】数据,且默认开启了深度监视。
④监视 ref 或 reactive 定义的【对象类型】数据中的某个属性
15.6 $refs 父——>子、$parent 子——>父
17.1 shallowRef 与 shallowReactive
1.Vue3 简介
2020年9月18日,Vue.js 发布版3.0版本,代号: Onepiece(海贼王)
经历了: 4800+次提交、40+个RFC、600+次PR、300+贡献者
官方发版地址: Release v3.0.0 One Piece·vuejs/core
截止2023年10月,最新的公开版本为: 3.3.4
1.1.性能的提升
- 打包大小减少 41%
- 初次渲染快 55%,更新渲染快 133%
- 内存减少 54%
1.2. 源码的升级
- 使用 Proxy 代替 defineProperty实现响应式。
- 重写虚拟 DOM 的实现和 Tree-shaking
1.3.拥抱TypeScript
- Vue3 可以更好的支持 Typescript
1.4.新的特性
(1)Cqmposition API (组合 API )
- setup
- ref与reactive
- computed与watch
(2)新的内置组件:
- Fragment
- Teleport
- Suspense
(3)其他改变:
- 新的生命周期钩子
- data 选项应始终被声明为一个函数
- 移除 keycode 支持作为 v-on 的修饰符
2.创建 Vue3工程
2.1. 基于 vue-cli 创建
备注:目前 vue-cli 已处于维护模式,官方推荐基于 Vite 创建项目。
2.2. 基于 vite 创建 (🌟推荐)
vite 是新一代前端构建工具,官网地址:https://vitejs.cn,vite的优势如下:
轻量快速的热重载( HMR )能实现极速的服务启动。
对 Typescript、JSX、css 等支持开箱即用.
真正的按需编译,,不再等待整个应用编译完成。
webpack 构建与 vite 构建对比图如下:
官方文档如下:(需要配置node环境)
## 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
createApp 创建应用,每一个应用都有一个根组件,根组件就是App。创建完成后将App挂载到id为app的div盒子里。
(必写:引入的盒子以及main.js)
⭕总结:
- vite 项目中,index.html 是项目的入口文件,在项目最外层。
- 加载 index.html 后,Vite 解析<script type="module” src="xxx”>指向的 Javascript 。
- Vue3 中是通过 createApp 函数创建一个应用实例。
0ptionsAPl 与 CompositionAPI
Vue2 的 API 设计是 options(配置)风格的。
Vue3 的API 设计是 Composition(组合)风格的(支持多个单标签)。
选项式语言 ——> 学习配置项
‼️Options API 的弊端
Options 类型的 API,数据、方法、计 算属性等,是分散在: data、methods 、
computed 中的,若想新增或者修改一个需求,就需要分别修改: data、methods、computed不便于维护和复用。(请参考以下这篇文章:)
3.拉开序幕的 setup
setup 概述
setup 是 vue3 中一个新的配置项,值是一个函数,它是 composition API “表演的舞台”,组件中所用到的: 数据、方法、计算属性、监视.…..等等,均配置在 setup 中。
特点如下:
- setup 函数返回的对象中的内容,可直接在模板中使用。
- setup 中访问this是undefined
- setup 函数会在 beforecreate之前调用,,它是“领先”所有钩子执行的,
‼️Vue3中setup函数中的this是undefined,Vue3中已经弱化this
setup生命周期早于beforeCreate
<script lang="ts">
export default{
setup(){
//return function(){
//} 相当于
// return ()=>{ return } 可简写为:
return () =>
}
}
</script>
‼️setup的返回可以是一个渲染函数
选项式写法可以与setup共存,并且data,methods 可以读到setup里面的内容,但setup不能读到data和methods的内容,因为setup是最早的生命周期钩子。
3.1 setup 语法糖
setup 函数有一个语法糖,这个语法糖,可以让我们把 setup 独立出去,代码如下:
扩展: 上述代码,还需要编写一个不写 setup 的script 标签,去指定组件名字,比较麻烦,我们可以借助 vite 中的插件简化
1.第一步: npm i vite-plugin-vue-setup-extend -D
2.第二步: vite.config.ts
npm i vite-plugin-vue-setup-extend -D
修改组件名组件
4. ref 创建:基本类型的响应式数据
作用: 定义响应式变量。
语法: let xxx= ref(初始值)
返回值: 一个 RefImpl 的实例对象,简称 ref对象 或 ref, ref 对象的 value 属性是响应式。
注意点:
- Js 中操作数据需要: xxx.value,但模板中不需要.value,直接使用即可
- 对于 let name = ref('张三')来说,,name 不是响应式的 name.value 是响应式的,
- ref ——> 基本类型数据、对象类型响应式数据
- reactive ——> (只能定义)对象类型响应式数据
5. reactive创建 对象类型的响应式数据
- 作用: 定义一个响应式对象(基本类型不要用它,要用ref,否则报错)
- 语法: let 响应式对象 = reactive(源对象)。
- 返回值: 一个 Proxy的实例对象,简称:响应式对象。
- 注意点: reactive 定义的响应式数据是“深层次”的。
对比 ref 声明响应式数组(ref 对象类型的value 底层还是用的 reative)
5.1 ref 对比 reactive
- 宏观角度看:
- ref 用来定义:基本类型数据、对象类型数据;
- reactive 用来定义: 对象类型数据。
- 区别:
- ref 创建的变量必须使用 .value(可以使用 volar 插件自动添加.value)。
- reactive 重新分配一个新对象,会失去响应式(可以使用 Object.assign 去整体替换)
- 使用原则:
- 若需要一个基本类型的响应式数据,必须使用 ref
- 若需要一个响应式对象,层级不深,ref、reactive 都可以。
- 若需要一个响应式对象,且层级较深,推荐使用 reactive。
Volar 开启自动添加 .value
- 使用原则:
1.若需要一个基本类型的响应式数据,必须使用ref
2.若需要一个响应对象层级不深,ref、reactive 都可以。
3.若需要一个响应式对象,且层级较深,推荐使用 reactive。
5.2 toRefs 与 toRef
作用: 将一个响应式对象中的每一个属性,转换为 ref 对象。
备注: toRefs 与 toRef 功能一致,但 toRefs 可以批量转换。
6. computed 计算属性
作用: 根据已有数据计算出新数据(和 vue2 中的 computed 作用一致)
7. watch 监视
- 作用: 监视数据的变化(和 vue2 中的 watch 作用一致)
- 特点: vue3 中的 watch 只能监视以下四种数据
- ref 定义的数据。
- reactive 定义的数据。
- 函数返回一个值(getter函数)。
- 一个包含上述内容的数组。
我们在 vue3 中使用 watch 的时候,通常会遇到以下几种情况:
①监视 ref 定义的【基本类型】数据:直接写数据名即可,监视的是其 value 值的改变
②监视 ref 定义的【对象类型】数据: 直接写数据名,监视的是对象的【地址值】,若想监视对象内部的数据,要手动开启深度监视 deep。
⭕注意:
- 若修改的是 ref 定义的对象中的属性,newValue 和 oldValue 都是新值,因为它们是同一个对象。
- 若修改整个 ref 定义的对象,newValue 是新值,oldValue 是旧值,因为不是同一个对象了。
③定义的 reactive 【对象类型】数据,且默认开启了深度监视。
即隐式开启深层监听且关闭不掉。
④监视 ref 或 reactive 定义的【对象类型】数据中的某个属性
⭕注意点如下:
1.若该属性值 不是【对象类型】,需要写成函数形式。
2.若该属性值是 依然是【对象类型】,可直接编,也可写成函数,不过建议写成函数。
结论: 监视的要是对象里的属性,那么最好写函数式
注意点: 若是对象监视的是地址值,需要关注对象内部,需要手动开启深度监视。
⑤监视上述的多个数据
7.1 watchEffect
官网: 立即运行一个函数,同时响应式地追踪其依赖,并在依赖更改时重新执行该函数。
- watch 对比 watchEffect
- 都能监听响应式数据的变化,不同的是监听数据变化的方式不同
- watch: 要明确指出监视的数据
- watchEffect: 不用明确指出监视的数据(函数中用到哪些属性,那就监视哪些属性)
8. 标签的 ref 属性
作用:用于注册模板引用。
- 用在普通 DOM 标签上,获取的是 DOM 节点
- 用在组件标签上,获取的是组件实例对象。
加在组件标签上时,获取到的就是组件的实例。
9. TS 接口 - 泛型 - 自定义类型
10. 组件 props 的使用
父组件
子组件
宏函数:不用引入,可以直接使用
11. 生命周期
- 概念: Vue 组件实例在创建时要经历一系列的初始化步骤,在此过程中 Vue 会在合适的时机,调用特定的函数,从而让开发者有机会在特定阶段运行自己的代码,这些特定的函数统称为:生命周期钩子
- 规律: 生命周期整体分为四个阶段,分别是: 创建、挂载、更新、销毁,每个阶段都有两个钩子,一前一后。
11.1 Vue2 生命周期
阶段:
- 创建 ( 创建前 beforeCreate,创建完毕 created )
- 挂载(挂载前 beforeMount,挂载完毕 mounted)
- 更新(更新前 beforeUpdate,更新完毕 updated)
- 销毁(销毁前 beforeDestroy,销毁完毕 destroyed)
11.2 Vue3 生命周期
阶段:
- 创建 (setup)
- 挂载(挂载前 onBeforeMount,挂载后 onMounted)
- 更新(更新前 onBeforeUpdate,更新后 onUpdated)
- 卸载(卸载前 onBeforeUnmount,卸载完毕 onUnmounted)
挂载前调用 onBeforeMount 指定的函数 onBeforeMount(()=>{})
常用的钩子: onMounted (挂载完毕)、onupdated (更新完毕)、onBeforeunmount (卸载之前)
子组件会比父组件先挂在解析,挂载完毕。
即APP创建 -> APP挂载前 -> 子组件创建、挂载、挂载完毕 -> App挂载完毕
12. 自定义 Hooks
Hooks: 数据与方法集中管理(定义一个hooks的文件夹,往里面写ts)
对比 使用 Hooks:
Hooks 支持生命周期钩子 。当有Hooks,组合式API才有意义!
13. 🌟路由
形成路由:
1.导航区、展示区
2.设置路由器
3.制定路由的具体规则(什么路径,对应着什么组件)4.形成一个一个的【???.vue】class.vue Subject.vue
13.1 路由基本切换
配置路由工作环境:
⭕路由配置注意点
- 路由组件 通常存放在 pages 或 views (视图)文件夹,一般组件 通常存放在 components 文件夹
- 通过点击导航,视觉效果上“消失”了的路由组件,默认是被卸载掉的,需要的时候再去挂载
13.2 路由器工作模式
(1)history 模式
- Vue2: mode:'history'
- Vue3: history:createWebHistory()
- React: BrowserRouter
- 优点: URL 更加美观, 不带有 #,更接近传统的网站 URL
- 缺点: 后期项目上线,需要服务端配合处理路径问题,否则刷新会有 404 错误
const router = createRouter({
history:createWebHistory(),//history模式
})
(2)hash 模式
- 优点:兼容性更好,因为不需要服务器端处理路径。
- 缺点: URL 带有 # 不太美观,且在 SEO 优化方面相对较差
const router=createRouter({
history:createWebHashHistory(),//hash模式
})
‼️to的两种写法
<!--第一种:to的字符串写法 -->
<router-link active-class="active" to="/home">主页</router-link>
<!--第二种:to的对象写法-->
<router-link active-class="active" :to="{path:'/home'}">Home</router-link>
进行路由命名:
作用: 可以简化路由跳转及传参
配置名字后可以通过对象里面的名字进行跳转
13.3 嵌套路由
声明子级路由(注意:子级不用写 / )
13.4 路由 query 参数
模板字符串
解构 route响应式对象,那么必定丢失响应式 ——> 需要添加上toRefs
13.5 路由 params 参数
占位但不想传值就在值后面加上?
进行解构:
⭕注意点:
- 传递 params 参数时,若使用 to 的对象写法,必须使用 name 配置项,不能用 path传递
- 传递 params 参数时,需要提前在规则中占位。占位规则不能是对象和数组
13.6 路由 props 配置
作用: 让路由组件更方便的收到参数(可以将路由参数作为 props 传给组件)
13.7 路由 replace 属性
1.作用: 控制路由跳转时操作浏览器历史记录的模式
2.浏览器的历史记录有两种写入方式: 分别为 push 和 replace :
- push 是追加历史记录(默认值)。
- replace 是替换当前记录。
3.RouterLink 开启 replace 模式:
<RouterLink replace>News</RouterLink>
13.8 🌟编程式导航
路由组件的两个重要的属性: $route 和 $router 变成了两个 hooks
编程式路由导航:脱离<RouterLink>实现路由跳转
如果用的 replace 则没有游览记录
重定向
让指定路径重新定位到另一个路径
14. Pinia 集中式状态(数据)管理
- 集中式:将状态,数据存放到容器中(定义一个store文件夹)
- 分布式:服务器集群
共享数据 Demo:
14.1 搭建 Pinia 环境,储存+读取数据
安装命令行: npm i pinia
state 必须写成函数并返回一个对象
- Store 是一个保存: 状态、业务逻辑 的实体,每个组件都可以读取、写入它
- 它有三个概念:state、getter、action,相当于组件中的: data、computed,和methods ,
- 具体编码:src/store/count.ts
reactive 定义的响应式对象 在访问其内部属性时,自动解包
14.2 修改数据(三种方式)
修改方式:
①直接修改
countStore.sum=666
②批量修改(数据较多时)
countStore.$patch({
sum:999,
school:'atguigu'
}
注意 spatch 只发生了一次变更
③借助 action 修改( action 中可以编写一些业务逻辑 )
如果多页面用到 actions 的逻辑就可以达到复用的效果
14.3 storeToRefs
- 借助 storeToRefs将 store 中的数据转为 ref 对象,方便在模板中使用
- 注意: pinia 提供的 storeToRefs 只会将数据做转换,而 Vue 的 toRefs 会转换 store 中数据。
store里的count.ts
Count.vue
方法是调用不是修改的
因此 storeToRefs 只关注 store 里的数据,不关注方法
14.4 getters
- 概念: 当 state 中的数据,需要经过处理后再使用时,可以使用 getters 配置。
- 追加 getters 配置。
14.5 $subscribe 订阅,监视修改
序列化与反序列化,深度克隆的方式之一
loveTalk.ts
14.6 store 组合式写法
组合式无需那么多结构,较为清晰
对比之前选项式 actions与state:
15.组件通信
Vue3 组件通信和 Vue2 的区别:
- 移出事件总线,使用 mitt 代替。
- vuex 换成了 pinia。
- 把.sync 优化到了 v-model里面了
- 把 $listeners 所有的东西,合并到 $attrs 中了
- $children 被砍掉了
15.1 props
概述: props 是使用频率最高的一种通信方式,常用与: 父↔子。
- 若 父传子: 属性值是 非函数。
- 若 子传父: 属性值是 函数。
15.2 自定义事件 $event
- $event 是特殊占位符,事件对象。
- ref 定义的数据在模板中使用的时候可以不写.value
自定义事件 ——> 子传父
‼️kebab-case事件名
15.3 miit 任意组件通信
1.pubsub
2.$bus
3.mitt
- 接收数据的: 提前绑定好事件( pubsub 提前订阅消息)
- 提供数据的: 在合适的时候触发事件(发布消息)
创建 utils 文件夹
- setInterval() 方法会按照指定的时间间隔重复执行指定的函数,直到被取消或页面被关闭。换句话说,它会创建一个不间断的循环定时器。
- setTimeout() 方法会在指定的时间间隔后执行一次指定的函数,只执行一次。如果希望函数在一段时间后执行一次,而不需要重复执行,就可以使用 setTimeout()
15.4 v-model 组件通信
UI组件库大量通过 v-model 进行通信(v-mode是 :value 和 @input 的简写)
$event到底是啥?啥时候能.target
- 对于原生事件,$event 就是 事件对象 ==> 能.target
- 对于 自定义事件,$event 就是 触发事件时,所传递的数据 ==>不能.target
细节写法:也可以更换 value,例如改成任意名称
即如果 value 可以更换,那么就可以在组件标签上多次使用 v-model
<zujian v-model:abc='userName' v-model:xyz='password'>
15.5 $attrs 祖——>孙
概述: $attrs用于实现当前组件的父组件,向当前组件的子组件通信(祖→孙但还需要子组件)
具体说明: $attrs是一个对象,包含所有父组件传入的标签属性
注意: $attrs 会自动排除 props 中声明的属性(可以认为声明过的 props 被子组件自己“消费”了)
父组件传值但子组件未声明接收的值便在 $attrs 中
15.6 $refs 父——>子、$parent 子——>父
原理如下:
属性 | 说明 |
$refs | 值为对象,包含所有被 ref 属性标识的 DOM 元素或组件实例。 |
$parent | 值为对象,当前组件的父组件实例对象。 |
父组件
- .key就是去对象里找key这个键名,[key]把key做变量用,找的键名是key的变量值
- refs 是响应式对象,不需要.value,会默认解包,底层会自动读取value属性(parent 也同理)
15.7 provide、inject(祖孙通信)
1.概述: 实现祖孙组件直接通信(无需子组件)
2.具体使用:
- 在祖先组件中通过 provide 配置向后代组件提供数据
- 在后代组件中通过 inject 配置来声明接收数据
money 不能.value,负责就是将数据传递过去而已,而非一个 ref 对象
16. 插槽
16.1 默认插槽 default
实现案例:
16.2 具名插槽 v-slot
注意:v-slot 只能用到组件标签上或者是 template标签上
v-slot 简写为 #
16.3 作用域插槽
- UI组件库 table 传给组件全部的数据,子组件传过来某一个单元格的数据
- 数据在子那边,根据数据生成的结构,却由父亲决定。
- 压岁钱在孩子那,但根据压岁钱买的东西,却由父亲决定。
⭕注意:作用域插槽也可以有名字
17. 其他API
17.1 shallowRef 与 shallowReactive
浅层次
(1)shallowRef
- 作用: 创建一个响应式数据,但只对顶层属性进行响应式处理,
- 用法:
let myVar=shallowRef(initialValue)
- 特点: 只跟踪引用值的变化,不关心值内部的属性变化。
(2)shallowReactive
作用: 创建一个浅层响应式对象,只会使对象的最顶层属性变成响应式的,对象内部的嵌套属性则不会变成响应式的
用法:
const myObj=shallowReactive({...});
特点: 对象的顶层属性是响应式的,但嵌套对象的属性不是。
总结:通过使用 shallowRef()和 shallowReactive()来绕开深度响应。浅层式 API 创建的状态只在其顶层是响应式的,对所有深层的对象不会做任何处理,避免了对每一个内部属性做响应式所带来的性能成本,这使得属性的访问变得更快,可提升性能。
17.2 readonly与shallowReadonly
(1)readonly
1.作用: 用于创建一个对象的深只读副本
2.用法:
const original=reactive({...});
const readOnlyCopy=readonly(original);
3.特点:
对象的所有嵌套属性都将变为只读。。
任何尝试修改这个对象的操作都会被阻止(在开发模式下,还会在控制台中发出警告)。
4.应用场景:
创建不可变的状态快照
保护全局状态或配置不被修改。
(2)shallowReadonly
1.作用: 与 readonly 类似,但只作用于对象的顶层属性,
2.用法:
const original=reactive({...});
const shallowReadOnlyCopy=shallowReadonly(original),
3.特点:
- 只将对象的顶层属性设置为只读,对象内部的嵌套属性仍然是可变的。
- 适用于只需保护对象顶层属性的场景。
17.3 toRaw与markRaw
(1)toRaw
作用: 用于获取一个响应式对象的原始对象,toRaw 返回的对象不再是响应式的,不会触发视图更新。
- 官网描述: 这是一个可以用于临时读取而不引起代理访问/跟踪开销,或是写入而不触发更改的特殊方法。不建议保存对原始对象的持久引用,请谨慎使用。
- 何时使用?-- 在需要将响应式对象传递给非 vue 的库或外部系统时,使用 toRaw 可以确保它们收到的是普通对象
(2)markRaw
作用: 标记一个对象,使其永远不会变成响应式的。
例如使用 mockjs 时,为了防止误把 mockjs 变为响应式对象,可以使用 markRaw 去标记 mockjs
17.4 customRef 自定义 ref
作用: 创建一个自定义的ref ,并对其依赖项跟踪和更新触发进行逻辑控制。实现防抖效果(useSumRef.ts)
17.5 Teleport
什么是Teleport(传送门)?
-- Teleport 是一种能够将我们的组件html结构移动到指定位置的技术,
当父盒子使用了 CSS 中的 filter 属性时,会导致其子元素中使用了 position: fixed 的定位属性失效的原因是因为 filter 属性会创建一个层叠上下文(stacking context),而定位属性 fixed 是相对于视口(viewport)进行定位的。
在层叠上下文中,position: fixed 的元素会相对于最近的具有滚动条的祖先元素定位,而不是整个视口。当父盒子使用了 filter 属性时,会创建一个新的层叠上下文,导致其子元素的定位方式发生改变,造成 position: fixed 失效。
为了解决这个问题,可以将使用了 filter 属性的父盒子的定位属性修改为 position: relative,或者调整页面布局,避免使用 filter 属性影响到 position: fixed 的元素。
17.6 Suspense
等待异步组件时渲染一些额外内容,让应用有更好的用户体验
使用步骤:
- 异步引入组件。
- 使用 Suspense 包裹组件,并配置好 default 与 fallback
17.7 全局API 转移到 应用对象
app 即Vue2 中的 vm
- app .component 全局组件
- app.config 全局配置对象
- app.directive 全局组件
- app.mount
- app.unmount
- app.use
非兼容改变:
- 过渡类名 v-enter 修改为 v-enter-from、过渡类名 v-leave 修改为 v-leave-from 。
- keycode 作为 v-on 修饰符的支持。
- v-model 指令在组件上的使用已经被重新设计,替换掉了v-bind.sync。
- v-if 和 v-for 在同一个元素身上使用时的优先级发生了变化。v-for等级更高
- 移除了 Son、$off 和 Sonce 实例方法。
- 移除了过滤器 filter 。
- 移除了 $children 实例 propert。