文章目录
uni-app组件
在实际制作页面的过程中,我们不会在一个页面中布局所有参数,而是将页面分为多个部分,每个部分都是一个组件。最后在页面中调用这些组件,形成页面。
创建组件
步骤:1. 选中项目-右键-新建-2.目录,创建一个名为components的目录。
2. 选中components-右键-新建组件。
3. 输入组件的名称,记得要选中左下角的创建同名目录。组件名称有两种命名方式:一种是驼峰的形式,如:UserInfo;一种是官方形式,如:uni-icon。
调用组件
按照刚刚说的命名方式,创建组件UserInfo,定义文字与图片,然后创建页面demo,在demo中调用组件,代码这样写:
<template>
<view class="">
<UserInfo></UserInfo>
</view>
</template>
<script setup>
</script>
<style lang="scss" scoped>
</style>
成功调用组件了:
使用props在组件中进行数据传递
多个页面有时会共用一个组件,但会对组件部分内容进行修改,这时就要用到props进行数据传递,修改组件。
重新定义组件UserInfo,然后在页面中写入三个UserInfo,这时页面会出现三个一样的布局:
现在尝试去修改,创建新页面demo作为页面,UserInfo是组件。要向组件中传值,首先要确保UserInfo可以接收到页面传来的命令,先在UserInfo声明defineProps,代码格式如下:
<template>
<view class="userinfo">
<image :src="avatar" mode="" class="avatar"></image>
<view class="username">{{username}}</view>
</view>
</template>
<script setup>
defineProps(['username','avatar'])
</script>
<style lang="scss" scoped>
.userinfo{
width: 100%;
height: 200px;
background: #ccc;
display: flex;
align-items: center;
justify-content: center;
flex-direction: column;
image{
width: 100px;
height: 100px;
border-radius: 50%;
}
.username{
padding: 10px 0 ;
font-size: 20px;
}
}
</style>
然后就可以在父组件页面中去设置了,代码格式如下:
<template>
<view class="">
<UserInfo username = "老三" avatar = "../../static/pic1.jpg"></UserInfo>
<UserInfo username = "王五" avatar = "../../static/pic2.jpg"></UserInfo>
<UserInfo username = "李四" avatar = "../../static/pic3.jpg"></UserInfo>
</view>
</template>
<script setup>
</script>
<style lang="scss" scoped>
</style>
页面把值传递给了组件,这是最终的效果,可以看到相同的组件拥有不同的文字和图片:
要是想在组件中对页面传过来的值进行修改,还得用到computed计算属性,且define要定义成常量。比如这里我们要在文字后面加上一个“@”,要这样写:
<template>
<view class="userinfo">
<image :src="avatar" mode="" class="avatar"></image>
<view class="username">{{myname}}</view>
</view>
</template>
<script setup>
import {computed} from "vue"
const prop = defineProps(['username','avatar'])
const myname = computed(()=>prop.username + "@")
</script>
<style lang="scss" scoped>
.userinfo{
width: 100%;
height: 200px;
background: #ccc;
display: flex;
align-items: center;
justify-content: center;
flex-direction: column;
image{
width: 100px;
height: 100px;
border-radius: 50%;
}
.username{
padding: 10px 0 ;
font-size: 20px;
}
}
</style>
效果:
prop校验与prop默认值用法及循环遍历数组对象
刚刚我们在页面中写入了三个UserInfo,并为其定义了username名称,现在删除第二个UserInfo中的username,名称一栏就会空出了,如下图。我们可以设置一个默认值,这样没有定义名称时就会有默认名称显示了。
用另一种方式定义defineProps,就可以设置默认值了,如代码所示:
<template>
<view class="userinfo">
<image :src="avatar" mode="" class="avatar"></image>
<view class="username">{{username}}</view>
</view>
</template>
<script setup>
const prop = defineProps({
username:{
type:String,
default:"匿名"
},
avatar:{
type:String,
default:"../../static/logo.png"
}
})
</script>
<style lang="scss" scoped>
.userinfo{
width: 100%;
height: 200px;
background: #ccc;
display: flex;
align-items: center;
justify-content: center;
flex-direction: column;
image{
width: 100px;
height: 100px;
border-radius: 50%;
}
.username{
padding: 10px 0 ;
font-size: 20px;
}
}
</style>
刚刚我们给图片也设置了默认值,那就把第二个UserInfo中的图片也删除,看看设置默认值后的效果:
刚刚我们设置的名称和图片,放到对象中去会显得更加简洁。接下来,尝试传递对象给组件:首先,要在组件中设置defineProps接受obj,并在template中渲染,然后在页面中script区域定义对象,并在template中调用。要注意的是,在给对象设置默认值时,要写函数的形式,return一下。
页面代码:
<template>
<view class="">
<UserInfo v-for="(item,index) in userinto" :obj = "item"></UserInfo>
</view>
</template>
<script setup>
import {ref} from "vue" ;
const userinto = ref([
{name:"王二麻子",avatar:"../../static/pic1.jpg"},
{name:"王五子",avatar:"../../static/pic2.jpg"},
{name:"张三子",avatar:"../../static/pic3.jpg"},
]
)
</script>
<style lang="scss" scoped>
</style>
子组件代码:
<template>
<view class="userinfo">
<image :src="obj.avatar" mode="" class="avatar"></image>
<view class="username">{{obj.name}}</view>
</view>
</template>
<script setup>
defineProps({
obj:{
type:Object,
default(){
return{name:"匿名",avatar:"../../static/logo.png"}
}
}
})
</script>
<style lang="scss" scoped>
.userinfo{
width: 100%;
height: 200px;
background: #ccc;
display: flex;
align-items: center;
justify-content: center;
flex-direction: column;
image{
width: 100px;
height: 100px;
border-radius: 50%;
}
.username{
padding: 10px 0 ;
font-size: 20px;
}
}
</style>
插槽slots及具名插槽实现组件高度定制化
插槽可以让我们定制一个更加强大的组件,新建组件,同时,创建两个页面demo1和demo2,组件分为三个区域,代码如下:
<template>
<view class="layout">
<view class="header">
header区域
</view>
<view class="main">
中心内容区
</view>
<view class="footer">底部区域</view>
</view>
</template>
<script setup>
</script>
<style lang="scss">
.layout{
.header{
height: 100px;
background: #cfcfcf;
}
.footer{
height: 120px;
background: orange;
}
}
</style>
在demo1和demo2中调用这个组件,此时,demo1和demo2的页面是一摸一样的。尝试分别定义这两个页面,组件中有三个区域header、main、footer,我们要定义中间的main,就在main内放入一个标签,然后就可以去demo1和demo2的组件标签中定义,下面是demo1的代码,在组件标签内填写任意内容;在demo2相应区域填写其他内容,这样就实现了两个页面共用一个组件,但实现了个性化定制。
<template>
<view class="">
<Pin-layout>
个性化定制内容
</Pin-layout>
</view>
</template>
<script setup>
</script>
<style lang="scss" scoped>
</style>
若是还想定制header和footer的界面,就不能只在组件中写上slot标签了,要使用具名插槽为slot定义名称,就像这样:
<template>
<view class="layout">
<view class="header">
<slot name="header"></slot>
</view>
<view class="main">
<slot name="main"></slot>
</view>
<view class="footer">
<slot name="footer"></slot>
</view>
</view>
</template>
<script setup>
</script>
<style lang="scss">
</style>
页面中,用template标签,使用v-slot属性,它还有一个简写,就是用#代替它,demo1示例代码如下:
<template>
<view class="">
<Pin-layout>
<template v-slot:header>
首页头部
</template>
<template #main>
首页的内容
</template>
</Pin-layout>
</view>
</template>
<script setup>
</script>
<style lang="scss" scoped>
</style>
组件中emit的声明触发事件
前面都是页面向组件传值,页面传递给组件的是单向数据流,组件是没法对值进行修改的。现在试着从组件返回值给页面。
在组件中,定义@click= “$emit(‘add’,123)” 括号前面是数据名称,后面是数据的值,这里我们设置随机数:
<template>
<view>
子组件
<button @click="$emit('add',Math.random())">按钮</button>
</view>
</template>
<script setup>
</script>
<style lang="scss" scoped>
</style>
在页面中,定义@add="onAdd",等于是接受add,然后在script中新建常量。
<template>
<view class="">
<Pin-child @add= "onAdd"></Pin-child>
</view>
</template>
<script setup>
const onAdd = function(e){
console.log(e);
}
</script>
<style lang="scss" scoped>
</style>
页面成功接收到了组件的返回值:
这种方式用的比较少,用的更多的是在组件的script代码中定义。用常量定义defineEmits要返回的数据名称,然后,新建函数,定义数据名称与数据的值,最后在template中调用该函数,例如:
<template>
<view>
子组件
<button @click="onClick">按钮</button>
</view>
</template>
<script setup>
const emit = defineEmits(["add"])
function onClick(){
emit("add",Math.random())
}
</script>
<style lang="scss" scoped>
</style>
vue3组合式API中的组件的生命周期函数(钩子函数)
生命周期函数主要用于在特定时间点执行特定的操作。 这就像一个人从出生到死亡会经历不同的阶段一样,Vue组件也会经历从创建到销毁的一系列过程,这些过程中的特定时间点就是生命周期函数的执行时机。
在uni-app中,分为Vue3的组件生命周期和uni-app自带的页面生命周期,一般来说,页面生命周期已经足够我们使用了。关于生命周期,可以看一下这篇文章: uniappVue3版本中组件生命周期和页面生命周期的详细介绍
onMoundted
在以下页面代码中,我们首先在scroll-view中创建了ref="scroll"引用,指向滚动视图组件的实例。通过这个引用,你可以在Vue组件的其他部分访问和操作这个滚动视图。在script中,是拿不到template中的引用的,因为此时DOM还属于虚拟DOM,使用onMounted让虚拟DOM变成真实DOM,就可以拿到了,如:
<template>
<view class="layout">
<scroll-view scroll-y="true" ref="scroll">
<view></view>
</scroll-view>
</view>
</template>
<script setup>
import {onMounted, ref} from "vue" ;
const scroll = ref(null) ;
onMounted(()=>{
console.log(scroll.value);
})
</script>
<style lang="scss" scoped>
</style>
onUnmounted
一般是在组件中完成监听,用于在组件卸载时执行一些清理操作,演示一下,先在life-demo组件中定义onUnmounted,代码如下:
<template>
<view>
子组件
</view>
</template>
<script setup>
import {onUnmounted} from "vue"
onUnmounted(()=>{
console.log("子组件被卸载了");
})
</script>
<style lang="scss">
</style>
然后在页面中,调用组件,并为其设置v-if,默认它是true的,设置定时2秒后,关闭组件,然后观察组件中是否能监听到,代码如下:
<template>
<view class="layout">
<life-demo v-if="show"></life-demo>
</view>
</template>
<script setup>
import {onMounted, ref} from "vue" ;
const scroll = ref("") ;
const show = ref(true);
setTimeout(()=>{
show.value = false
},2000)
</script>
<style lang="scss" scoped>
</style>
2秒钟后,组件关闭了,打印了返回值。
使用defineExpose暴露子组件的属性及方法
前面在组件中使用了emit向页面传值,还可以在页面直接从组件中取值,那就是使用defineExpose属性。在正常情况下,script中的常量只能用于本页面,要是在其他页面中使用,就要用到defineExpose属性,设置格式是”属性: 值“,接下来,演示一下如何使用,我们在组件中使用这个属性,代码如下:
<template>
<view class="out">
子组件
</view>
</template>
<script setup>
import {ref} from "vue"
const count = ref(100)
defineExpose({
count,
str:"Pin"
})
</script>
<style lang="scss" scoped>
</style>
然后在页面template区域里定义ref,通过onMounted函数,就可以拿到组件中的值了,页面代码如下:
<template>
<view class="layout">
<demo-child ref="child"> </demo-child>
</view>
</template>
<script setup>
import {onMounted, ref} from "vue"
const child = ref(null);
onMounted(()=>{
console.log(child.value)
})
</script>
<style lang="scss" scoped>
</style>
已取到值:
函数也可以从组件中取到,现在组件中定义函数updateCount,并将其暴露:
<template>
<view class="out">
子组件count的值:{{count}}
</view>
</template>
<script setup>
import {ref} from "vue"
const count = ref(100)
const updateCount = function(){
count.value++
}
defineExpose({
count,
str:"Pin",
updateCount
})
</script>
<style lang="scss" scoped>
</style>
在页面中,调用组件的函数:
<template>
<view class="layout">
<demo-child ref="child"> </demo-child>
<view>-----</view>
<button @click="update">点击修改count的值</button>
</view>
</template>
<script setup>
import {onMounted, ref} from "vue"
const child = ref(null);
const update = function(){
child.value.updateCount()
}
onMounted(()=>{
console.log(child.value)
})
</script>
<style lang="scss" scoped>
.out{
padding: 20px;
background: #eee;
}
</style>
成功调用,不仅取到了子组件的值,还对其进行了修改。
页面生命周期onLoad和onReady在vue3组合式API中的使用
onLoad
使用onLoad要从@dcloudio/uni-app中导入方法,格式如下:
import {onLoad} from "@dcloudio/uni-app"
在onLoad中有个回调函数,即跳转后可以接收到上个页面传来的参数,我们把刚刚的demo5页面拿过来,设置跳转页面并传参到新的页面demo6,代码如下:
<template>
<view class="layout">
<demo-child ref="child"> </demo-child>
<view>-----</view>
<button @click="update">点击修改count的值</button>
<navigator url="/pages/demo6/demo6?name=王五&age=20">跳转到demo6</navigator>
</view>
</template>
<script setup>
import {onMounted, ref} from "vue"
const child = ref(null);
const update = function(){
child.value.updateCount()
}
onMounted(()=>{
console.log(child.value)
})
</script>
<style lang="scss" scoped>
.out{
padding: 20px;
background: #eee;
}
</style>
进入到浏览器点击跳转,可以看到跳转后的链接里显示着参数:
那我们现在要接受这个参数,就要用到新页面demo6中的onLoad函数,然后把拿到的值赋给name,代码是这样的:
<template>
<view class="content">
<view>
姓名:{{name}}
</view>
</view>
</template>
<script setup>
import{ref} from "vue"
import{onLoad} from "@dcloudio/uni-app"
const name = ref("张三")
onLoad((e)=>{
name.value = e.name
})
</script>
<style scoped>
</style>
一般我们要接受参数的话,才会用到onLoad。
onReady
与组件onMounted类似,都是DOM体挂载完毕了,然后我们就可以操作template上的节点了。在demo6的template中写入scroll的ref值,通过onLoad是无法拿到的,用onReady就可以拿到,代码如下:
<template>
<view class="content">
<view>
姓名:{{name}}
<scroll-view scroll-y="true" ref="scroll">
<view></view>
</scroll-view>
</view>
</view>
</template>
<script setup>
import{ref} from "vue"
import{onLoad,onReady} from "@dcloudio/uni-app"
const name = ref("张三")
const scroll = ref(null)
onLoad((e)=>{
name.value = e.name
})
onReady(()=>{
console.log(scroll.value)
})
</script>
<style scoped>
</style>
但若是回调函数的话,只有用onLoad才可以,onReady是没有这个功能的。
onShow和onHide钩子的对比和执行顺序
onShow和onHide、onLoad有什么区别:onLoad是进入页面时就会触发,onHide是离开页面时触发,onShow是回到页面时触发,刚刚我们写了代码是从demo5跳转到demo6,现在我们写个代码让它能从demo6再跳回demo5,这下他俩之间就可以互跳了。现在,在demo6中定义一个计时器,然后根据onShow和onHide的属性,实现离开页面停止计时,回到页面开始计时的功能,这是demo6的代码:
<template>
<view class="content">
<view>
姓名:{{name}}
<scroll-view scroll-y="true" ref="scroll">
<view></view>
</scroll-view>
<navigator url="/pages/demo5/demo5">跳转到demo5</navigator>
</view>
<view>----</view>
<view>计数:{{count}}</view>
</view>
</template>
<script setup>
import{ref} from "vue"
import{onLoad,onReady,onShow, onHide} from "@dcloudio/uni-app"
const name = ref("张三")
const scroll = ref(null)
const count = ref(0)
let time = setInterval(()=>{
count.value++
},50)
onLoad(()=>{
console.log("onLoad函数")
})
onShow(()=>{
console.log("onShow函数")
})
onReady(()=>{
console.log("onReady函数")
})
onHide(()=>{
console.log("onHide函数")
})
</script>
<style scoped>
</style>
计时器现在是一直在计时的,即使在后台也是在计时:
现在,在onHide中设置 clearInterval,也就是离开页面时停止计时;然后在onShow设置开始计时,也就是回到页面开始计时,代码如下:
<template>
<view class="content">
<view>
姓名:{{name}}
<scroll-view scroll-y="true" ref="scroll">
<view></view>
</scroll-view>
<navigator url="/pages/demo5/demo5">跳转到demo5</navigator>
</view>
<view>----</view>
<view>计数:{{count}}</view>
</view>
</template>
<script setup>
import{ref} from "vue"
import{onLoad,onReady,onShow, onHide} from "@dcloudio/uni-app"
const name = ref("张三")
const scroll = ref(null)
const count = ref(0)
let time = setInterval(()=>{
count.value++
},50)
onLoad(()=>{
console.log("onLoad函数")
})
onShow(()=>{
console.log("onShow函数")
time = setInterval(()=>{
count.value++
},50)
})
onReady(()=>{
console.log("onReady函数")
})
onHide(()=>{
console.log("onHide函数")
clearInterval(time)
})
</script>
<style scoped>
</style>
功能成功实现,离开页面停止计时,回到页面又开始计时了:
onUnload页面卸载和onPageScroll监听页面滚动
onUnload
onUnload只有在打开新页面同时关闭所有页面时才会触发,所以在设置navigator时,我们要用reLaunch属性,同时,定义一个onUnload,代码如下:
<template>
<view class="content">
<view>
姓名:{{name}}
<scroll-view scroll-y="true" ref="scroll">
<view></view>
</scroll-view>
<navigator url="/pages/demo5/demo5">跳转到demo5</navigator>
</view>
<view>----</view>
<view>计数:{{count}}</view>
<view>----</view>
<navigator open-type = "reLaunch" url= "/pages/demo4/demo4">跳转到demo4</navigator>
</view>
</template>
<script setup>
import{ref} from "vue"
import{onLoad,onReady,onShow, onHide,onUnload} from "@dcloudio/uni-app"
const name = ref("张三")
const scroll = ref(null)
const count = ref(0)
let time = setInterval(()=>{
count.value++
},50)
onLoad(()=>{
console.log("onLoad函数")
})
onShow(()=>{
console.log("onShow函数")
time = setInterval(()=>{
count.value++
},50)
})
onReady(()=>{
console.log("onReady函数")
})
onHide(()=>{
console.log("onHide函数")
clearInterval(time)
})
onUnload(()=>{
console.log("页面被卸载")
})
</script>
<style scoped>
</style>
跳转到demo4时,onUnload被触发了,在实际应用中,onUnload一般会用于一些缓存的清除
onPageScroll
onPageScroll是用于监听滚动页面的,下面我们就利用onPageScroll来实现这个功能,让滚动条拖动到底部时显示箭头,向上滚动时箭头消失。先用v-for循环一组数,让页面产生滚动条。然后我们定义一个向上的箭头,并为其设置样式,后设置一个fixed变量定义为false,再给onPageScroll设置判断逻辑,让箭头在一定的条件下出现,代码如下:
<template>
<view class="content">
<view>
姓名:{{name}}
<scroll-view scroll-y="true" ref="scroll">
<view></view>
</scroll-view>
<navigator url="/pages/demo5/demo5">跳转到demo5</navigator>
</view>
<view>----</view>
<view>计数:{{count}}</view>
<view>----</view>
<navigator open-type = "reLaunch" url= "/pages/demo4/demo4">跳转到demo4</navigator>
<view v-for="item in 50">{{item}}</view>
<view class="fixed" v-if="fixed">⬆</view>
</view>
</template>
<script setup>
import{ref} from "vue"
import{onLoad,onReady,onShow, onHide,onUnload,onPageScroll} from "@dcloudio/uni-app"
const name = ref("张三")
const scroll = ref(null)
const count = ref(0)
const fixed = ref(false)
let time = setInterval(()=>{
count.value++
},50)
onLoad(()=>{
console.log("onLoad函数")
})
onShow(()=>{
console.log("onShow函数")
time = setInterval(()=>{
count.value++
},50)
})
onReady(()=>{
console.log("onReady函数")
})
onHide(()=>{
console.log("onHide函数")
clearInterval(time)
})
onUnload(()=>{
console.log("页面被卸载")
})
onPageScroll((e)=>{
fixed.value = e.scrollTop>200
// if(e.scrollTop>200){
// fixed.value = true
// }else{
// fixed.value = false
// }
})
</script>
<style scoped>
.fixed{
width: 100px;
height: 100px;
background: orange;
position: fixed;
right: 30px;
bottom: 30px;
}
</style>
成功实现: