目录
- 一、vue3的优点
- 1、vue3的优点
- 二、常用的API
- 1、setup(Composition API)
- 2、生命周期(Composition API)
- 3、ref函数和reactive函数用法和区别(Composition API)
- 1、ref
- 2、reactive
- 3、ref和reactive的区别
- 4、计算属性(computed)和侦听属性(watch)(Composition API)
- 1、computed
- 2、watch
- 5、nextTick(全局API)
- 6、props(选项式API)
- 1、非ts语法
- 2、ts语法
- 7、toRefs(Composition API)
- 8、vue3中的响应式原理
- 1、vue2中的响应式原理
- 2、vue3中的响应式原理
- 总结
- 9、自定义指令
- 1、什么是指令
- 2、怎么实现
- 3、应用场景
- 10、自定义修饰符
- 11、vue的vueuse库(工具库)
- 三、新组件和新属性
- 1、v-bind(内置内容)
- 2、teleport 传送门
- 3、Suspense
一、vue3的优点
1、vue3的优点
1、源码体积得到优化:
(1) API减少,移除一些冷门API,如filter、inline-template等;
(2)引入tree-shaking 减少打包体积;
2、源码的升级:
(1)Vue3使用Proxy进行数据劫持,可以很好的规避vue2使用Object.defineProperty进行数据劫持带来的缺陷;
3、拥抱TypeScript:
(1) 更好的支持Ts(typescript)
4、高级给与:
(1)暴露了更底层的API和提供更先进的内置组件;
5、 组合API(Composition API):
(1) 能够更好的组织逻辑,封装逻辑,复用逻辑;
二、常用的API
1、setup(Composition API)
1、setup是vue3中的一个全新的配置项,值为一个函数;
2、setup是所有CompositionAPI(组合API)的基础,组件中所用到的数据、方法等都需要在setup中进行配置;
(1):若返回一个对象,则对象中的属性、方法,均可以在模板中直接使用;
(2):若返回一个渲染函数:则可以自定义渲染内容;
setup的两个注意点:
1、setup执行时机,在beforeCreate之前执行一次,this是undefined;
2、setup的参数:
(1):props:指为对象,包含组件外部传递过来,且组件内部声明接收了的属性。
(2):context:上下文对象
attrs:值为对象,包含组件外部传递过来,但没有在props配置中声明的属性,相当于this.$attrs;
slots:收到的插槽内容,相当于this.$slots;
emit:分发自定义事件的函数,相当于this.$emit。
2、生命周期(Composition API)
vue3中的生命周期和vue2的区别:
beforeCreate=>setup()(创建实例前)
created=>setup() (创建实例后)
beforeMount=>onBeforeMount ( 挂载DOM前)
mounted=>onMounted ( 挂载DOM后)
beforeUpdate=>onBeforeUpdate (更新组件前)
updated=>onUpdated (更新组件后)
beforeUnmount=>onBeforeUnmount (卸载销毁前)
unmounted=>onUnmounted (卸载销毁后)
3、ref函数和reactive函数用法和区别(Composition API)
1、ref
特点
(1)ref的参数一般是基本数据类型,也可以是对象类型
(2)如果参数是对象类型,其实底层的本质还是reactive,系统会自动将ref转换为reactive,例如
==ref(1) => reactive({value:1})=
(3)在模板中访问ref中的数据,系统会自动帮我们添加.value,在JS中访问ref中的数据,需要手动添加.value
(4)ref的底层原理同reactive一样,都是Proxy
<script setup>
import { ref } from 'vue'
const count = ref(0)
function increment() {
count.value++
}
</script>
<template>
<button @click="increment">
{{ count }} <!-- 无需 .value -->
</button>
</template>
2、reactive
特点
(1)reactive的参数一般是对象或者数组,他能够将复杂数据类型变为响应式数据。
(2)reactive的响应式是深层次的,底层本质是将传入的数据转换为Proxy对象
<template>
<h2>名称:{{name.title}}</h2>
<h2>主要爱好:{{job.hobby[0].type}}</h2>
<button @click="changeName">修改姓名和爱好</button>
</template>
<script>
import { ref,reactive } from 'vue' // 按需引入ref函数
export default {
name: 'App',
setup () {
let name = ref({ // ref定义对象类型响应式变量
title:'赵丽颖'
})
let job = reactive({ // reactive定义对象类型响应式变量
type:'演员',
hobby:[
{
type:'演戏',
degree:'✨✨✨✨✨'
},
{
type:'看书',
degree:'✨✨✨'
}
],
})
function changeName () {
console.log('name',name)
name.value.title = '刘亦菲' // 对ref定义的响应式数据进行操作,需要使用.value
console.log('job',job.hobby[0])
job.hobby[0].type = '运动' // 对reactive定义的响应式数据进行操作,无需使用.value,且是深层次响应
console.log('job',job.hobby[0])
}
// 将变量和函数返回,以便在模版中使用
return {
name,
job,
changeName
}
}
}
</script>
3、ref和reactive的区别
1、从定义数据角度对比:
ref用来定义:基本数据类型;
reactive用来定义:对象(或数组)类型数据;
备注:ref也可以用来定义对象或数组类型数据,它内部会自动通过reactive转为代理对象;
2、从原理角度对比:
ref通过Object.defineProperty()的get和set来实现响应式;
reactive通过使用Proxy来实现响应式(数据劫持),并通过Reflect操作源对象内部的数据。
3、从使用角度对比:
ref定义的数据:操作数据需要.value,读取数据时模板中直接读取不需要.value;
reactive定义的数据:操作数据与读取数据均不需要.value;
4、计算属性(computed)和侦听属性(watch)(Composition API)
computed和watch都是vue框架中的用于监听数据变化的属性,都能在数据改变时,针对改变的相关数据做成相应的变化
1、computed
(1)计算属性的优点:
计算属性是基于缓存来实现的,无论你后端调用十次还是几百次,只要计算属性依赖的相关值没有发生变化,那计算属性就可以通过缓存读取。例子如下:
<script setup lang="ts">
import { reactive, computed } from "vue"
const allName = reactive({
userName1:'小', //姓
userName2:'明', //名
age:20,
})
const userName = computed(()=>{
console.log("我是计算属性调用的");
return allName.userName1 +
allName.userName2+"年龄"+allName.age
})
const userAllName = function(){
console.log("我是方法调用的");
return allName.userName1 + allName.userName2+"年龄"+allName.age
}
</script>
<template>
<!--下面是通过方法得出的名字-->
<div>{{userAllName()}}</div>
<div>{{userAllName()}}</div>
<div>{{userAllName()}}</div>
<div>{{userAllName()}}</div>
<div>{{userAllName()}}</div>
<div>{{userAllName()}}</div>
<div>{{userAllName()}}</div>
<div>{{userAllName()}}</div>
<!--下面是通过计算属性得出的名字-->
<div>{{userName}}</div>
<div>{{userName}}</div>
<div>{{userName}}</div>
<div>{{userName}}</div>
<div>{{userName}}</div>
<div>{{userName}}</div>
<div>{{userName}}</div>
</template>
从上面对应的浏览器中打印的我们可以看出,方法和计算属性调用了八次,但是与方法不一样的是,计算属性就打印了一次,就好像就第一次调用了这个函数一样,后面就没调用了。这就是计算属性的缓存,只要其依赖的值不发生相关的变化,那么无论你计算属性调用了好多次,但最终就只是执行一次。
(2)可写的计算属性
当计算属性是只读的,你试着修改他的话,会收到一个警告,例如:
<script setup lang="ts">
import { ref, computed } from "vue"
const count = ref(1)
const plusOne = computed(() => count.value + 1)
plusOne.value++
</script>
<template>
<div>
<p>{{ count }}</p>
<p>{{ plusOne }}</p>
</div>
</template>
值既没有发生变化还会得到警告,如图:
正确的可写计算属性代码如下:
<script setup lang="ts">
import { ref, computed } from "vue"
const count = ref(1)
/*利用get和set函数*/
const plusOne = computed({
get: () => count.value + 1,
set: (val) => {
count.value = val - 1
}
})
plusOne.value++
</script>
<template>
<div>
<p>{{ count }}</p>
<p>{{ plusOne }}</p>
</div>
</template>
结果如图:
2、watch
默认是懒侦听的,即仅在侦听源发生变化时才执行回调函数。
第一个参数是侦听器的源。这个来源可以是以下几种:
一个函数,返回一个值
一个 ref
一个响应式对象
…或是由以上类型的值组成的数组
第二个参数是在发生变化时要调用的回调函数。这个回调函数接受三个参数:新值、旧值,以及一个用于注册副作用清理的回调函数。
第三个可选的参数是一个对象,支持以下这些选项:
immediate:在侦听器创建时立即触发回调。第一次调用时旧值是 undefined。
deep:如果源是对象,强制深度遍历,以便在深层级变更时触发回调。
flush:使侦听器在侦听器回调中能访问被 Vue 更新之后的DOM。
<script setup lang="ts">
import { ref, watch } from "vue"
const count = ref(0)
/**
* 挑战 1: Watch 一次
* 确保副作用函数只执行一次
* 使用stopWatch停止侦听器
*/
const stopWatch=watch(count, () => {
console.log("Only triggered once")
stopWatch()
})
count.value = 1
setTimeout(() =>{ count.value = 2})
/**
* 挑战 2: Watch 对象
* 确保副作用函数被正确触发
* 使用deep深层次监听
*/
const state = ref({
count: 0,
})
watch(state, () => {
console.log("The state.count updated")
},{
deep:true,
})
state.value.count = 2
/**
* 挑战 3: 副作用函数刷新时机
* 确保正确访问到更新后的`eleRef`值
* 一般侦听器是在Vue组件更新之前被调用,所以要使用flush访问被Vue更新后的DOM
*/
const eleRef= ref()
const age = ref(2)
watch(age, () => {
console.log(eleRef.value)
},{ flush: 'post'})
age.value = 18
</script>
<template>
<div>
<p>
{{ count }}
</p>
<p ref="eleRef">
{{ age }}
</p>
</div>
</template>
watch和watchEffect
相同点
1、两者都可以监听data属性变化
2、watch 与 watchEffect 在手动停止侦听、清除副作用 (将 onInvalidate 作为第三个参数传递给回调)、刷新时机和调试方面有相同的行为。
区别
1、watch不加immediate初始值监听不到,watchEffect一开始就能监听到;
2、watch需要明确监听哪个属性;
3、watchEffect会根据其中的属性,自动监听其变化;
4、watcheffect初始化时,一定会执行一次(收集要监听的数据,不然不知道监听的是什么),watch只有你设置了初始化监听才会监听;
5、watchEffect获取不到更改前的值,而watch可以同时获取更改前和更改后的值;
<script setup lang="ts">
import {ref,reactive,watch,watchEffect} from 'vue'
//数据
let sum = ref(0)
let person = reactive({
name:'张三',
age:18,
job:{
j1:{
salary:20
}
}
})
//监视
/*watch(sum,(newValue,oldValue)=>{
console.log('sum的值变化了',newValue,oldValue)
},{immediate:true})*/
watchEffect(()=>{
const x1 = sum.value
const x2 = person.job.j1.salary
console.log('watchEffect所指定的回调执行了')
})
function add(){
sum.value=sum.value+1
}
</script>
<template>
<div>
<p>
{{sum}}
</p>
<button @click="add">
点我加一
</button>
</div>
</template>
5、nextTick(全局API)
在下次 DOM 更新循环结束之后执行延迟回调。在修改数据之后立即使用这个方法,获取更新后的 DOM,例子
<script setup>
import { ref,nextTick } from "vue"
const count = ref(0)
const counter = ref(null)
async function increment(){
count.value++
console.log(counter.value.textContent )
await nextTick()
console.log(+counter.value.textContent ===1,counter.value.textContent )
}
</script>
<template>
<button ref="counter" @click="increment">
{{ count }}
</button>
</template>
6、props(选项式API)
主要用在组件的父组件向子组件的传值,主要有两大类ts语法和非ts语法
default:默认值;
type:类型;
required:是否必传;
validator:props验证;
1、非ts语法
const props = defineProps({
// 基础类型检查
// (给出 `null` 和 `undefined` 值则会跳过任何类型检查)
propA: Number,
// 多种可能的类型
propB: [String, Number],
// 必传,且为 String 类型
propC: {
type: String,
required: true
},
// Number 类型的默认值
propD: {
type: Number,
default: 100
},
// 对象类型的默认值
propE: {
type: Object,
// 对象或数组的默认值
// 必须从一个工厂函数返回。
// 该函数接收组件所接收到的原始 prop 作为参数。
default(rawProps) {
return { message: 'hello' }
}
},
// 自定义类型校验函数
propF: {
validator(value) {
// The value must match one of these strings
return ['success', 'warning', 'danger'].includes(value)
}
},
// 函数类型的默认值
propG: {
type: Function,
// 不像对象或数组的默认,这不是一个工厂函数。这会是一个用来作为默认值的函数
default() {
return 'Default function'
}
}
})
2、ts语法
interface Props {
// id
id:string
// 宽度
width: number,
// 高度?:
height?: number,
// 是否初始化
init?: boolean,
// 子项
list?: Array<ganttChartItem>,
// 是否显示描述
desc?:string
// 描述颜色
descColor?:string
}
const props = withDefaults(defineProps<Props>(), {
// 高度默认20
height: 20,
// 默认初始化
init: true,
// 子项默认为空
list: () => [],
// 是否显示描述
desc: '',
// 描述颜色
descColor: ''
});
通过定义interface接口来规范props的属性的格式
?:表示可选属性
定义默认属性值时需要使用withDefaults
7、toRefs(Composition API)
toref函数和toref函数与ref函数的区别:
toRef 函数会与源数据交互,修改响应式数据会造成源数据的修改,但是他的修改不会造成视图层数据的更新;
ref 函数可以将对象里面的属性值变成响应式的数据,修改响应式数据,是不会影响到源数据,但是视图层上的数据会被更新
为啥要使用toRefs函数,原因:
在Vue.js
中,我们同样解构/扩展“响应式”对象,但它会失去响应性。toRefs函数才能保证解构/扩展不丢失响应性
一般都会使用toRefs函数而不是toRef函数,原因:
(1)toRefs 函数用于批量设置多个数据为相应是数据。
(2)toRefs 函数还可以与其他响应式数据相交互,更加方便处理视图层数据。
toRef函数的使用:
//toRef 函数有两个参数
toRef(操作对象, 对象属性)
toRefs函数的使用:
<script setup lang="ts">
import { reactive ,toRefs } from "vue"
function useCount() {
const state = reactive({
count: 0,
})
function update(value: number) {
state.count = value
}
return {
...toRefs(state),
update,
}
}
// Ensure the destructured properties don't lose their reactivity ...toRefs(state)
const { count , update } =useCount()
</script>
<template>
<div>
<p>
<span @click="update(count-1)">-</span>
{{ count }}
<span @click="update(count+1)">+</span>
</p>
</div>
</template>
8、vue3中的响应式原理
1、vue2中的响应式原理
基本原理
vue2利用的是原生js下边的Object.defineProperty()进行数据劫持,在通过里面的getter和
setter方法,进行查看和数据的修改,通过发布、订阅者模式进行数据与视图的响应式。
let person = {
name:'路飞',
age:18
}
let p = {}
Object.defineProperty(p, "name", {
get(){ //有人读取name时调用
return person.name;
},
set(value){ //有人修改name时调用
console.log("有人修改了name属性")
person.name = value
}
});
Object.defineProperty(p, "age", {
get(){ //有人读取age时调用
return person.age;
},
set(value){ //有人修改age时调用
console.log("有人修改了age属性")
person.age = value
}
});
vue2给对象增加和修改新属性用 $set;
vue2想删除已有的属性可以用 $delete;
2、vue3中的响应式原理
基本原理
1、对于基本数据类型来说,响应式依然是靠Object.defineProperty()的get和set来完成的
2、对于对象类型的数据:
通过Proxy代理:拦截对象中任意属性的变化,包括属性值得读写、添加、删除等操作等…
通过Reflect反射函数进行操作
new Proxy(data,{
//拦截读取属性值
get(target, prop){
return Reflect.get(target, prop)
},
//拦截设置属性值或添加新属性
set(target, prop, value){
return Reflect.set(target, prop, value)
},
//拦截删除属性
deleteProperty(target, prop){
return Reflect.deleteProperty(target, prop)
}
})
总结
在Vue2中,数据响应式主要借助Object.defineProperty()来实现,存在的缺陷是无法操作数据的增加和删除,在Vue3中,数据响应式主要借助proxy和Reffect配合实现,可以做到实现数据的增删改查。
9、自定义指令
1、什么是指令
指令系统是计算机硬件的语言系统,也叫机器语言,它是系统程序员看到的计算机的主要属性。因此指令系统表征了计算机的基本功能决定了机器所要求的能力
在vue中提供了一套为数据驱动视图更为方便的操作,这些操作被称为指令系统
我们看到的v- 开头的行内属性,都是指令,不同的指令可以完成或实现不同的功能
除了核心功能默认内置的指令 (v-model 和 v-show),Vue 也允许注册自定义指令
指令使用的几种方式:
//会实例化一个指令,但这个指令没有参数
v-xxx
// – 将值传到指令中
v-xxx="value"
// – 将字符串传入到指令中,如v-html="'<p>内容</p>'"
v-xxx="'string'"
// – 传参数(arg
),如v-bind:class="className"
v-xxx:arg="value"
// – 使用修饰符(modifier
)
v-xxx:arg.modifier="value"
2、怎么实现
注册一个自定义指令有全局注册与局部注册
全局注册注册主要是用过Vue.directive方法进行注册
Vue.directive第一个参数是指令的名字(不需要写上v-前缀),第二个参数可以是对象数据,也可以是一个指令函数
// 注册一个全局自定义指令 `v-focus`
Vue.directive('focus', {
// 当被绑定的元素插入到 DOM 中时……
inserted: function (el) {
// 聚焦元素
el.focus() // 页面加载完成之后自动让输入框获取到焦点的小功能
}
})
局部注册通过组件options选项中设置directive属性
directives: {
focus: {
// 指令的定义
inserted: function (el) {
el.focus() // 页面加载完成之后自动让输入框获取到焦点的小功能
}
}
}
使用新自定义指令
<input v-focus="state" type="text">
指令钩子
const myDirective = {
// 在绑定元素的 attribute 前
// 或事件监听器应用前调用
created(el, binding, vnode, prevVnode) {
// 下面会介绍各个参数的细节
},
// 在元素被插入到 DOM 前调用
beforeMount(el, binding, vnode, prevVnode) {},
// 在绑定元素的父组件
// 及他自己的所有子节点都挂载完成后调用
mounted(el, binding, vnode, prevVnode) {},
// 绑定元素的父组件更新前调用
beforeUpdate(el, binding, vnode, prevVnode) {},
// 在绑定元素的父组件
// 及他自己的所有子节点都更新后调用
updated(el, binding, vnode, prevVnode) {},
// 绑定元素的父组件卸载前调用
beforeUnmount(el, binding, vnode, prevVnode) {},
// 绑定元素的父组件卸载后调用
unmounted(el, binding, vnode, prevVnode) {}
}
钩子参数
指令的钩子会传递以下几种参数:
el
:指令绑定到的元素。这可以用于直接操作 DOM。
binding
:一个对象,包含以下属性。
(1)value:传递给指令的值。例如在 v-my-directive=“1 + 1” 中,值是 2。
(2)oldValue:之前的值,仅在 beforeUpdate 和 updated 中可用。无论值是否更改,它都可用。
(3)arg:传递给指令的参数 (如果有的话)。例如在 v-my-directive:foo 中,参数是 “foo”。
(4)modifiers:一个包含修饰符的对象 (如果有的话)。例如在 v-my-directive.foo.bar 中,修饰符对象是 { foo: true, bar: true }。
(5)instance:使用该指令的组件实例。
(6)dir:指令的定义对象。
vnode
:代表绑定元素的底层 VNode。
prevNode
:之前的渲染中代表指令所绑定元素的 VNode。仅在 beforeUpdate 和 updated 钩子中可用。
3、应用场景
例子一:自定义防抖点击指令
<script setup lang='ts'>
/**
* 实现以下自定义指令
* 确保在一定时间内当快速点击按钮多次时只触发一次点击事件
* 你需要支持防抖延迟时间选项, 用法如 `v-debounce-click:ms`
*
*/
const VDebounceClick = {
beforeMount(el, binding, vnode, prevVnode) {
let s = binding.arg * 1
let timer
el.addEventListener('click', () => {
if (timer) {
clearTimeout(timer)
}
timer = setTimeout(() => {
binding.value(s)
}, s)
})
},
}
function onClick() {
console.log("Only triggered once when clicked many times quickly")
}
</script>
<template>
<button v-debounce-click:1000="onClick">
Click on it many times quickly
</button>
</template>
例子二:自定义获取焦点指令
<script setup lang='ts'>
import { ref } from "vue"
const state = ref(false)
/**
* 实现一个自定义指令,让元素获取焦点
* 确保当切换`state`时,元素随着状态值获取/失去焦点
*
*/
const VFocus = {
updated:(el)=>{
if(state.value){
el.focus()
}else{
el.blur()
}
}
}
setInterval(() => {
state.value = !state.value
}, 2000)
</script>
<template>
<input v-focus="state" type="text">
</template>
例子三:一键copy功能
import { Message } from 'ant-design-vue';
const vCopy = { //
/*
bind 钩子函数,第一次绑定时调用,可以在这里做初始化设置
el: 作用的 dom 对象
value: 传给指令的值,也就是我们要 copy 的值
*/
bind(el, { value }) {
el.$value = value; // 用一个全局属性来存传进来的值,因为这个值在别的钩子函数里还会用到
el.handler = () => {
if (!el.$value) {
// 值为空的时候,给出提示,我这里的提示是用的 ant-design-vue 的提示,你们随意
Message.warning('无复制内容');
return;
}
// 动态创建 textarea 标签
const textarea = document.createElement('textarea');
// 将该 textarea 设为 readonly 防止 iOS 下自动唤起键盘,同时将 textarea 移出可视区域
textarea.readOnly = 'readonly';
textarea.style.position = 'absolute';
textarea.style.left = '-9999px';
// 将要 copy 的值赋给 textarea 标签的 value 属性
textarea.value = el.$value;
// 将 textarea 插入到 body 中
document.body.appendChild(textarea);
// 选中值并复制
textarea.select();
// textarea.setSelectionRange(0, textarea.value.length);
const result = document.execCommand('Copy');
if (result) {
Message.success('复制成功');
}
document.body.removeChild(textarea);
};
// 绑定点击事件,就是所谓的一键 copy 啦
el.addEventListener('click', el.handler);
},
// 当传进来的值更新的时候触发
componentUpdated(el, { value }) {
el.$value = value;
},
// 指令与元素解绑的时候,移除事件绑定
unbind(el) {
el.removeEventListener('click', el.handler);
},
};
export default vCopy;
10、自定义修饰符
主要是用来进行数据表单的双向绑定的
添加到组件 v-model 的修饰符将通过 modelModifiers prop 提供给组件。
下面是自定义修饰符capitalize
,自动将 v-model
绑定输入的字符串值首字母转为大写:
父组件:
<template>
<div>
<Renderer v-model.capitalize="data"/> {{modelValue}}
</div>
</template>
<script setup>
import Renderer from "./Comp.vue";
import { ref } from "vue";
const data = ref('');
</script>
子组件:
<script setup>
import {ref} from 'vue'
const props = defineProps({
modelValue: String,
modelModifiers: {
default: () => ({})
}
});
const emit = defineEmits(['update:modelValue']);
const updateValue = (e) => {
let value=e.target.value
// console.log('oooo',props.modelModifiers)
if (props.modelModifiers.capitalize) {
value = value.charAt(0).toUpperCase() + value.slice(1)
// console.log(value,'pplplp')
}
emit('update:modelValue',value);
}
</script>
<template>
<input type="text" :value="modelValue" @input="updateValue" />
</template>
11、vue的vueuse库(工具库)
vueuse工具库链接
1、实现一个计数器
<script setup lang='ts'>
import { ref } from 'vue'
interface UseCounterOptions {
min?: number
max?: number
}
/**
* 实现计数器函数,确保功能正常工作
* 1. 加 (+)
* 2. 减 (-)
* 3. 重置
* 4. 最小值 & 最大值 选项支持
*/
function useCounter(initialValue = 0, options: UseCounterOptions = {}) {
const count = ref(initialValue)
const {max, min} = options
const inc = () => count.value = Math.min(max, count.value + 1)
const dec = () => count.value = Math.max(min, count.value - 1)
const reset = () => (count.value = initialValue)
return { count, inc, dec, reset }
}
const { count, inc, dec, reset } = useCounter(0, { min: 0, max: 10 })
</script>
<template>
<p>Count: {{ count }}</p>
<button @click="inc">
inc
</button>
<button @click="dec">
dec
</button>
<button @click="reset">
reset
</button>
</template>
2、实现一个切换状态的可组合函数
<script setup lang='ts'>
import { ref } from 'vue'
/**
* 实现一个切换状态的可组合函数
* 确保该功能正常工作
*/
function useToggle(state: boolean): [boolean, () => void] {
const status = ref(state);
const toggle = () => (status.value = !status.value);
return [status, toggle];
}
const [state, toggle] = useToggle(false)
</script>
<template>
<p>State: {{ state ? 'ON' : 'OFF' }}</p>
<p @click="toggle">
Toggle state
</p>
</template>
三、新组件和新属性
1、v-bind(内置内容)
Vue3中的新增的v-bind()的常用使用方式,主要包括在css,less,scss中的使用
<script setup>
import { ref } from "vue"
const theme = ref("red")
const colors = ["blue", "yellow", "red", "green"]
const width=200
const style={
height:'50px'
}
setInterval(() => {
theme.value = colors[Math.floor(Math.random() * 4)]
}, 1000)
</script>
<template>
<p>hello</p>
</template>
<style scoped>
/* 修改以下代码绑定动态颜色 */
p {
color: v-bind(theme);
line-height: v-bind('style.height');
border:solid;
width: v-bind(width+'px');
}
</style>
2、teleport 传送门
能将插槽内容渲染到 DOM 中的另一个指定位置。
<script setup>
const msg = "Hello World"
</script>
<template>
<!-- to='移动到指定DOM里'-->
<teleport to="body">
<span>{{ msg }}</span>
</teleport>
</template>
3、Suspense
允许应用程序在等待异步组件时渲染一些其它内容,让用户有一个更好体验。
使用步骤:
1、异步引入组件;
import {defineAsyncComponent} from 'vue'
const Child = defineAsyncComponent(()=>import('./components/Child.vue'))
2、使用Suspense包裹组件,并配置好default 与 fallback
(1) 插槽包裹异步组件;
(2) 插槽包裹渲染异步组件渲染之前的内容;
<template>
<div class="app">
<h3>我是App组件</h3>
<Suspense>
<template v-slot:default>
<Child/>
</template>
<template v-slot:fallback>
<h3>加载中.....</h3>
</template>
</Suspense>
</div>
</template>