核心:ref、reactive、computed、watch、生命周期…
Vue3相比Vue2的区别:
- 性能的提升
- 源码的升级
- 使用
Proxy
代替defineProperty
实现响应式 - 重写
虚拟DOM
的实现和Tree-Shaking
- 使用
- 可以更好的支持TS
- 新的特性
Composition API
(组合API):setup
ref
与reactive
computed
与watch
- 新的内置组件
Fragment
Teleport
Suspense
- 其他改变:
- 新的生命周期钩子
data
选项应始终被声明为一个函数- 移除
keyCode
支持v-on
的修饰符
1.Options API与Composition API
选项式API即Vue2中的写法,通过data、methods等分别定义。
组合式API即Vue3中的写法,通过写在set up
里
data、methods 和 set up 是可以同时存在的
前者可以
this.
读出set up里的东西,而反之不行。 这是因为set up 在beforeCreated之前执行,且set up中this指向undefined。基本在vue3的setup中,都不能去碰this
2.ref与reactive
set up
语法糖写法<script setup>
在script标签中通过name=
指定组件名字,先要安装一个插件vite-plugin-vue-setup-extend
基本数据类型的响应式写法:
1
2
3
4
5
6
7
8<script lang="ts" setup>
import {ref} from 'vue'
let name = ref('张三')
function changeName(){
name.value = 'zhang-san'
}
</script>引用类型的响应式写法
1
2import {reactive} from 'vue'
let car = reactive({brand:'奔驰',price:100})
ref可以定义基本、引用类型
reactive只能定义引用类型
但是其实ref处理引用类型数据,用的是reactive去处理
使用原则如下:
- 基本类型的响应数据,必须用
ref
- 引用类型的响应式数据,且层级不深,
ref、reactive
都可以 - 引用类型的响应式数据,且层级深,推荐用
reactive
- 基本类型的响应数据,必须用
注意:
1.如果从一个响应式的对象上,去直接解构属性,属性将丢失响应式
如:
1
2
3import {reactive} from 'vue'
let car = reactive({brand:'奔驰',price:100})
let {brand,price} = car //brand和price会丢失响应式2.reactive定义的对象,不能直接修改
1
2
3
4
5
6
7
8
9let car = reactive({brand:'奔驰',price: 80})
//要修改car,不能直接修改
car = {brand:"宝马",price:100}
//或
car = reactive({brand:"宝马",price:100})
//都不行
//得这样修改
Object.assign(car, {brand:"宝马",price:100})
3.toRefs与toRef,storeToRefs
都是将一个响应式的数据,解构出来的东西,仍具响应式。
1 | import {reactive,toRefs,toRef} from 'vue' |
- storeToRefs:将store中的state属性,进行解构后仍然有响应式,不会对方法进行ref包裹
4.computed
页面中多次用某个值,只计算一次
vue3中的写法
1 | // 写法一:这么定义的fullName是一个只读的计算属性,不能对fullName进行修改 |
5.watch
Vue3中的watch只能监听以下四种数据:
ref
定义的数据reactive
定义的数据- 函数返回一个值
- 一个包含上述内容的数组
watch(被监视的数据,监视的回调,配置对象(deep,immediate...))
对于四种情况,监视写法如下:
1 | //1.监视ref定义的基本类型数据 |
6.watchEffect
立即运行一个函数,同时响应式地追踪其依赖,并在依赖更改时重新执行该函数
watch
:要指明监视的数据
watchEffect
:不用明确指明,自动追踪
1 | watchEffect(()=>{ |
7.标签的ref属性
用于注册模板引用
- 用在普通DOM标签上,获取的是DOM节点
- 用在组件标签上,获取的是组件实例对象
8.props
父组件给子组件传值常用,子给父传数据时,需要父先自定义一个事件。具体例子在13节
1 | <!--父组件给子组件Person传list--> |
9.生命周期函数
和Vue2相似,都是4个大阶段:创建、挂载、更新、销毁
- 创建:
setup
- 挂载:
onBeforeMount
,onMounted
- 更新:
onBeforeUpdate
,onUpdated
- 销毁:
onBeforeUnmount
,onUnmounted
10.hooks
类似Vue2里的mixin
,本质就是将模块化开发
发挥到极致,将一些ts封装成xxx.ts文件,这些文件命名为useXXX,比如useDog.ts
正是因为hooks,Composition才发挥出了威力
这些hooks文件里,支持写数据、方法,还支持写钩子函数
1 | // hooks/useDog.ts |
11.路由
点击导航,视觉上消失的路由组件,默认是被销毁了,需要的时候再去挂载
vue2中通过keep-alive缓存组件
12.Pinia
vue2中用的是vuex,vue3中用的pinia
一个使用示例:
1 | //count.ts |
1 | //vue组件中 |
pinia中也有getters
,对数据进行计算处理,像computed
pinia中有$subscribe
,像watch
13.组件通信
1.props
父给子传递car,子给父传递toy
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29<h>父组件</h>
<h4>汽车:{{car}}</h4>
<Child :car="car" :sendToy="getToy" />
<h4 v-show="toy">子给父的玩具:{{toy}}</h4>
<script lang="ts" setup>
import Child from './Child.vue'
import {ref} from 'vue'
//数据
let car = ref('奔驰')
//方法
function getToy(value:string){
console.log('父拿到子传来的数据',value)
}
</script>
<h>子组件</h>
<h4>玩具:{{toy}}</h4>
<h4>父给的汽车:{{car}}</h4>
<button @click="sendToy(toy)">把玩具toy传给父</button>
<script lang="ts" setup>
import {ref} from 'vue'
//数据
let toy = ref('奥特曼')
//声明接收props
defineProps(['car','sendToy'])
</script>2.自定义事件
子声明一个事件,并且在合适时机去触发;父给子组件绑定该事件,从而实现子给父传值
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27<h>子组件</h>
<h4>玩具:{{toy}}</h4>
<button @click="emit('send-toy',toy)">把玩具toy传给父</button>
<script lang="ts" setup>
import {ref} from 'vue'
//数据
let toy = ref('奥特曼')
//声明事件,合适时机触发
const emit = defineEmits(['send-toy'])
</script>
<h>父组件</h>
<Child @send-toy="saveToy" /> <!--绑定该事件-->
<h4 v-show="toy">子给父的玩具:{{toy}}</h4>
<script lang="ts" setup>
import Child from './Child.vue'
import {ref} from 'vue'
let toy = ref('')
//方法
function saveToy(value:string){
console.log('父拿到子的数据',value)
toy.value = value
}
</script>3.mitt
像vue2中的pubsub、$bus还有vue3中的mitt,原理都是 提供数据的:在合适时机触发事件;接收数据的:提前绑定好事件。可以实现任意组件间通信
举一个兄弟组件通信的例子:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17<h>哥哥</h>
<button @click="emitter.emit('send-toy',toy)"></button> <!--触发事件-->
<h>弟弟</h>
<h4>
哥哥给的玩具{{toy}}
</h4>
<script>
//给emitter绑定send-toy事件
emitter.on('send-toy',(value:any)=>{
toy.value = value
})
//组件销毁卸载后,解绑事件
onUnmounted(()=>{
emitter.off('send-toy')
})
</script>4.v-model
实际很少用,但是UI组件库会发现大量使用。
面试可以吹一波,v-model原理解析:
v-model用在html标签上时
1
2
3<input v-model="username">
其实就是下面这句:
<input :value="username" @input="username = (<HTMLInputElement>$event.target).value">v-model用在组件上
1
2
3
4
5
6
7
8
9
10
11<SuInput v-model="username"></SuInput>
翻译:
<SuInput :modelValue="username" @update:modelValue="username = $event"></SuInput> <!--vuu3写法-->
<SuInput :value="username" @input="username = $event"></SuInput> <!--vue2写法-->
自定义的组件SuInput中应该写:
<input :value="modelValue" @input="emit('update:modelValue',(<HTMLInputElement>$event.target).value)"/>
<script>
defineProps(['modelValue'])
const emit = defineEmits(['update:modelValue'])
</script>$event是什么?
- 对于原生事件,
$event
就是事件对象==》能.target
- 对于自定义事件,
$event
就是触发事件时,所传递的数据==》不能.target
- 对于原生事件,
5.$attrs
实现当前组件的父组件,向当前组件的子组件通信。(祖–>孙,孙–>祖)
1
2
3
4
5
6
7
8
9
10
11<h>祖先</h>
<Child :a="a" :updateA="updateA"></Child>
<h>孩子</h>
<GrandChild v-bind="$attrs"></GrandChild>
<h>孙子</h>
<button @click="updateA(6)"></button>
<script>
defineProps(['a','updateA'])
</script>6.$refs、$parent
$refs
:父–》子$parent
:子–》父
7.provide、inject
任意组件的通信
provide
向后代提供数据,inject
拿到数据子给父传递的时候通过触发事件,绑定事件的方式。父中定义一个事件并将该事件provide给子组件,子组件inject接收事件并在合适时机触发该事件
1 | provide('qian', money) //父 向后代提供数据 |
pinia
slot
14.其他API
shallowRef()
和shallowReacrive()
绕开深度响应,浅层式API创建的状态只在其
顶层是响应式
,对所有深层的对象不会做任何处理,避免了对每一个内部属性做响应式所带来的性能成本,这使得属性的访问变得更快,提升性能.readonly
和shallowReadonly
readonly:创建一个对象的深只读副本
shallowReadonly:只作用于对象的顶层属性
toRaw
和markRaw
toRaw:获取一个响应式对象的原始对象
markRaw:标记一个对象,使其永远不会变成响应式
customRef
自定义Ref
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20import {customRef} from 'vue'
let initValue = 'hello'
let timer:number
// track(跟踪) trigger(触发)
let msg = customRef((track,trigger)=>{
return {
get(){
track() //告诉Vue数据msg很重要,你要对msg进行持续关注,一旦msg变化就去更新
return initValue
},
set(){
clearTimeout(timer)
timer = setTimeout(()=>{
initValue = value
triggeer() //通知Vue,数据msg变化了
})
}
}
})Teleport
将组件
html结构
移动到指定位置的技术1
2
3
4
5
6
7<teleport to='body'>
<div class="modal" v-show="isShow">
<h2>弹窗标题</h2>
<p>弹窗内容</p>
<button @click="isShow = false">关闭弹窗</button>
</div>
</teleport>Suspense
等待异步组件时渲染一些额外的内容
- 异步引入组件
- 使用
Suspense
包裹组件,并配置好default
与fallback
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16<div class = "app">
<h3>我是APP组件</h3>
<Suspense>
<template v-slot:default>
<Child></Child>
</template>
<template v-slot:fallback>
<h3>加载中.....</h3>
</template>
</Suspense>
</div>
<script>
import {defineAsyncComponent, Suspense} from 'vue'
const Child = defineAsyncComponent(()=>import('./Child.vue'))
</script>全局API转移到应用对象
app.component
:注册组件app.config
:注册配置属性app.directive
:注册指令app.mount
:挂载组件app.unmount
:卸载组件app.use
:使用组件