toRef
作用:创建一个ref对象,其value值指向另一个对象中的某个属性。新的ref 把传来的reactive的源数据地址拿过来 ,指向的地方换成指定的
语法:const name = toRef(person , " name " ) const name = toRef(person.job.j1 , " salary" )
为什么不能用ref:ref的本质是拷贝,与原始数据无关联。toRef的本质是引用,与原始数据有关联。用ref等于生成新数据,那么它改变的时候原始数据是不变的
应用场景:要将响应式对象中的某个属性单独提供给外部使用时,并且还需要响应式。
引出多个:toRefs
与toRef功能一致,但可以批量创建多个ref对象,语法: toRefs(person)。但是这个只能浅拷贝,也就是只能得到对象第一层的属性,如果属性是对象,里面的属性还是要自己去取出来。
setup() {
*let* person = reactive({
name: '张三',
age: 18,
job: {
j1: {
salary: 18
}
}
})
return {
person,
name: toRef(person, 'name'),
age: toRef(person, 'age'),
salary: toRef(person.job.j1, 'salary'),
//直接拆分多个,这样写必须用到扩展运算符,因为返回值是一个对象,所以要拆分出来
//...toRef(person)
}
}
其他组合式API
shallowReactive 与shallowRef
shallowReactive:只处理对象最外层属性的响应式(浅响应式)。
与reactive的区别就是 reactive会使所有层次的属性都变成响应式,而shallowReactive只有第一层的属性变成响应式
shallowRef:只处理基本数据类型的响应式,不进行对象的响应式处理
与ref的区别:如果传入的是一个对象,那么ref就会求助于reactive去处理,而使对象也可以进行响应式。而shallowRef则只处理基本类型,传入对象没有响应式
应用场景:
- 如果有一个对象数据,结构比较深,但变化的只是外层属性变化===> shallowReactive,
- 如果有一个对象数据,后续功能不会修改该对象中的属性,而是生新的对象来替换(比如置空对象的属性)===>shallowRef。
深层没响应式,但是第一层改变貌似shallowReactive会替换所有数据,出现了第一层改变,然后刷新了深层的数据显示
readonly 与shallowReadonly
readonly:让一个响应式数据变为只读的(深只读),这时候所有数据都不能修改
shallowReadonly:让一个响应式数据变为只读的(浅只读),这时候深层的数据可以被修改
应用场景:不希望数据被修改时。比如别人定义的数据,不希望被修改,引入的时候可以加一个readonly;或者前期需要响应,后期不可修改,就可以使用这个API
toRaw 与markRaw
toRaw:
作用:将一个由reactive生成的响应式对象转为普通对象。
应用场景:用于读取响应式对象对应的普通对象,对这个普通对象的所有操作,不会引起页面更新。
markRaw :
作用:标记一个对象,使其永远不会再成为响应式对象。修改对象的值控制台看会改变,但是页面不做响应式
应用场景:
- 有些值不应被设置为响应式的,例如复杂的第三方类库等。
- 当渲染具有不可变数据源的大列表时,跳过响应式转换可以提高性能。
function showRawPerson(){
const p = toRaw(person)
p.age++
console.log(p)
}
function addCar(){
let car = {name:'奔驰',price:40}
person.car = markRaw(car)
}
customRef
作用:实现一个自定义的ref,也是可以响应式的,但是可以添加更多功能,比如防抖等
使用:
- 引入customRef这个API
- 定义一个函数调用这个API,函数的参数是你使用时myRef时候传过来的。
一定要把这个API返回return!!!!
- 调用customRef,它的参数是一个函数,而且这个函数也必须要有返回值(第二个return)。
- customRef的参数函数的参数是track和trigger,他们分别负责追踪变化和通知重新解析模板
- 如果需要的是响应式,那么返回值里面就要有get和set,get里面要用track()追踪数据是否发生了变化,而set里面用trigger()通知重新vue解析模板(记得把set得到的新值去重新赋给get里面需要重新读取的变量)
- 变化的过程是:修改数据 —— set监测到数据修改,trigger()通知重新解析 —— get里面track()追踪数据的变化,把值重新读取出来
<template>
<!-- Vue3可以没有根标签 -->
<input type="text" v-model="keywords" />
<h3>{{ keywords }}</h3>
</template>
<script>
import { customRef } from 'vue'
export default {
name: 'App',
setup() {
*let* timer
*function* myRef(*value*, *delay*) {
return customRef((track, trigger) *=>* {
return {
get() {
track()
return *value*
},
set(*newValue*) {
clearTimeout(timer)
timer = setTimeout(() *=>* {
*value* = *newValue*
trigger() //通知vue重新解析模板
}, *delay*)
}
}
})
}
*let* keywords = myRef('hello', 500)
//使用程序员自定义的ref
return {
keywords,
myRef
}
}
}
</script>
provide与inject
作用:实现祖孙组件间通信(隔代通信,跨级通信)。子代其实也可以收到,但是一般不用这种方式,父子一般用props。
使用方式:祖组件用provide,子组件用inject。这样得到的数据也是响应式的
祖组件:
import { reactive, toRefs, provide } from 'vue'
import AppSon from './components/Son.vue'
export default {
name: 'App',
components: { AppSon },
setup() {
*let* person = reactive({ name: 'zs', age: '18' })
provide('people', person)
return { ...toRefs(person) }
}
}
孙组件:
<script>
import { inject } from 'vue'
export default {
name: 'AppChild',
setup() {
let people = inject('people')
return {
people
}
}
}
</script>
响应式数据判断的API
-
isRef:检查一个值是否为一个ref 对象
-
isReactive:检查一个对象是否是由reactive创建的响应式代理
-
isReadonly:检查一个对象是否是由readonly创建的只读代理。被readOnly修饰过的数据还是响应式的,只是不可以修改的。
-
isProxy:检查一个对象是否是由reactive或者readonly方法创建的代理
Composition API 组合式API的优势
-
vue2中 Options API存在的问题
使用传统的Options API时,新增或者修改一个需求,就需要分别在data、methods、computed、watch等里面不断地寻找、修改
-
vue3中 Composition API的优势
更加优雅的组织我们的代码、函数,让相关功能的代码更加有序的组织在一起。甚至可以把他们都变成一个个hook函数,修改那部分就可以直接去对应的文件里修改。
新的组件
Fragment:
- vue2中组件必须有一个根标签
- Vue3中组件可以没有根标签,内部会将多个标签包含在一个Fragment虚拟元素中。
好处:减少标签层级,减小内存占用
Teleport
能够将我们的组件传送到指定位置
如下,比如需要弹窗,又不想让弹窗把原本内容撑开,那么就可以把它传到body上。就不会影响其他组件。
<teleport to="body">
<div v-if="isShow" class="mask">
<div class="dialog">
<h3>我是一个弹窗</h3>
<h4>aa</h4>
<h4>aaaa</h4>
<h4>aaaaa</h4>
<button @click="isShow = false">关闭弹窗</button>
</div>
</div>
</teleport>
Suspense
等待异步组件时渲染一些额外内容,让应用有更好的用户体验
Suspense内部本身有两个插槽,一个叫default,一个叫fallback。default里面是要展示的组件,fallback里面是加载中时候显示的内容
使用步骤:
-
异步引入组件
import {defineAsyneComponent} from 'vue ' const Child = defineAsyncComponent(()=>import ('./ components/Child.vue'))
-
使用Suspense包裹组件,并配置好default 与fallback
<template> <div class="app"> <h3>我是App组件</h3> <Suspense> <template v-slot:default> <child/> </template> <template v-slot:fallback> <h3>加载中.....</h3> </template> </Suspense> </div> </template>
前面说setup不能是一个async函数, 因为返回值不再是retum的对象,而是promise,模板看不return对象中的属性。只有这个时候可以返回一个Promise实例,!!!必须Suspense和异步组件配合使用
async setup(){ let sum = ref(0) let p = new Promise((resolve,reject)=>{ setTimeout(()=>{ resolve({sum}) },3000) }) return await p }
vue3的其他变化
全局API的转移
-
Vue 2.x有许多全局API和配置。
例如:注册全局组件、注册全局指令等。//注册全局组件 Vue.component('MyButton', { data: () => ({ count: 0 }), template: ' <button @click-"count++">Clicked {{count}} times</button> }) //注册全局指令 Vue.directive('focus', { inserted: el => el.focus() }
-
Vue3中对这些API做出了调整,将全局的API调整到应用实例app上
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-VFR0zNW8-1650098358694)(E:\markdown\前端\csdn\20220416_1.png)]
其他改变
-
data选项应始终被声明为一个函数
-
过度类名的更改,就是在开始的那个位置上加了一个-from
-
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: e; } .v-leave-from, .v-enter-to { opacity: 1; }
-
-
移除了keyCode作为v-on的修饰符,同时也不支持config.keyCodes了(自定义按键别名,别名的定义也是依赖keyCode的,所以一并移除了)
-
移除了v-on.native修饰符
父组件中绑定事件:
<my- component v-on:close="handleComponentEvent" v-on:click="handleNativeClickEvent" />
子组件中声明自定义事件:子组件emits里面接收的就是自定义事件,不接收就默认是原生事件
<script> export default { emits: ['close'] } </script>
-
移除了过滤器 filter
-on的修饰符,同时也不支持config.keyCodes了(自定义按键别名,别名的定义也是依赖keyCode的,所以一并移除了)
-
移除了v-on.native修饰符
父组件中绑定事件:
<my- component v-on:close="handleComponentEvent" v-on:click="handleNativeClickEvent" />
子组件中声明自定义事件:子组件emits里面接收的就是自定义事件,不接收就默认是原生事件
<script> export default { emits: ['close'] } </script>
vue3的视频看完啦,后面整理一下vue2和vue3的内容,明天开始做项目进行实践了,理论学完了还要有实战经验