1.vue3创建:在cmd中
2.vue3的compositionAPI
和v2的options对比:
v2是把几个对象的数据放data里,把几个对象的方法放在methods里面,对于每个对象来说太分散了。v3就是会把一个对象的所有东西放在一起的
3.setup(){ return()}
做法:把export defalut{
setup(){
//对于数据,即原来的v2的data(){}
let name = '张三',//此时的数据不是响应式的,页面不会变,但是数据内容改变
let age = 18,
//对于方法,即原来的v2的methods
function changeage(){
name = '李四'
}
return{name,age,changeage} //把写的东西交出去。
}
}
①因为setup()执行是在beforecreate之前,还未进行数据代理。
②setup()中的this是undefined(v3弱化了this),setup里面不可以用this
③setup的返回也可以是个函数,return ()=>{....//函数} 会把上面所有内容覆盖掉了。
④:(很重要的)
v2中的data和methods可以和setup共存。
原来v2写法,可以用this.数据名去访问setup中的数据的,但是setup中不可以去访问外面v2写法data中的数据。
4.setup语法糖:(setup的简写)
原来setup写法:
<script lang='ts'>
export default {
name: 'person',
setup(){
//定义数据
let name='张三'
let age= 18
let tel='123123' //不要加逗号
//定义方法
function showTel(){
alert('tel')
}
function changename(){
name = '李四' //这里数据是未响应的!!
}
return {name, age, tel, showTel, changename} //这里用花括号
},
}
</script>
现在简写为:
不用写setup()和return了!!!
但是多写一个<script lang='ts' setup> //把所有的setup的内容写这里 </script>
<script lang='ts'>
export default {
name: 'person',
}
</script>
<script lang='ts' setup>
//定义数据
let name='张三'
let age= 18
let tel='123123' //不要加逗号
//定义方法
function showTel(){
alert('tel')
}
function changename(){
name = '李四' //这里数据是未响应的!!
}
</script>
对于第一个script是为了配组件名字的,第二个配置内容的!!
5.响应式数据:谁改变谁是响应式的数据
ref(参数):基本类型及对象型:
首先在<script> </script>中引入:import {ref} from 'vue'
其次是对于数据想变成响应式的: let name = ref('张三'),
对于方法的话,想让里面的值(属性名.value才能修改数据)改变则:function changename(){ name.value='李四'}
rective对象类型响应式数据:
先引入reactive,再给数据添加reactive()
<script lang='ts' setup>
import {reactive} from 'vue' //这里是花括号的
let car = reactive({name:'奔驰', price:10000})
//函数修改
function changeprice(){
car.price += 10 } //直接用对象.属性名
</script>
注意:表面上是ref处理对象型的数据,但是其实ref还是调用了reactive去处理对象型数据的。
注意:1.如果修改掉原来的reactive对象,则无法修改。如:原来是: 车=奥迪,价钱。现在改为雅迪,车价。里面全改了就不行了。要用object.assgin(要改的对象名,要改成什么的内容) 。
如果基本类型和简单的对象都用ref,深层次的对象用reactive。
如果做表单收集也最好用reactive。(看实际吧)
6.torefs:
目的:从reactive对象中拿到的数据并不是响应式的,为了使得拿到的值也是响应式的就用torefs()
import {reactive, toRefs} from 'vue' //引入这两个
let person = reactive({
name:'张三'
age:18
}) //这是个对象型的{.....}
let {name, age} = toRefs(person) //把reactive对象改成ref
.......
,let name = person.name 虽然person是reactive的,但是person.name只是取到这个值,而name又变成不响应了,所以 let{name} = toRefs(person)
7.v3的computed属性:
首先用import引入computed,可以把fullname写成方法或者计算得到,前者无缓存,后者是若两次数据同就直接从缓存拿出。
<template>
<div class="person">
姓:<input type="text" v-model="firstname"> <br>
名:<input type="text" v-model='lastname'> <br>
全名:<span> {{ fullname }} </span>
</div>
</template>
<script lang="ts">
export default {
name: 'person'
}
</script>
<script lang='ts' setup >
import {ref, computed} from 'vue'
let firstname = ref('zhang')
let lastname = ref('三')
//计算得到fullname,并且让首字母大写(slice选取第一个字符并且大写,slice选取除了第一个字符进行拼接)
let fullname = computed(( )=>{
return firstname.value.slice(0,1).toUpperCase() + firstname.value.slice(1) + '-' + lastname.value
})
</script>
缺点是:上面的计算方法中对于值来说只能读,不可修改。
因此计算属性函数用set()去修改,用get()去得到数据。而不是去用函数定义了。
let fullname = computed({
get() {
return firstname.value.slice(0, 1).toUpperCase() + firstname.value.slice(1) + '-' + lastname.value
},
set(val){
const [str1,str2]= val.split('-') //注意的是:把val以-为分隔符,
firstname.value = str1
lastname.value=str2
}
})
8.watch监视
写法: import {watch} from vue
函数:watch(监视谁, 回调函数)
用处例子:监视若数据大于10000可以请求一个优惠券。
只会监视四种数据:
① ref型的基本类型:
监视和停止监视(if判断如果newvalue大于10就调用stopwatch函数,停止监视)
② ref类型的对象类型数据:
在watch()中第三个参数添加deep:true.开启深度监听。
对于stu {年龄,对象},若只是年龄或者对象改变,并不能监听到旧值的。类似于旧房子只是桌子椅子改变了。只有整个房子改变时才能监听到新旧值。(内存地址不一样了)
③ resctive数据
reactive缺点不可以整体修改整个对象的,只能用boject.assgin()来修改
reactive定义的数据默认是开启深度监听的,而且是无法关闭的!!
但是新旧值是不变的。
④getter函数的返回值
4.1监视响应式对象中的某一个属性,该属性是基本类型的,要写成函数式。
person是响应式的,但是person. name不是响应式的。要在watch中把监视谁中person.name改写成一个回调函数的return 值。 watch(() => {person.name} , ......)
4.2监视响应式对象某一个属性,该属性是对象类型,可以直接监视或者写成函数式。若还想监视这个对象里面的细节变化就加个deep:true.
⑤包含以上三种内容的数组
watch([()=>person.name,()=>person.car], 回调函数(参数){..}) 用一个数组把所有要监视的包含进去。
9.watchEffect:达到某个值会请求。
首先引入watcheffect。
应用时不用交代监视谁,watcheffect(()=>{......//要判断的条件和结果}),不用再写监听谁了。
10.标签的ref属性:
<h2 ref = 'time'>...</h2>
创建一个time,用于存储ref标记的内容
let time = ref() //time是自己取名的,上下一致就行。
作用:加了ref只会在本页面执行关于time的,防止和其他页面id=time会混。
补充css: 在<style scoped> 这个也是只会在本页面添加属性,不影响其他页面,无脑加上!
总结:ref放在html标签获得dom对象,ref放在组件中获得是实例对象。
v3中若把ref放在组件里,需要用defineExpose()去拿实例对象中某个数据。
11.TS接口:(问题在于报错.ts不在app.json中)
1.
<script lang='ts' setup >
import {type PersonInter} from "@/types"
let person:PersonInter = {id:'123',name:'张三', age:15}
</script>
export interface PersonInter {
id:string,
name:string,
age:number
}
ts泛型:......
let person:Array<personInter(接口),....,>
12.props使用:
左边是app.vue管理所有组件的,右边是app管理的一个组件person.vue.
要用组件person:先import引入,再调用person标签。
现在app(父)给perso(子)传递数据时:
①从person的标签中传入数据
:list='personlist'
//personlist是父中的数据,list是个参数而已,别忘带冒号(冒号是读父中数据personlist, 把数据用参数list传过去)
②子接收数据:
引入defineprops: import {defineProps} from 'vue'
调用defineprops去接收传来的数据: defineProps(['list(list是参数名)'])
接收list后子页面就有相关的数据了,就可以用在需要地方: z这里用在了<li>进行v-for遍历。
③:子接收数据list时还要对数据限制接收:
子接收:defineprops <{list:persons}>() //persons是限制的类型
④:接收数据,限制类型,指定默认值(withDefaults):
withDefaults(definedProps<{list? :persons}>(), {list:()=>[...])
//list是传来的参数,persons是限制条件,后面函数是若夫没给,就用这个默认值。
13.生命周期(组件的一生):
创建,挂载,更新,卸载。
创建直接用setup,没有前后了。
v3的挂载:挂载前是 onBeforeMount( ()=> {//函数体}) 挂在前会执行箭头函数内容。
挂载是onMounted( ()=> {.....})
更新是onBeforeMount,OnUpdated...
(子先挂载完,夫再挂载完)
14自定义hooks:
①hooks单独创建一个文件夹。文件夹下面是.ts或者.js文件
②把原来在person组件中<script> </script>中的内容:
h2标签的求和方法,传一个初始值数据。 img标签的请求狗图。
</script>
<script lang='ts' setup >
import { ref, reactive } from 'vue'
import axios from 'axios'
let sum = ref(0) //数值数据
//想把狗图插入到按钮下面,先把图片用数组存一下,在img标签中遍历v-for
let dogList = reactive(['https://images.dog.ceo/breeds/pembroke/n02113023_4038.jpg']) //图片数据
//方法
function add() {
sum.value += 1
}
//再来一只,请求网址,把返回的图放在img中,失败就弹窗提示
async function getDog() {
try {
let result = await axios.get('https://dog.ceo/api/breed/pembroke/images/random') //axios请求一个图,用result接收
dogList.push(result.data.message) //把返回的message放到数组doglist中
} catch (error) {
alert(error)
}
}
</script>
③现在用hooks文件改成 useDog.ts 这个ts中包含请求狗图操作!
useSum.ts 这个ts中包含传值的数据和方法求和内容!
把相关数据和方法都封装成一个函数,并且暴漏出去。还要return返回所写的方法和数据内容
import { ref, reactive } from 'vue'
import axios from 'axios'
//把狗图插入到按钮下面,先把图片用数组存一下,在img标签中遍历v-for
export default function(){
let dogList = reactive(['https://images.dog.ceo/breeds/pembroke/n02113023_4038.jpg']) //图片数据
//再来一只,请求网址,把返回的图放在img中,失败就弹窗提示
async function getDog() {
try {
let result = await axios.get('https://dog.ceo/api/breed/pembroke/images/random') //axios请求一个图,用result接收
dogList.push(result.data.message) //把返回的message放到数组doglist中
} catch (error) {
alert(error)
}
}
return {dogList, getDog}
}
import { ref, reactive } from 'vue'
export default function(){
let sum = ref(0) //数值数据
//方法
function add() {
sum.value += 1
}
//除了要封装成函数而且暴漏接口,还要把数据和方法都return出去
return {sum, add}
}
<script lang='ts' setup >
import useDog from '@/hooks/useSum'
import useSum from '@/hooks/useSum'
const { sum, add } = useSum()
const { dosList, getDog } = useDog()
</script>
在原来的文件中引入,再调用函数,返回值用一个let{.....}来接收。
15.路由 route, 路由器 router
路由=key +value
多个路由组成路由器
①创建导航区和展示区在app.vue 根组件中
<template>
<div class="app">
<!--标题-->
<h2>路由测试</h2>
<!--导航栏-->
<div class="navigate">
<a href="#">首页</a><br/>
<a href="#">新闻</a><br/>
<a href="#">关于</a><br/>
</div>
<!--展示区-->
<div class="content">
s
</div>
</div>
</template>
<script lang="ts" setup>
</script>
<style>
</style>
②请来路由器:
首先在src下面建立router文件夹,里面放index.ts
(为了配路由规则。把路由引入到各个页面)
其次创建compoents文件夹里面放三个组件。
每个组件对应展示区跳转的页面。
最后在main.ts中配置路由器
//router文件夹是用来引入路由器
import {createRouter, createWebHistory} from 'vue-router' //引入路由器,后者是规定v3路由是用什么模式
//要引入组件。 因为电点击不同按钮会跳转不同页面,这些页面是用组件写的。
import Home from '@/components/Home.vue'
import About from '@/components/About.vue'
import News from '@/components/News.vue'
//创建路由器:用ceraterouter()函数
const router = createRouter({
history:createWebHistory(),//指定路由器是hash(后面有)
routes:[{ //写路由规则的。
path: '/home',
component: Home
},
{ //写路由规则的。
path: '/about', //path是指http网址后面/path 是/About时,会链接组件About。
component: About
},
{ //写路由规则的。
path: '/news',
component: News
},
]
})
export default router //把路由暴漏出去
app也是必须要引入的。
//引入createapp用于创建应用
import { createApp } from 'vue'
//引入app根组件
import App from './App.vue'
//引入路由器
import router from './router'
//创建一个应用
const app=createApp(App)
//使用路由器
app.use(router)
//挂载整个应用到app容器中
app.mount('#app')
上面几步都是说配置好路由,/path后会跳转页面。只是制定了规则但是还没能让路由器把页面放到展示区呢。
③:在根组件中的展示区引入routerview标签,并且在script中import引入。
此时修改网址:http://localhost:5173/home就可以跳转
④最后实现点击链接自动跳转
引入routerlink,并且在导航区中的原来html中<a href>标签全部换成<routerlink to='/path'>..</routerlink>
补充路由标签中的css: active-class='类名' //表示这个路由激活时会配上这个类的css,每个标签都可以添加这个类,选谁谁应用css。
16.路由器工作模式:
history:优点http中无#,美观。但是服务器刷新会有问题
hash模式:兼容性好。有#,且seo优化差
17.命名路由:
routes:[{ //写路由规则的。
name:'shouye' //这个路由名
path: '/home',
component: Home
},
可以根据路由名或者path去跳转到页面<routerlink :to{//name:'shouye' 或者//path:“/path}>
18嵌套路由
案例:在路由测试页面中导航栏有三个按钮,可以对应跳转三个展示区(新闻页。首页,关于页面),这个已经用路由实现。
现在在新闻页面中导航栏有几个标题,每个标题对应跳转不同的页面新闻内容,又要在新闻页面中整一个路由。
在路由配置新闻页面的配置后面加上children{path不用带/,和compent组件 },并且在新闻业的展示区引入routerview标签。
19.路由-query参数
在新闻页面有 导航区(各种标题)和展示区(要跳转内容)
在新闻业面中引入路由,导航栏的每个li标签中添加routerlink标签,这个标签就是点击这个标题就能跳转到路由绑定的页面。
跳转的页面是一个组件,要有个组件.vue,绑定新闻页面和这个组件通过在路由文件router中路由配置,给新闻业面配置的子孩子是这个组件(配了路径和compoent)。
现在是新闻页面的script中有了数据,想把这些数据传到跳转之后的页面里面去。
通过新闻页面中路由标签中query传入参数给子页面,子页面那边用useroute来接受数据。
子页面中 import {useRoute} from 'vue-router'
let route = useRoute //接收到数据在route中,而route是一个对象吗,在route中的.query中是传过来的参数。
想用数据就直接插值语法{{route.query.数据名}}
20.路由中的-params参数
传参数过程:
在router配置文件中的新闻页面的children里面的path:/路径名/:a /:b
用a,b来占位,表示这里穿的是参数。 而且不能用path跳转了,只能用name跳转。
其次,在新闻页面的导航栏的路由标签中<routerlink to="/详情页的路径/a/b"> //a,b是要穿的数据。
详情页接收:
引入route,route()返回的数据中有params,直接插值用。
(既然占位了。就要传参数,如果没有要传的参数,就在占位参数后面加上❓)
此图还差个新闻业面路由标签中传参数 。
21路由的props配置
props的底层逻辑,虽说传的是参数,但是底层传的是键值对。
第一种:props:true
只能baparams参数传给跳转页。
defineprop()是接收父组件传来的参数的。
第二种:自己决定把什么传给路由组件(跳转页)
props(参数){
return 参数.query
}
22.路由-replace属性
push:路由每次跳转的页面都会存在栈中,由页面箭头来返回或前进浏览历史记录(默认是 push)
replace: 浏览页面会覆盖上一次浏览页面。 在路由标签中添加replace就可以修改模式。
23.编程式导航♥
就是脱离标签routerlink。
应用场景: 跳转页面时要一些条件或者要干些其他事,而不是直接点击链接就跳转。
写法:
在主页引入userouter(注意是路由器!)。
调用userouter()
此图目的是点击之后三秒在跳转到新闻页面上。
在挂载(生成dom放在页面上之前做这个事情)三秒后计时器调用,执行router.push(..),就是push到新闻业。把新闻业放在栈中。 (router类似于董事长,让董事长把新闻业推到栈中去)
router.push()是一个函数,就是用来跳转页面的,和routerlink中 :to....是实现相同功能的,只是push()可以添加一些事件,所有可以给路由标签绑定一个点击事件,事件执行函数就是router.push.
interface是对news传的参数进行限制的。否则会报错any,或者写成news:any也行就是对其不限制了但不推荐。
24pinia
多个组件共享数据采用pinia。
1.pinia读取数据
pinia是用来存取数据之类的,pinia的具体体现在根目录下的store文件夹,store中文件时.ts,这里面文件和component组件中文件时对应着管理的。
如:store中的count.ts 存取数据是对应compoents中的count.vue的。
首先引入definestore,state()类似一个仓库,要存的数据直接写在state中。并且把接口暴漏。
2.使用store中数据:
先引入接口useCountStore,再调用。
解释:若是响应式对象里面数据是ref类型的,取数据的值时直接拿数据名,不用再.value了
拿state的数据有两种方式:
返回的countStore对象中有sum,也有$state里面有个sum,两种都可以得到值。
(这个sum就是store文件中用state(‘存储名字’),{satate....} 中的sum存储名。 就是state中存的数据给它取个名,这个名就是前面state的第一个参数。)
3.pinia修改数据:
第一种:直接修改数据
stroe中sum数据用countstore.sum可以取出来,如果要修改sum。直接再按钮上绑定点击事件让countstore.sum +=1就行。
第二种:若要批量修改数据用方法.$patch()
countstore.$patch({
存储名:值,
存储名2:值
})
第三中修改方式: actions()
好处是可以对修改变量进行限制条件之类的,复用性好。
在store文件大的state并排添加actions:{}对象,对象里面写要执行的动作increament函数,再用的时候调用该函数。
注意:actions要是想要拿到state中的数据要用this.值名 才能取到
4.storetoRef:
作用:从store文件中取出的数据都不是响应式的了,如果给取出的countstore加上torefs,那么countstore对象包含的所有方法和数据都变成响应式的了,代价大,而storeRef只会让countstore中的数据变成响应式的。
5.getters属性
作用:当store中state中的数据需要处理之后再使用时,就在state并排加上getters属性,类似于计算属性。
注意::string是因为返回报错为 ....any,解决方法就是upperschool() :string规定一下返回类型是string类型的。
6.$subscribe作用:
类似于watch监听。
store中state的数据发生了改变时,暴漏的接口名.$subscribe(()=>{..})就会被调用的。
25.组件通信:♥♥
1.props实现子父互传(大盒子嵌套一个小盒子)
①父传子:
首先在父中引入子组件import,在父中要用到子组件标签才能显示子组件<子组件/>,
父给子传数据时在子组件标签中写入穿的信息<Child :car="car" /> (父亲有个数据叫 let car=...)。
子接收父的信息:利用defineProps(要接受哪个参数//从父的子标签中要传的参数找)
接收后直接用插值语法用这个参数就行。
②子传父:
首先父给子一个函数,子收到这个函数后如果需要给父传参数就调用这个函数。
父亲定义的是gettoy(),通过子组件标签把这个函数传过去并且用变量sendtoy作为传的参数并且接收,子需要时调用sendtoy()并且传入数据,可以执行这个函数。父这边就可以直接调用变量访问值。
2.自定义事件:
补充dom知识:
当要传三个参数,但是只想传两个参数时,就用$event占位符,结果会输出c是一个事件。
自定义事件:专门用于子传给父!!!
写法:①在父页面的孩子标签中 写 @自定义事件名:“回调函数名” //当自定义事件触 发时,就会执行回调函数。
②在子页面中声明这个自定义事件:const emit = defineEmits(['自定义事件 名'])
③在任何地方只要写 emit(‘自定义事件名’) 就可以在此处触发该事件。
3.emitt实现任意的组件通信:
补充: $bus, pubsub, mitt都是提前绑定事件。
提供数据的:(发布消息)在合适时候触发事件
接收数据的:(提前订阅消息)提前绑定好事件。(给谁绑定的事件,就找谁触发事件。)
用法:首先emitter文件引入并且暴漏接口
在main.ts中引入emitter文件:import enitter from 文件名
例子:
用emitter绑定和解绑事件:
孩子2传给孩子1如图。
注意,组件在卸载时要解绑事件。(写在绑定事件的那个页面上)
onunmounted ( ()=> {emitter.off('要解绑的事件')})
4.v-model通信:子传父,父传子都可以。
补充:defineProps
的大部分用法是进行父子组件传值。defineemitt:子组件触发父组件的的事件,并且进行传值。
v-model在html标签和组件标签传的方式不一样的。
在vue3中@update:modelvalue,@是个绑定事件,@后面就是绑定事件名,没有其他含义。
在普通标签中第五行等价于第六行。
注意:v-model=‘username’,在等价时如果不想写modelvalue这种太长名,在第一种写法中,改为v-model : 别名 =‘username’,这样以后写等价时候可以用别名了。而且这种可以在组件中写多个v-model:别名。
5.$attrs:用于祖孙互传的。
补充:父传子时,如果传多个参数,但是子只收一个参数,剩下参数都在$attrs里面存储着,直接用$attrs插值就可以看到了,不用去接收。
在爷页面中写一些数据,并且在爷页面中的父组件标签中传给父,在父中写入孙标签,在孙标签中写入v-blind = "$attrs'' ,在孙页面中正常接收数据内容。
6.$refs:父传子。 $parent:子传父。
如果说父只想获得一个孩子的内容,就用ref=‘名字’,用来标识孩子。
但是父要得到所有孩子内容,就用$refs,这个会把孩子暴漏的所有内容用数组存起来。
孩子用 defineExpose({computer,book})把想让父亲访问的内容暴漏出去。
7.provide 和inject : 用于祖孙传递(没有经过他人传递)
首先在爷页面提供给后代能访问的,后代用inject接收数据。
在孙中接收数据时,是需要设置一下默认值的。
8.用pinia传递。
9.插槽:
① 默认插槽:
案例:一个父组件里面含有三个子组件,而三个子组件内容各不相同。
做法:在父组件中子组件标签用一对标记,在子组件标签内写入内容。 在子页面中用slotE标签来占位,表示父页面的子组件的内容要放在子组件的位置。
如果说父页面中没有传东西过来时就会用默认内容代替。
父页面中引用了三次子组件标签就会在页面上呈现三次。
<slot>默认内容<slot/> 如果说父没给子传内容,就会把默认内容显示在页面上。
② 具体插槽:
应用于:每个子组件中含有多个内容,每个内容在每个子组件的插槽位置是不一样的。
把父页面中子组件的内容用<template>标签包含起来,并且指明v-slot=‘名字’。 在子组件中在<slot>标签中用name=‘名字’去找其插槽位置。
【语法糖: #s2 就是v-slot=‘s2’】
③:作用域插槽
应用于:数据在孩子那里,但是父亲要访问孩子,才能在父页面中使用这些数据。
【子组件维护自己的数据,但是结构是由父组件决定的!!!】
子组件中含有数据games,用标签slot中:youxi=‘games’ 把游戏内容传给插槽使用者(父),父用v-slot=‘参数名’ 去接收传来的所有的数据(用数组保存所有内容)。 父就可以访问孩子传来的所有的内容了。
26.其他api:
1.shallowref:提高效率。只关注第一层。
只会处理浅层的ref。 例如person.value.name=’ci‘,就无法使用了,person.value就已经是第一层的响应了。
2.readonly:
s2=readonly(s1)可以只是读出s1,在s2上去做修改,但是s1还是不会被修改掉的。
shallowreadonly(): 只有第一层会被保护,只能读。 如果是深层的会被修改的。
3.toraw和markraw:
toraw用于获取一个响应式对象,返回值不再是响应式的了。一般用于将响应式对象传给别人时,又不想让对方直接修改数据内容。
markraw:标记一个对象,这个对象永远不会是响应式的对象。
4.customref:♥(自定义ref)
作用:创建一个自定义的ref,并对其依赖项跟踪和更新触发进行控制。
优点在于: 对于响应式的数据,一修改页面就会立马修改,现在想要的是修改一秒之后页面再修改,就只能用自定义的ref了。
首先这个自定义ref一般写在hooks里面的,被封装一下之后暴漏出去的。
initvalvalue是初始值,delay是定时器的值(是传进来的)
customref()里面track是用来让vue跟踪数据的,trigger是让vue直到数据改变了。
5.teleport:
应用场景: 父盒子里面有个小盒子,小盒子的定位是以整个窗口为标准的,如果父盒子的图片增加 filter: saturate(0%) 会让图片变成灰色的,此时小盒子的定位就会以父元素为参考对象而不是整个窗口了。
此时就要用传送门teleport。 (就是把标签teleport中间的内容插入到某个地方去。)
6.suspense:?
应用场景:等待异步事件时渲染一些内容,增加客户体验。
使用步骤:引入异步组件,使用suspense包裹组件,配置default和fallback。???
7.v3非兼容性改变 (面试题)♥♥