1、Composition Api
OptionsAPI 选项式API:配置 name,data,methods...,是分散的
CompositionsAPI 组合式:用函数的方式将 name,data,methods...组合到一起
1.setup
setup 语法糖,添加组件名称(标签中)需要安装插件vite-plugin-setup-extent
<script setup lang="ts" name="Hello"></script>
2.ref
创建【基本类型】的响应式数据,返回一个RefImpl的实例对象
对于let name = ref('张三')来说,name不是响应式的,name.value是响应式的。
接收的数据也可以是【对象类型】,内部其实还是调用了 reactive 函数
3.reactive
创建【对象类型】的响应式数据,返回一个Proxy的实例对象
定义的响应式数据是深层次的
对比:
- ref 创建的变量必须使用
.value - reactive 重新分配一个新对象时会失去响应式(可以使用
Object.assign去整体替换)
4.toRefs 与 toRef
将一个响应式对象中的每一个属性装换为 ref 对象,toRefs可以批量替换
<script lang="ts" setup name="Hello">
let aa = reactive({
name: 'judian',
age: 18
})
// toRefs将aa对象的n个属性批量取出,都保持响应式
let {name,age} = toRefs(aa);
// toRef将aa对象的name属性取出,保持响应式
let name2 = toRef(aa, 'name');
</script>
5.computed
根据已有的数据计算出新数据
计算属性有记忆,仅在依赖的数据改变时执行。函数是只要调用就执行
<script setup>
// 只读,不能修改
let fullName = computed(() => {
return firstName.value.slice(0,1).toUppercase() + firstName.value.slice(1) '-' + lastName.value
})
// 想要修改添加get,set
let fullName2 = computed({
// 读取
get(){
return firstName.value + '-' + lastName.value
},
// 修改
set(val){
const [first,last] = val.split('-')
firstName.value = first
lastName.value = last
}
})
</script>
6.watch
监视数据变化,只监视以下四种数据:
ref定义的数据reactive定义的数据- 函数返回一个值(
getter函数)() => xx - 一个包含上述内容的数组
ref定义的基本类型数据,监视的是其 value 值的改变
<script setup>
let sum = ref(0);
const stopWatch = watch(sum, (newval,oldval) => {
if(newval >= 10) stopWatch()
})
// 监视 sum,且大于 10 的时候停止监视
</script>
ref定义的对象类型数据,监视的是对象的地址值,若想监视内部数据需要手动开启深度监视{deep: true}
若修改的是 ref 定义的对象中的属性,new 和 old 都是新值,因为他们是同一个对象。若修改整个 ref 定义的对象,new 是新值 ,old 是旧值,因为他们不是同一个对象了
<script setup>
let person = ref({
name: 'judian',
age: 18
})
watch(person, (newval,oldval) => {
console.log("person改变了")
},{deep: true})
</script>
reactive 定义的对象类型的数据,默认开启了深度监视且不可关闭
<script setup>
let person = reactive({
name: 'judian',
age: 18
})
watch(person, (newval,oldval) => {
console.log("person改变了")
})
</script>
监视 ref 或reactive 定义的对象类型中的某个属性:
- 若该属性不是对象类型需要写成函数的形式(getter函数,一个函数一个返回值)
- 若该属性依然是对象类型,可以直接写也可以写成函数的形式,建议写成函数的形式
监视的是对象,是地址值,需要关注对象内部,要手动开启深度监视
<script setup>
let person = reactive({
name: 'judian',
age: 18
})
watch(() => person.name, (newval,oldval) => {
console.log("person.name改变了")
})
// 一个函数,一个返回值(getter 函数) () => { return person.name} 简写了,
</script>
监视多个数据,数组的形式
<script setup>
let sum = ref(0);
let person = reactive({
name: 'judian',
age: 18
});
watch([() => person.name, sum], (newval,oldval) => {
// val是一个数组的形式[person.name,sum]
console.log("person.name,sum改变了")
})
</script>
7.watchEffect
立即运行一个函数,同时响应式的追踪其依赖,并在依赖更改时重新执行该函数
对比
- watch:要明确指出监视的数据
- watchEffect:不用指出,函数中用到那些属性就监视那些属性
8.标签ref属性
用于注册模板引用
- 用在普通 DOM 标签上,获取的是 DOM 节点
- 用在组件标签上,获取的是组件实例对象
<template>
<Person ref="per" />
</template>
<script setup>
let per = ref();
// per.value.name 子组件里定义的 name
</script>
子组件:要使用defineExpose暴露内容
<script setup>
let name = ref('judian');
defineExpose({name});
// 使用defineExpose将组件中的数据交给外部
</script>
9.一些TS-接口泛型
// 定义接口,限制每个 person 对象的格式
export interface PersonInter {
name: string,
age: number,
x?: number // 可有可无
}
<script setup>
import {type PersonInter} from "@/types"
let personList:Array<PersonInter> = [
{name: 'judian', age: 18},
{name: 'judian', age: 18, x: 9},
]
</script>
personList:Array<PersonInter>可以写成一个自定义类型的形式
// 定义一个自定义类型Persons
export type Persons = Array<PersonInter>
<template>
<Person :list="persons"/>
</template>
<script lang="ts" setup name="App">
import {type Persons} from './types'
let persons = reactive<Persons>([
{name:'judian',age:18}
])
</script>
10.props
Person.vue
<script setup>
// 只接收 list
defineProps(['list']);
// 接收并保存 props
let props = defineProps(['list']);
console(props.list);
// 接收+限制类型(可有可无)
defineProps<{list?:Persons}>();
// 接收+限制类型(可有可无)+ 指定默认值
withDefaults(defineProps<{list?:Persons}>(),{
list: () => [{name: 'judian', age: 18}]
})
</script>
11.生命周期
生命周期分为四个阶段:创建,挂载,更新,销毁
vue2的生命周期
创建:beforeCreate、created
挂载:beforeMount、mounted
更新:beforeUpdate、updated
销毁:beforeDestory、destoryed
vue3的生命周期
创建:setup
挂载:onBeforeMount、onMounted
更新:onBeforeUpdate、onUpdated
销毁:onBeforeUnmount、onUnmounted
常用的钩子:
onMounted挂载完毕、onUpdated更新完毕、onBeforeUnmount卸载之前
12.自定义 hook
本质是一个函数,把setup函数中使用的composition API进行了封装,类似于 vue2 的 mixin
优势:复用代码,让 setup 中的逻辑更清晰
useSum.ts
export default function() {
let sum = ref(0);
const increment = () => {
sum.value += 1
}
const decrement = () => {
sun.value -= 1
}
onMounted(() => {
increment()
})
// 向外暴露数据
return {
sum,
increment,
decrement
}
}
uesDog.ts
import axios, {AxiosError} from "axios";
export default function() {
let dogList = reactive<string[]>([]);
async function getDog() {
try {
let {data} = await axios.get("https://dog.ceo/api/breed/pembroke/images/random");
dogList.push(data.message)
}catch (error) {
const err = <AxiosError>error
console.log(err.message)
}
}
onMounted(() => {
getDog()
})
return {
dogList,
getDog
}
}
在组件中的使用
<template>
<h2>当前求和为:{{sum}}</h2>
<button @click="increment">点我+1</button>
<button @click="decrement">点我-1</button>
<hr>
<img v-for="dog in dogList" :key="dog" :src="dog">
<button @click="getDog">再来一只狗</button>
</template>
<script setup lang="ts">
import useSum from './hooks/useSum'
import useDog from './hooks/useDog'
let {sum,increment,decrement} = useSum()
let {dogList,getDog} = useDog()
</script>
2、路由
1.工作模式
- history 模式:不带有#,需要服务器处理路径问题
- hash 模式:带#,不需要处理
const router = createRouter({
history: createWebHistory(), // history
})
// history: createWebHashHistory() hash模式
2.命名路由
routes: [
name: 'zhuye',
path: '/home',
component: Home
]
3.routerLink to
<!-- 第一种:to的字符串写法 -->
<router-link active-class="active" to="/home">主页</router-link>
<!-- 第二种:to的对象写法 -->
<router-link active-class="active" :to="{path:'/home'}">Home</router-link>
<router-link :to="{name:'zhuye'}">跳转</router-link>
4.嵌套路由
news添加 detail
{
name:'xinwen',
path:'/news',
component:News,
children:[
{
name:'xiang',
path:'detail',
// path:'detail/:a/:b/:content'
component:Detail
}
]
},
跳转路由添加完整路径
<router-link to="/news/detail">xxxx</router-link>
<!-- 或 -->
<router-link :to="{path:'/news/detail'}">xxxx</router-link>
<router-link :to="{name:'xiang'}">xxxx</router-link>
5.传参
- query
- params:若使用
to的对象写法,必须使用name配置项,不能用path。并需要提前在规则中占位
// query
<router-link to="/news/detail?a=1&b=2&content=欢迎你">跳转</router-link>
<router-link :to="{path: '/news/detail', query: {a: 1, b: 2, content: '欢迎你'}}">跳转</router-link>
// 接收参数
import {useRoute} from 'vue-router'
const route = useRoute()
// route.query
// params
<RouterLink :to="`/news/detail/1/2/欢迎你`">{{news.title}}</RouterLink>
<router-link :to="{name: 'xiang', params: {a: 1, b: 2, content: '欢迎你'}}">跳转</router-link>
{
name:'xiang',
path:'detail/:a/:b/:content?',
...
}
// params 传参数,一一对应,并且只能使用 name,:content?表示可传可不传
// 接收参数
route.params
6.路由的 props 配置
让路由组件更方便的收到参数(可以将路由参数作为props传给组件)
{
name:'xiang',
path:'detail/:a/:b/:content',
component:Detail,
// props的对象写法,作用:把对象中的每一组key-value作为props传给Detail组件
// props:{a:1,b:2,c:3},
// props的布尔值写法,作用:把收到了每一组params参数,作为props传给Detail组件
// props:true
// props的函数写法,作用:把返回的对象中每一组key-value作为props传给Detail组件
props(route){
return route.query
}
}
defineProps(['a','b','c'])
7.replace属性
控制路由跳转时操作浏览器历史记录的模式
浏览器的历史纪录有两种方式:push 和 replace
- push:追加历史纪录,默认的
- replace:替换当前纪录
<RouterLink replace .......>News</RouterLink>
8.编程式导航
路由组件的两个重要的属性:$route和$router变成了两个hooks
import {useRoute,useRouter} from 'vue-router'
const route = useRoute()
const router = useRouter()
console.log(route.query)
console.log(route.parmas)
console.log(router.push)
console.log(router.replace)
9.重定向
将特定的路径,重新定向到已有路由。
{
path:'/',
redirect:'/about'
}
3、pinia
安装:npm install pinia
引入:main.ts
import {createPinia} from "pinia";
const pinia = createPinia();
app.use(pinia)
1.store
是一个保存:状态,业务逻辑的实体,每个组件都可以读取、写入它
有三个概念:state,getter,action。相当于组件中data,computed,methods
src/store/count.ts
import {defineStore} from "pinia"
export const useCountStore = defineStore('count',{
// 动作
action: {},
// 状态
state() {
return {
sum: 6
}
},
// 计算
getters: {}
})
src/store/talk.ts
export const useTalkStore = defineStore('talk',{
action: {},
state() {
return {
talkList:[
{id:'01',content:'你今天有点怪,哪里怪?怪好看的!'},
{id:'02',content:'草莓、蓝莓、蔓越莓,你想我了没?'}
]
}
},
getters: {}
})
组件中使用 state 中的数据
<template>
<h2>当前求和为:{{ countStore.sum }}</h2>
</template>
<script setup lang="ts" name="Count">
import {useCountStore} from '@/store/count'
const countStore = useCountStore()
</script>
<!-- v-model.number 尽可能的转成数字 -->
<select v-model.number="n">
<option value="1">1</option>
<option value="2">2</option>
</select>
2.修改数据
- 直接修改:
countStore.sum = 666 - 批量修改:
countStore.$patch({sum: 999}) - 借助 action 修改
export const useCountStore = defineStore('count',{
// 动作
action: {
//加
increment(value:number) {
if (this.sum < 10) {
//操作countStore中的sum
this.sum += value
}
},
//减
decrement(value:number){
if(this.sum > 1){
this.sum -= value
}
}
},
...
})
组件中调用 actioncountStore.increment(9)
3.storeToRefs
借助 storeToRefs 将 store 中的对象转换 ref 对象
pinia提供的storeToRefs只会将数据做转换,不会对方法进行包裹,而Vue的toRefs会转换store中数据。
<template>
<div class="count">
<h2>当前求和为:{{sum}}</h2>
</div>
</template>
<script setup lang="ts" name="Count">
import { useCountStore } from '@/store/count'
/* 引入storeToRefs */
import { storeToRefs } from 'pinia'
/* 得到countStore */
const countStore = useCountStore()
/* 使用storeToRefs转换countStore,随后解构 */
const {sum} = storeToRefs(countStore)
</script>
4.getters
当 state 中的数据,需要经过处理后再使用时可以使用getters配置
export const useCountStore = defineStore('count',{
...
// 计算
getters:{
bigSum:(state):number => state.sum *10,
upperSchool():string{
return this.school.toUpperCase()
}
}
})
组件中读取数据:let {bigSum} = storeToRefs(countStore)
5.$subscribe
通过 store 的$subscribe()方法监听 state 及其变化,as string
talkList = JSON.parse(localStorage.getItem('talk') as string) || []
talkStore.$subscribe((mutate,state) => {
localStorage.setItem('talk', JSON.stringify(state.talkList))
})
6.store组合式写法
import {defineStore} from 'pinia'
import axios from 'axios'
import {nanoid} from 'nanoid'
import {reactive} from 'vue'
export const useTalkStore = defineStore('talk',()=>{
// talkList就是state
const talkList = reactive(
JSON.parse(localStorage.getItem('talkList') as string) || []
)
// getATalk函数相当于action
async function getATalk(){
// 发请求,下面这行的写法是:连续解构赋值+重命名
let {data:{content:title}} = await axios.get('https://api.uomg.com/api/rand.qinghua?format=json')
// 把请求回来的字符串,包装成一个对象
let obj = {id:nanoid(),title}
// 放到数组中
talkList.unshift(obj)
}
return {talkList,getATalk}
})
4、组件通信
与 vue2 的区别
- 移除事件总线,使用
mitt代替 - vuex 换成 pinia
- 把
.sync优化到 v-model 里面 - 把
$listeners所有东西合并到$attrs中 $children被砍掉了
1.props
- 父传子:属性值是非函数
- 子传父:属性值是函数
<Child :car="car" :sendToy="getToy"/>
<h4>我的玩具:{{ toy }}</h4>
<h4>父给我的车:{{ car }}</h4>
<button @click="sendToy(toy)">玩具给父亲</button>
const toy = ref('奥特曼')
defineProps(['car','sendToy'])
2.自定义事件
常用于:子 => 父,推荐使用kebab-case的事件名
区分原生事件
- 原生事件:名字是特定的,事件对象
$event包含事件相关的对象 pageX、target.... - 自定义事件:名字是任意的,事件对象
$event是调用 emit 时所提供的数据,可以是任意类型
<!--在父组件中,给子组件绑定自定义事件:-->
<Child @send-toy="toy = $event"/>
//子组件中,触发事件:
this.$emit('send-toy', 具体数据)
3.mitt
与消息订阅与发布pubsub类似,可以实现任意组件间的通信
- 接收数据的:提前绑定好事件(订阅消息)
- 提供数据的:在合适的时候触发事件(发布消息)
npm i mitt
emitter.ts
const emitter = mitt();
// 绑定事件
emitter.on('abc',(val) => {})
// 触发事件
emitter.emit('abc',123)
// 清理事件
emitter.all.clear()
// 创建并暴露mitt
export default emitter
接收数据的组件中:绑定事件,同时在销毁前解绑事件
// 绑定事件
emitter.on('send-toy',(value)=>{
console.log('send-toy事件被触发',value)
})
onUnmounted(()=>{
// 解绑事件
emitter.off('send-toy')
})
提供数据的组件中:在合适的时候触发事件
import emitter from "@/utils/emitter";
function sendToy(){
// 触发事件
emitter.emit('send-toy',toy.value)
}
4.v-model
v-model 本质
$event对于原生事件,就是事件对象,$event.target.value- 对于自定义事件,
$event是触发事件时所传递的数据
<!-- 使用v-model指令 -->
<input type="text" v-model="userName">
<!-- v-model的本质是下面这行代码 -->
<input
type="text"
:value="userName"
@input="userName =(<HTMLInputElement>$event.target).value"
>
组件上的 v-model::modelValue+update:modelValue ,
<!-- 组件标签上使用v-model指令 -->
<AtguiguInput v-model="userName"/>
<!-- 组件标签上v-model的本质 -->
<AtguiguInput :modelValue="userName" @update:model-value="userName = $event"/>
<template>
<div class="box">
<!--将接收的value值赋给input元素的value属性,目的是:为了呈现数据 -->
<!--给input元素绑定原生input事件,触发input事件时,进而触发update:model-value事件-->
<input
type="text"
:value="modelValue"
@input="emit('update:model-value',(<HTMLInputElement>$event.target).value)"
>
</div>
</template>
<script setup lang="ts" name="AtguiguInput">
// 接收props
defineProps(['modelValue'])
// 声明事件
const emit = defineEmits(['update:model-value'])
</script>
value 就是 modelValue。v-model 传递的默认 value
如果value可以更换,那么就可以在组件标签上多次使用v-model
<AtguiguInput v-model:abc="userName" v-model:xyz="password"/>
5.$attrs
用于实现当前组件的父组件,向当前组件的子组件通信(祖 -> 孙)
$attrs是一个对象,包含所有父组件传入的标签属性
$attrs会自动排除props中声明的属性(可以认为声明过的props被子组件自己“消费”了)
父组件:
v-bind="{x:100,y:200}" ==> :x=100 :y=200
<template>
<div class="father">
<h3>父组件</h3>
<Child :a="a" :b="b" :c="c" :d="d" v-bind="{x:100,y:200}" :updateA="updateA"/>
</div>
</template>
<script setup lang="ts" name="Father">
import Child from './Child.vue'
import { ref } from "vue";
let a = ref(1)
let b = ref(2)
let c = ref(3)
let d = ref(4)
function updateA(value){
a.value = value
}
</script>
子组件:
<template>
<div class="child">
<h3>子组件</h3>
<GrandChild v-bind="$attrs"/>
</div>
</template>
<script setup lang="ts" name="Child">
import GrandChild from './GrandChild.vue'
</script>
孙组件:
<template>
<div class="grand-child">
<h3>孙组件</h3>
<h4>a:{{ a }}</h4>
<h4>b:{{ b }}</h4>
<h4>c:{{ c }}</h4>
<h4>d:{{ d }}</h4>
<h4>x:{{ x }}</h4>
<h4>y:{{ y }}</h4>
<button @click="updateA(666)">点我更新A</button>
</div>
</template>
<script setup lang="ts" name="GrandChild">
defineProps(['a','b','c','d','x','y','updateA'])
</script>
6.parent
$refs用于 :父→子。$parent用于:子→父。
原理如下:
需要使用defineExpose()暴露
| 属性 | 说明 |
|---|---|
$refs | 值为对象,包含所有被ref属性标识的DOM元素或组件实例。 |
$parent | 值为对象,当前组件的父组件实例对象。 |
7.provide/inject
实现祖孙组件直接通信
- 在祖先中间中通过
provide向后代组件提供数据 - 在后代组件中通过
inject配置来声明接收数据
父组件,使用 provide 提供数据
let money = ref(100)
// 用于更新money的方法
function updateMoney(value:number){
money.value += value
}
// 提供数据
provide('moneyContext',{money,updateMoney})
provide('car',car)
注意:子组件中不用编写任何东西,是不受到任何打扰的
孙组件,使用inject接收数据,inject('car','默认值')
<button @click="updateMoney(6)">点我</button>
let {money,updateMoney} = inject('moneyContext',{money:0,updateMoney:(x:number)=>{}})
updateMoney孙组件修改祖组件的数据,{money:0,updateMoney:(x:number)=>{}}默认值
8.slot
- 默认插槽
父组件中:
<Category title="今日热门游戏">
<ul>
<li v-for="g in games" :key="g.id">{{ g.name }}</li>
</ul>
</Category>
子组件中:
<template>
<div class="item">
<h3>{{ title }}</h3>
<!-- 默认插槽 -->
<slot></slot>
</div>
</template>
- 具名插槽
父组件中:
<Category title="今日热门游戏">
<template v-slot:s1>
<ul>
<li v-for="g in games" :key="g.id">{{ g.name }}</li>
</ul>
</template>
<template #s2>
<a href="">更多</a>
</template>
</Category>
子组件中:
<template>
<div class="item">
<h3>{{ title }}</h3>
<slot name="s1"></slot>
<slot name="s2"></slot>
</div>
</template>
- 作用域插槽
数据在组件的自身,但根据数据生成的结构需要组件的使用者来决定。
数据在子组件,父使用子的数据,需要子通过插槽给父
新闻数据在News组件中,但使用数据所遍历出来的结构由App组件决定
父组件中:
<Game v-slot="params">
<!-- <Game v-slot:default="params"> -->
<!-- <Game #default="params"> -->
<ul>
<li v-for="g in params.games" :key="g.id">{{ g.name }}</li>
</ul>
</Game>
子组件中:
<template>
<div class="category">
<h2>今日游戏榜单</h2>
<slot :games="games" a="哈哈"></slot>
</div>
</template>
<script setup lang="ts" name="Category">
import {reactive} from 'vue'
let games = reactive([
{id:'asgdytsa01',name:'英雄联盟'},
{id:'asgdytsa02',name:'王者荣耀'},
{id:'asgdytsa04',name:'斗罗大陆'}
])
</script>
| 组件关系 | 传递方式 |
|---|---|
| 父子 | props,v-model,$refs,默认插槽、具名插槽 |
| 子父 | props,自定义事件,v-model,$parent,作用域插槽 |
| 祖孙,孙祖 | $attrs,provide、inject |
| 兄弟,任意组件 | mitt,pinia |
总结
5、其他API
1.shallowRef、shallowReactive
shallowRef:创建一个响应式数据,但只对顶层属性进行响应式处理。只跟踪引用值的变化,不关心值内部的属性变化shallowReactive:创建一个浅层响应式对象,只会使对象的最顶层属性变成响应式的,对象内部的嵌套属性不变。对象的顶层属性是响应式的,但嵌套对象的属性不是
const myVal = shallowRef(initVal);
const myObj = shallowReacitve({...});
通过使用
shallowRef()和shallowReactive()来绕开深度响应。浅层式API创建的状态只在其顶层是响应式的,对所有深层的对象不会做任何处理,避免了对每一个内部属性做响应式所带来的性能成本,这使得属性的访问变得更快,可提升性能。
2.readonly、shallowReadonly
readonly:创建一个对象的深只读副本
const original = reactive({...});
const readOnlyCopy = readonly(original);
对象的所有嵌套属性都将变为只读
任何尝试修改这个对象的操作都会被阻止(开发模式下会发出警告)
应用:创建不可改变的状态快照,保护全局状态或配置不被修改
shallowReadonly:作用于readonly类似,但只作用于对象的顶层属性,浅层
const original = reactive({...});
const shallowReadOnlyCopy = shallowReadonly(original);
只将对象的顶层属性设置为只读,对象内部的嵌套属性仍然是可变的。
适用于只需保护对象顶层属性的场景
3.toRaw、markRaw
toRaw:用于获取一个响应式对象的原始对象,返回的对象不再是响应式的,不会触发视图更新
这是一个可以用于临时读取而不引起代理访问/跟踪开销,或是写入而不触发更改的特殊方法,不建议保存对原始对象的持久引用,请谨慎使用
何时使用?在需要将响应式对象传递给非 Vue 的库或者外部系统时,使用toRaw可以确保他们收到的是普通对象。
let person = reactive({name: 'judian',age: 18})
let rawPer = toRaw(person)
markRaw:标记一个对象,使其永远不会变为响应式的
例如使用mockjs时,为了防止勿把其变成响应式对象可以使用这个标记
let citys = markRaw([
{id:'asdda01',name:'北京'},
{id:'asdda02',name:'上海'},
{id:'asdda03',name:'天津'},
{id:'asdda04',name:'重庆'}
])
// 根据原始对象citys去创建响应式对象citys2 —— 创建失败,因为citys被markRaw标记了
let citys2 = reactive(citys)
4.customRef
作用:创建一个自定义的ref,并对其依赖项跟踪和更新触发进行逻辑控制。
接收一个 get,set 函数
实现防抖效果(useSumRef.ts)
import {customRef } from "vue";
export default function(initValue:string,delay:number){
let msg = customRef((track,trigger)=>{
// track跟踪,trigger触发
let timer:number
return {
get(){
track() // 告诉Vue数据msg很重要,要对msg持续关注,一旦变化就更新
return initValue
},
set(value){
clearTimeout(timer)
timer = setTimeout(() => {
initValue = value
trigger() //通知Vue数据msg变化了
}, delay);
}
}
})
return {msg}
}
组件中使用:
let {msg} = useSumRef("haha",200)
- track告诉 vue 持续关注
- trigger通知 vue 我已经改变
6、新组件
1.Teleport
是一种能够将我们的组件 html 结构移动到指定位置的技术
<teleport to='body' >
<div class="modal" v-show="isShow">
<h2>我是一个弹窗</h2>
<p>我是弹窗中的一些内容</p>
<button @click="isShow = false">关闭弹窗</button>
</div>
</teleport>
teleport包裹的 modal 被放到 body 下,以 body 为父级居中展示,
解决filter:saturate(0%)饱和度,图片变灰色添加这个 modal 只在他父级居中了
2.Suspense
等待异步组件时渲染一些额外内容:组件的数据是通过异步任务获取的
- 异步组件引入
- 使用
Suspense包裹组件,并配置好default于fallback
import { defineAsyncComponent,Suspense } from "vue";
const Child = defineAsyncComponent(()=>import('./Child.vue'))
<template>
<div class="app">
<h3>我是App组件</h3>
<Suspense>
<template v-slot:default>
<Child/>
</template>
<template v-slot:fallback>
<h3>加载中.......</h3>
</template>
</Suspense>
</div>
</template>
3.全局 API 转移到应用对象
- app.component
- app.config
- app.directive
- app.mount
- app.unmount
- app.use
4.非兼容性改变
- 过渡类名
v-enter修改为v-enter-from、过渡类名v-leave修改为v-leave-from。
keyCode作为v-on修饰符的支持。v-model指令在组件上的使用已经被重新设计,替换掉了v-bind.sync。v-if和v-for在同一个元素身上使用时的优先级发生了变化。- 移除了
$on、$off和$once实例方法。 - 移除了过滤器
filter。 - 移除了
$children实例propert。