vue3的新特性
一、setup
1、是vue3.0的中的一个新增配置项,值为一个函数。
2、setup是所有composition API(组合式api)展示的舞台。
3、setup函数的两种返回值:
若返回一个对象,则对象中的属性、方法, 在模板中均可以直接使用。(重点关注!)
若返回一个渲染函数:则可以自定义渲染内容。(了解) (不常用)
注意点:
1、尽量不要和vue2.x版本混用。
- vue2.x版本可以访问到setup里的属性。
- setup访问不到vue2.x版本的属性。
- 如果有重名,setup优先
2、setup不能是一个async函数,因为返回值不再是对象, 而是promise, 模板看不到return对象中的属性。
3、setup执行顺序在beforeCreat,并且在setup中this为undefined
4、setUp(props, contex)接受两个参数
- props:值为对象,包含:组件外部传递过来,且组件内部声明接收了的属性(其实就是vue2.0的props功能)
- context:上下文对象(其中可以获取到1、attrs组件外部传递过来,但没有在props配置中声明的属性。2、slots:插槽内容3、emit:分发自定义事件的函数,并且以后在setup中不能写this.$emit,要写context.emit)
<template>
<h2>姓名:{{name}}</h2>
<h2>年龄:{{age}}</h2>
<button @click="sayInfo">显示信息</button>
</template>
<script>
export default {
name: "App",
setup(){
//此时的数据不具有双向绑定的功能
let name = "小明"
let age = 18
// 方法
function sayInfo(){
alert(`你好${name},你太厉害了吧`)
}
// 想要在模板中使用setup中的方法或者数据,必须return
return {
name,age, gender,sayInfo
}
// return ()=> h('h1','试试')
}
};
</script>
二、ref
作用:定义一个响应式数据。
语法:let xxx = ref(xxx)
注意点:
- ref(),括号中可以填基本数据类型和也可以是对象类型
- 填基本数据类型会返回一个RefImpl的实例对象,RefImpl类中有getter和setter可以检测到数据的变化。
- 填对象类型的数据:其实就是使用了Vue3.0中的一个新函数—— reactive函数。
- js操作值的方法是xxx.value,模板中直接{{xxx}}
- 使用之前要先引入。
<template>
<h2>姓名:{{ name }}</h2>
<h2>年龄:{{ age }}</h2>
<button @click="changeInfo">修改信息</button>
</template>
<script>
import { ref } from "vue";
export default {
name: "App",
setup() {
// 数据
let name = ref("小明");
let age = ref(18);
// 方法
function changeInfo() {
name.value = "小明";
age.value = 48;
}
return {
name,
age,
changeInfo,
};
},
};
</script>
三、reactive
作用:定义一个响应式对象。
语法:let xxx = reactive(xxx)
注意点:
- reactive(),括号要填对象类型
- 填基本数据类型会返回一个proxy实例对象。
- 填对象类型的数据:其实就是使用了Vue3.0中的一个新函数—— reactive函数。
- js操作值的方法是xxx.value,模板中直接{{xxx}}
- 使用之前要先引入。
- 上面ref()方法虽然也可以填对象,但是他的本质还是调用了reactive方法,并且js操作时还需要xxx.value,多此一举。而且使用reactive()定义的响应式对象是深层次的,不会出现vue2对象数据层级过深,不改变的情况。
<template>
<h2>姓名:{{ yk.name }}</h2>
<h2>年龄:{{ yk.age }}</h2>
<h2>爱好:{{ yk.hobby }}</h2>
<h3>测试数据:{{ yk.job.a.b.c }}</h3>
<button @click="changeInfo">修改信息</button>
</template>
<script>
import { reactive } from "vue";
export default {
name: "App",
setup() {
// 数据
let yk = reactive({
age: 18,
hobby: ["吃饭", "睡觉", "打豆豆"],
job: {
a: {
b: {
c: 666,
},
},
},
});
// 方法
function changeInfo() {
yk.age = 48;
yk.job.a.b.c = 888;
// 直接通过数组下标修改,可以触发响应式
yk.hobby[0] = "打豆豆";
}
return {
yk,
changeInfo,
};
},
};
</script>
三、计算属性computed
与vue2.x相比,功能几乎一样,但是写法有些许变动。
<template>
姓:<input v-model="person.firstName"></input>
名: <input v-model="person.lastName"></input>
姓名:<input v-model="person.fullName"></input>
</template>
<script>
//先引入
import {computed,reactive } from 'vue'
export default {
name: "App",
setup() {
let person = reactive({
firstName :"小",
lastName:"明",
fullName:""
})
//计算属性 —— 简写
//let fullName = computed(()=>{
// return person.firstName + '-' + person.lastName
//})
//计算属性 —— 完整
person.fullName = computed({
get(){
return person.firstName + '-' + person.lastName
},
set(value){
const nameArr = value.split('-')
person.firstName = nameArr[0]
person.lastName = nameArr[1]
}
})
return {
person
};
},
};
</script>
三、计算属性watch
和计算属性差不多,在vue3中和只是语法上上的改变。
注意点:
- 监视ref定义的响应式数据时:oldValue无法正确获取、强制开启了深度监视(deep配置失效)
- 监视reactive定义的响应式数据中某个属性时(这个属性需是对象):deep配置有效
//情况一:监视ref定义的响应式数据
watch(sum,(newValue,oldValue)=>{
console.log('sum变化了',newValue,oldValue)
})
//如果用ref定义了一个对象
watch(person.value,(newValue,oldValue)=>{
console.log('person变化了',newValue,oldValue)
})
//情况二:监视多个ref定义的响应式数据
watch([sum,msg],(newValue,oldValue)=>{
console.log('sum或msg变化了',newValue,oldValue)
})
//情况三:监视reactive定义的响应式数据
watch(person,(newValue,oldValue)=>{
console.log('person变化了',newValue,oldValue)
},{immediate:true,deep:false}) //此处的deep配置不再奏效
//情况四:监视reactive定义的响应式数据中的某个属性
watch(()=>person.job,(newValue,oldValue)=>{
console.log('person的job变化了',newValue,oldValue)
},{immediate:true,deep:true})
//情况五:监视多个reactive定义的响应式数据中的某些属性
watch([()=>person.job,()=>person.name],(newValue,oldValue)=>{
console.log('person的job变化了',newValue,oldValue)
},{immediate:true,deep:true})
//特殊情况
//person.job中的job也是一个对象
watch(()=>person.job,(newValue,oldValue)=>{
console.log('person的job变化了',newValue,oldValue)
},{deep:true}) //此处由于监视的是reactive素定义的对象中的某个属性,所以deep配置有效
四、watchEffect函数
watch是:既要指明监视的属性,也要指明监视的回调。
watchEffect是:不用指明监视哪个属性,监视的回调中用到哪个属性,那就监视哪个属性。
这个函数的功能和计算属性差不多,但是
- 但computed注重的计算出来的值(回调函数的返回值),所以必须要写返回值。
- 而watchEffect更注重的是过程(回调函数的函数体),所以不用写返回值。
//watchEffect所指定的回调中用到的数据只要发生变化,则直接重新执行回调。
watchEffect(()=>{
const x1 = sum.value
const x2 = person.age
console.log('watchEffect配置的回调执行了')
})
五、生命周期钩子
1、vue3.0中可以继续使用vue2.x中的生命周期钩子,但是有两个被更名:
- beforeDestroy改成beforeUnmount
- destroyed改成 unmounted
2、vue3.0也提供了composition API形式的生命周期钩子,与vue2.x钩子对应关系如下:
- beforeCreate======>setup()
- created==========>setup()
- beforeMount======>onBeforeMount
- mounted=========>onMounted
- beforeUpdate=====>onBeforeUpdate
- updated =========>onUpdated
- beforeUnmount ===>onBeforeUnmount
- unmounted ======>onUnmounted
六、自定义hook函数
- 什么是hook?-------本质是一个函数,把setup中使用的compositionAPI进行了封装。
- 类似于vue2.x中的mixin。
- 自定义hook的优势:复用代码,让setup中的逻辑更清晰。
- emmmm感觉就是模块导入,和vue2.x 的mixin也有些许差别。
创建一个hook文件夹,里面创建文件point.js
import { reactive, onMounted, onBeforeUnmount } from "vue";
export default function() {
//实现鼠标“打点”相关的数据
let point = reactive({
x: 0,
y: 0,
});
//实现鼠标“打点”相关的方法
function savePoint(event) {
point.x = event.pageX;
point.y = event.pageY;
console.log(event.pageX, event.pageY);
}
//实现鼠标“打点”相关的生命周期钩子
onMounted(() => {
window.addEventListener("click", savePoint);
});
onBeforeUnmount(() => {
window.removeEventListener("click", savePoint);
});
return point;
}
在组件中使用
<template>
<h2>当前点击时鼠标的坐标为:x:{{point.x}},y:{{point.y}}</h2>
</template>
<script>
import usePoint from '../hook/point.js'
export default {
name:'HelloWorld',
setup(){
const point = usePoint()
return {point}
}
}
</script>
七、toRef和toRefs
- 作用:创建一个 ref 对象,其value值指向另一个对象中的某个属性。
- 语法:const name = toRef(person,‘name’)
- 应用: 要将响应式对象中的某个属性单独提供给外部使用时。
- 扩展:toRefs与toRef功能一致,但可以批量创建多个 ref 对象,语法:toRefs(person)
<template>
<h4>{{person}}</h4>
<h2>姓名:{{name}}</h2>
<h2>年龄:{{age}}</h2>
<h2>薪资:{{job.j1.salary}}K</h2>
<button @click="name+='~'">修改姓名</button>
<button @click="age++">增长年龄</button>
<button @click="job.j1.salary++">涨薪</button>
</template>
<script>
import {ref,reactive,toRef,toRefs} from 'vue'
export default {
name: 'HelloWorld',
setup(){
let person = reactive({
name:'张三',
age:18,
job:{
j1:{
salary:20
}
}
})
// const name1 = person.name
// console.log('%%%',name1)
// const name2 = toRef(person,'name')
// console.log('####',name2)
const x = toRefs(person)
console.log('******',x)
return {
person,
// name:toRef(person,'name'),
// age:toRef(person,'age'),
// salary:toRef(person.job.j1,'salary'),
...toRefs(person)
}
}
}
</script>
八、其他新特性
1、shallowReactive 与 shallowRef
- shallowReactive:只处理对象最外层属性的响应式(浅响应式)。
- shallowRef:只处理基本数据类型的响应式, 不进行对象的响应式处理。
- 什么时候使用?
1、如果有一个对象数据,结构比较深, 但变化时只是外层属性变化 ===> shallowReactive。
2、如果有一个对象数据,后续功能不会修改该对象中的属性,而是生新的对象来替换 ===> shallowRef。
2、readonly 与 shallowReadonly
- readonly: 让一个响应式数据变为只读的(深只读)。
- shallowReadonly:让一个响应式数据变为只读的(浅只读)。
- 应用场景: 不希望数据被修改时。
3、toRaw 与 markRaw
toRaw
作用:将一个由reactive生成的响应式对象转为普通对象。
使用场景:
- 用于读取响应式对象对应的普通对象,对这个普通对象的所有操作,不会引起页面更新。
markRaw
作用:标记一个对象,使其永远不会再成为响应式对象。
应用场景:
- 有些值不应被设置为响应式的,例如复杂的第三方类库等。
- 当渲染具有不可变数据源的大列表时,跳过响应式转换可以提高性能
4、customRef
作用:创建一个自定义的 ref,并对其依赖项跟踪和更新触发进行显式控制。
实现防抖效果
<template>
<input type="text" v-model="keyWord" />
<h3>{{ keyWord }}</h3>
</template>
<script>
import { customRef } from "vue";
export default {
name: "App",
setup() {
//自定义一个ref——名为:myRef
function myRef(value, delay) {
let timer;
return customRef((track, trigger) => {
return {
get() {
console.log(`有人从myRef这个容器中读取数据了,我把${value}给他了`);
track(); // 通知Vue追踪value的变化(提前和get商量一下,让他认为这个value是有用的)
return value;
},
set(newValue) {
console.log(`有人把myRef这个容器中数据改为了:${newValue}`);
clearTimeout(timer);
timer = setTimeout(() => {
value = newValue;
trigger(); // 通知Vue去重新解析模板
}, delay);
},
};
});
}
// let keyWord = ref('hello') //使用Vue提供的ref
let keyWord = myRef("hello", 500); //使用程序员自定义的ref
return { keyWord };
},
};
</script>
5、provide 与 inject
作用:实现祖与后代组件间通信
套路:父组件有一个 provide 选项来提供数据,后代组件有一个 inject 选项来开始使用这些数据
具体写法
祖组件中:
setup(){
......
let car = reactive({name:'奔驰',price:'40万'})
provide('car',car) // 给自己的后代组件传递数据
......
}
后代组件中:
setup(props,context){
......
const car = inject('car') // 拿到祖先的数据
return {car}
......
}
6、响应式数据的判断
isRef: 检查一个值是否为一个 ref 对象。
isReactive: 检查一个对象是否是由 reactive 创建的响应式代理。
isReadonly: 检查一个对象是否是由 readonly 创建的只读代理。
isProxy: 检查一个对象是否是由 reactive 或者 readonly 方法创建的代理。
九、新的组件
1、Fragment
在Vue2中: 组件必须有一个根标签
在Vue3中: 组件可以没有根标签, 内部会将多个标签包含在一个Fragment虚拟元素中
好处: 减少标签层级, 减小内存占用
2、Teleport
Teleport 是一种能够将我们的组件html结构移动到指定位置的技术。
3、Suspense
等待异步组件时渲染一些额外内容,让应用有更好的用户体验
使用步骤:
异步引入组件
import {defineAsyncComponent} from 'vue'
const Child = defineAsyncComponent(()=>import('./components/Child.vue'))
使用Suspense包裹组件,并配置好default与 fallback
<template>
<div class="app">
<h3>我是App组件</h3>
<Suspense>
//default:就是组件要显示的内容
<template v-slot:default>
<Child/>
</template>
//fallback:就是组件没加载完全的“备胎”
<template v-slot:fallback>
<h3>加载中.....</h3>
</template>
</Suspense>
</div>
</template>
十、其他变化
1、data选项应始终被声明为一个函数
2、过渡类名的更改
//Vue2.x写法
.v-enter,
.v-leave-to {
opacity: 0;
}
.v-leave,
.v-enter-to {
opacity: 1;
}
//Vue3.x写法
.v-enter-from,
.v-leave-to {
opacity: 0;
}
.v-leave-from,
.v-enter-to {
opacity: 1;
}
3、移除keyCode作为 v-on 的修饰符,同时也不再支持config.keyCodes
4、移除v-on.native修饰符
5、移除过滤器(filter)
哔哩哔哩的尚硅谷教学视频-笔记
链接:https://www.bilibili.com/video/BV1Zy4y1K7SH?p=158