10个常见的使用场景,助你从 Vue2 丝滑过渡到 Vue3 !
本文为稀土掘金技术社区首发签约文章,14天内禁止转载,14天后未获授权禁止转载,侵权必究!
相信有很多这样的兄弟,学了 Vue3 的各种 API 和新特性,但公司项目依然使用的是 Vue2 ,也不知道自己的水平能否上手 Vue3 项目。其实你学的是零碎的知识点,缺少真实的使用场景。
今天就把实战过程中遇到的十个场景分享给大家,结合尤大大推荐的script setup,希望你能从 Vue2 丝滑过渡到 Vue3!
场景一:父子组件数据传递父组件数据传递到子组件Vue3 中父组件同样是通过属性传递数据,但子组件接受数据的方式和 Vue2 不同。在script setup中,props 需要使用defineProps()这个宏函数来进行声明,它的参数和 Vue2 props 选项的值是一样的。
!--父组件--
scriptsetup
importChildViewfrom'./ChildView.vue'
/script
template
ChildViewsome-prop="parentmessage"/
/template
!--子组件--
scriptsetup
constprops=defineProps({
someProp:{
type:String,
required:true
}
})
console.log(props.someProp)//parentmessage
/script
template
!--使用someProp或props.someProp--
div{{someProp}}/div
div{{props.someProp}}/div
/template
注意:defineProps、defineEmits、defineExpose和withDefaults这四个宏函数只能在script setup中使用。他们不需要导入,会随着script setup的处理过程中一起被编译。
子组件数据传递到父组件Vue2 中子组件数据传递到父组件,通常是使用$emit触发一个自定义事件来进行传递。但$emit无法在script setup中使用,这时候我们需要使用defineEmits():
!--子组件--
scriptsetup
constemit=defineEmits(['someEvent'])
functiononClick(){
emit('someEvent','childmessage')
}
/script
template
button@click="onClick"点击/button
/template
!--父组件--
scriptsetup
importChildViewfrom'./ChildView.vue'
functionsomeEvent(value){
console.log(value)//childmessage
}
/script
template
ChildView@some-event="someEvent"/
/template
父组件使用子组件数据在script setup中,组件的属性和方法默认都是私有的。父组件无法访问到子组件中的任何东西,除非子组件通过defineExpose显式的暴露出去:
!--子组件--
scriptsetup
import{ref}from'vue'
constmsg=ref('hellovue3!')
functionchange(){
msg.value='hivue3!'
console.log(msg.value)
}
//属性或方法必须暴露出去,父组件才能使用
defineExpose({msg,change})
/script
!--父组件--
scriptsetup
importChildViewfrom'./ChildView.vue'
import{ref,onMounted}from'vue'
constchild=ref(null)
onMounted(()={
console.log(child.value.msg)//hellovue3!
child.value.change()//hivue3!
})
/script
template
ChildViewref="child"/ChildView
/template
场景二:组件之间双向绑定大家都知道 Vue2 中组件的双向绑定采用的是v-model或.snyc修饰符,两种写法多少显得有点重复,于是在 Vue3 中合成了一种。Vue3 统一使用v-model进行处理,并且可以和多个数据进行绑定,如v-model:foo、v-model:bar。
v-model等价于:model-value="someValue"和@update:model-value="someValue = $event"
v-model:foo等价于:foo="someValue"和@update:foo="someValue = $event"
下面就是一个父子组件之间双向绑定的例子:
!--父组件--
scriptsetup
importChildViewfrom'./ChildView.vue'
import{ref}from'vue'
constmsg=ref('hellovue3!')
/script
template
ChildViewv-model="msg"/
/template
!--子组件--
scriptsetup
defineProps(['modelValue'])
constemit=defineEmits(['update:modelValue'])
/script
template
div@click="emit('update:modelValue','hivue3!')"{{modelValue}}/div
/template
子组件可以结合input使用:
!--子组件--
scriptsetup
defineProps(['modelValue'])
constemit=defineEmits(['update:modelValue'])
/script
template
input:value="modelValue"@input="emit('update:modelValue',$event.target.value)"/
/template
如果你觉得上面的模板比较繁琐,也可以结合computed一起使用:
!--子组件--
scriptsetup
import{computed}from'vue'
constprops=defineProps(['modelValue'])
constemit=defineEmits(['update:modelValue'])
constnewValue=computed({
get(){
returnprops.modelValue
},
set(value){
emit('update:modelValue',value)
}
})
/script
template
inputv-model="newValue"/
/template
场景三:路由跳转,获取路由参数在 Vue2 中我们通常是使用this.$router或this.$route来进行路由的跳转和参数获取,但在script-setup中,是这些方法无法使用的。我们可以使用vue-router提供的useRouter方法,来进行路由跳转:
scriptsetup
import{useRouter}from'vue-router'
constrouter=useRouter()
functiononClick(){
router.push({
path:'/about',
query:{
msg:'hellovue3!'
}
})
}
/script
当我们要获取路由参数时,可以使用vue-router提供的useRoute方法:
scriptsetup
import{useRoute}from'vue-router'
constroute=useRoute()
console.log(route.query.msg)//hellovue3!
/script
场景四:获取上下文对象Vue3 的setup中无法使用this这个上下文对象。可能刚接触 Vue3 的兄弟会有点懵,我想使用this上的属性和方法应该怎么办呢。虽然不推荐这样使用,但依然可以通过getCurrentInstance方法获取上下文对象:
scriptsetup
import{getCurrentInstance}from'vue'
const{ctx}=getCurrentInstance()
console.log(ctx)//和this的属性一样
/script
这样我们就可以使用$parent、$refs等,干自己想干的事情了,下面是我打印出来的完整属性。
image.png场景五:插槽的使用在 Vue2 的中一般是通过slot属性指定模板的位置,通过slot-scope获取作用域插槽的数据,如:
!--父组件--
scriptsetup
importChildViewfrom'./ChildView.vue'
/script
template
divparentdiv
ChildView
templateslot="content"slot-scope="{msg}"
div{{msg}}/div
/template
/ChildView
/template
!--子组件--
template
divchild/div
slotname="content"msg="hellovue3!"/slot
/template
在 Vue3 中则是通过v-slot这个指令来指定模板的位置,同时获取作用域插槽的数据,如:
!--父组件--
scriptsetup
importChildViewfrom'./ChildView.vue'
/script
template
divparent/div
ChildView
templatev-slot:content="{msg}"
div{{msg}}/div
/template
/ChildView
/template
!--ChildView也可以简写为:--
ChildView
template#content="{msg}"
div{{msg}}/div
/template
/ChildView
!--子组件--
template
divchild/div
slotname="content"msg="hellovue3!"/slot
/template
注意:v-slot在 Vue2 中也可以使用,但必须是 Vue2.6+ 的版本。
场景六:缓存路由组件缓存一般的动态组件,Vue3 和 Vue2 的用法是一样的,都是使用KeepAlive包裹Component。但缓存路由组件,Vue3 需要结合插槽一起使用:
//Vue2中缓存路由组件
KeepAlive
RouterView/
/KeepAlive
//Vue3中缓存路由组件
RouterViewv-slot="{Component}"
KeepAlive
Component:is="Component"/Component
/KeepAlive
/RouterView
一个持续存在的组件可以通过onActivated()和onDeactivated()两个生命周期钩子注入相应的逻辑:
scriptsetup
import{onActivated,onDeactivated}from'vue'
onActivated(()={
//调用时机为首次挂载
//以及每次从缓存中被重新插入时
})
onDeactivated(()={
//调用时机为从DOM上移除、进入缓存
//以及组件卸载时
})
/script
场景七:逻辑复用Vue2 中逻辑复用主要是采用mixin,但mixin会使数据来源不明,同时会引起命名冲突。所以 Vue3 更推荐的是全新的Composition Api。
下面是鼠标跟踪的例子,我们可以把逻辑提取出来:
//mouse.js
import{ref,onMounted,onUnmounted}from'vue'
//按照惯例,组合式函数名以use开头
exportfunctionuseMouse(){
//组合式函数管理的数据
constx=ref(0)
consty=ref(0)
functionupdate(event){
x.value=event.pageX
y.value=event.pageY
}
//组合式函数可以挂靠在所属组件的生命周期上,来启动和卸载副作用
onMounted(()=window.addEventListener('mousemove',update))
onUnmounted(()=window.removeEventListener('mousemove',update))
//通过返回值暴露所管理的数据
return{x,y}
}
这时候在组件中我们就可以直接使用mouse.js暴露的数据了。
scriptsetup
import{useMouse}from'./mouse.js'
const{x,y}=useMouse()
/script
templateMousepositionisat:{{x}},{{y}}/template
我们还可以在一个组件中引入多个组合式函数,或者在一个组合式函数中引入其他的组合式函数,这个比较简单,我就不演示了。接下来,我们看看使用异步方法的组合式函数。
在做异步数据请求时,我们通常需要处理三个不同的状态:加载中、加载成功和加载失败。获取这些状态的逻辑是通用的,我们可以把它提取出来:
//request.js
import{ref}from'vue'
exportfunctionuseRequest(url){
constdata=ref(null)
consterror=ref(null)
axios.get(url)
.then((res)=(data.value=res.data))
.catch((err)=(error.value=err))
return{data,error}
}
现在我们在组件中只需要:
scriptsetup
import{useRequest}from'./request.js'
const{data,error}=useRequest('http://...')
/script
template
divv-if="data"Datais:{{data}}/div
divv-else-if="error"Errormessageis:{{error.message}}/div
divv-elseLoading.../div
/template
任何组件都可以使用上面这个逻辑,这就是逻辑复用。是不是可以节省很多重复的代码,感觉摸鱼时间又要增加了~
场景八:生命周期Vue3 的生命周期和 Vue2 相比,有以下改动:
Vue3生命周期钩子都以on开头,并且需要在组件中手动导入。
scriptsetup
import{onMounted}from'vue'
onMounted(()={
console.log('onMounted')
})
/script
Vue3 取消了beforeCreate和created钩子。如果需要在组件创建前注入逻辑,直接在script setup中编写同步代码就可以了。如果这几个钩子同时存在,setup的执行顺序要优先于beforeCreate和created。
Vue3 中组件卸载的钩子名称有变化,beforeDestroy改为onBeforeUnmount,destroyed改为onUnmounted。
场景九:全局 APIVue2 中的全局属性或全局方法,是在构造函数 Vue 的原型对象上进行添加,如:Vue.prototype.$axios=axios。但在 Vue3 中,需要在app实例上添加:
//main.js
app.config.globalProperties.$axios=axios
在组件中使用:
scriptsetup
import{getCurrentInstance}from'vue'
const{proxy}=getCurrentInstance()
proxy.$axios.get('http://...')
/script
Vue3 中其他的全局 API,如directive、component等,跟 Vue2 的用法都差不多,只不过一个是在 Vue 上调用,一个是在app实例上调用:
//main.js
//全局自定义指令
app.directive('focus',{
mounted(el){
el.focus()
}
})
//全局自定义组件
importCustomCompfrom'./components/CustomComp.vue'
app.component('CustomComp',CustomComp)
需要注意的是,Vue3 废弃了filter这个方法,因为通过函数或computed可以实现一样的功能。
常见十:与 TypeScript 结合使用与TypeScript结合使用,我们只需要在script setup中添加lang="ts"就可以了。下面是一些和TypeScript结合使用的例子。
为 props 标注类型运行时声明。当使用script setup时,defineProps()宏函数支持从它的参数中推导类型:
scriptsetuplang="ts"
constprops=defineProps({
foo:{type:String,required:true},
bar:Number
})
props.foo//string
props.bar//number|undefined
/script
这被称为运行时声明,因为传递给defineProps()的参数会作为运行时的 props 选项使用。
基于类型的声明。我们还可以通过泛型参数来定义 props 的类型,这种方式更加常用:
scriptsetuplang="ts"
interfaceProps{
foo:string
bar?:number
}
constprops=definePropsProps()
/script
这被称为基于类型的声明,编译器会尽可能地尝试根据类型参数推导出等价的运行时选项。这种方式的不足之处在于,失去了定义 props 默认值的能力。为了解决这个问题,我们可以使用withDefaults宏函数:
scriptsetuplang="ts"
interfaceProps{
msg?:string
labels?:string[]
}
constprops=withDefaults(definePropsProps(),{
msg:'hellovue3!',
labels:()=['one','two']
})
/script
为 ref() 标注类型默认推导类型。ref 会根据初始化时的值自动推导其类型:
import{ref}from'vue'
constyear=ref(2022)
year.value='2022'//TSError:不能将类型string分配给类型number
通过接口指定类型。有时我们可能想为 ref 内的值指定一个更复杂的类型,可以使用Ref这个接口:
import{ref}from'vue'
importtype{Ref}from'vue'
constyear:Refstring|number=ref('2022')
year.value=2022//成功!
通过泛型指定类型。我们也可以在调用ref()时传入一个泛型参数,来覆盖默认的推导行为:
constyear=refstring|number('2022')
year.value=2022//成功!
为 reactive() 标注类型默认推导类型。reactive()也会隐式地从它的参数中推导类型:
import{reactive}from'vue'
constbook=reactive({title:'Vue3指引'})
book.year=2022//TSError:类型{title:string;}上不存在属性year
通过接口指定类型。要显式地指定一个reactive变量的类型,我们可以使用接口:
import{reactive}from'vue'
interfaceBook{
title:string
year?:number
}
constbook:Book=reactive({title:'Vue3指引'})
book.year=2022//成功!
其他 API 与TypeScript结合使用的方法和上面大同小异,这里我就不一一列举了。具体可以参考这篇文章:如何为 Vue3 组件标注 TS 类型,看这个就够了!。
小结以上就是我在 Vue3 项目中遇到最多的场景,如果你掌握了 Vue3 常用的 API 和今天这些场景,相信参与 Vue3 项目的开发是没有问题了。当然如果要用好 Vue3 ,可能还需要对Pinia、Vite等相关生态有一个深入的了解。后面我也会持续分享 Vue3 的使用技巧及相关生态,希望你尽早掌握 Vue3!
有问题欢迎在评论区留言,如果觉得今天的分享对你有所帮助,记得点赞支持一下!??
参考文档:Vue3 官网
阅读原文
网站开发网络凭借多年的网站建设经验,坚持以“帮助中小企业实现网络营销化”为宗旨,累计为4000多家客户提供品质建站服务,得到了客户的一致好评。如果您有网站建设、网站改版、域名注册、主机空间、手机网站建设、网站备案等方面的需求...
请立即点击咨询我们或拨打咨询热线:13245491521 13245491521 ,我们会详细为你一一解答你心中的疑难。 项目经理在线