uniapp学习day5

106 阅读5分钟

一.vue3语法

1.关于computed的小案例

使用demo7制作的案例,加上价格以及价格的统计:

image.png 首先把checkboxgroup加在外层 ,加上@change事件 ,checkbox中加上:value ,id值换成字符串

实现选择一个选项就输出对应的数组位置

<template>
	<view>
		<view class="out">
			<checkbox-group @change="itemChange">
				<view class="item" v-for="(item,index) in goods" :key="item.id">
					<checkbox :value="item.id"></checkbox>
					<text class="title">{{item.name}}</text>
					<text class="money">¥{{item.price}}</text>
					<text class="del" @click="remove(index)">删除</text>
				</view>
			</checkbox-group>
			
		</view>
	</view>
</template>

<script setup>
	import { ref } from 'vue';
	const goods=ref([
		{id:"1",name:"小米",price:3000},
		{id:"2",name:"华为",price:4000},
		{id:"3",name:"oppo",price:5000},
		{id:"4",name:"苹果",price:6000},
	])
	
	function remove(index){
		goods.value.splice(index,1)
	}
	function itemChange(e){
		console.log(e);
	}
</script>

<style lang="scss" scoped>
.out {
	margin:10px 0;
	.item {
		padding: 10px 0;
		.del {
			padding-left:30px;
			color: red;
		}
		.money {
			padding-left:30px;
			
		}
	}
}
</style>


image.png 增加计算选中总个数和总价格,这里对数组的操作要熟练,用到了forEach,includes,reduce

<template>
	<view>
		<view class="out">
			<checkbox-group @change="itemChange">
				<view class="item" v-for="(item,index) in goods" :key="item.id">
					<checkbox :value="item.id" :checked="item.checked"></checkbox>
					<text class="title">{{item.name}}</text>
					<text class="money">¥{{item.price}}</text>
					<text class="del" @click="remove(index)">删除</text>
				</view>
			</checkbox-group>
			
			<view class="card">选中{{selectNum.length}}个,总价{{selectPrice}}元</view>
		</view>
	</view>
</template>

<script setup>
	import { ref ,computed} from 'vue';
	const goods=ref([
		{id:"1",name:"小米",price:3000,checked:false},
		{id:"2",name:"华为",price:4000,checked:false},
		{id:"3",name:"oppo",price:5000,checked:false},
		{id:"4",name:"苹果",price:6000,checked:false},
	])
	const selectNum=ref([]);
	const selectPrice=computed(()=>{
		return goods.value.reduce((total,item)=>{
			if(item.checked){
				total+=item.price;
			}
			return total;
		},0)
	});
	
	function remove(index){
		goods.value.splice(index,1)
	}
	function itemChange(e){
		selectNum.value=e.detail.value;
		goods.value.forEach(item=>{
			item.checked=selectNum.value.includes(item.id)
		})
	}
</script>

<style lang="scss" scoped>
.out {
	margin:10px 0;
	.item {
		padding: 10px 0;
		.del {
			padding-left:30px;
			color: red;
		}
		.money {
			padding-left:30px;
			
		}
	}
}
</style>


实现效果:

image.png

2. watch

默认是浅层监听:

<template>
	<view>
		<input type="text" v-model="person" />
	</view>
	{{person}}
</template>

<script setup>
	import {ref,watch} from 'vue';
	const person=ref("");
	watch(person,(newValue)=>{
		console.log(newValue);
		
	})
</script>

<style lang="scss">

</style>

换成对象的数据:watch里面就不能只写一个person了,要换成person.value.name,用箭头函数

<template>
	<view>
		<input type="text" v-model="person.name" />
	</view>
	{{person}}
</template>

<script setup>
	import {ref,watch} from 'vue';
	const person=ref({
		name:"张三",
		age:20
	});
	watch(()=>person.value.name,(newValue)=>{
		console.log(newValue);
		
	})
</script>

<style lang="scss">

</style>

使用deeptrue:

<template>
	<view>
		<input type="text" v-model="person.name" />
	</view>
	{{person}}
</template>

<script setup>
	import {ref,watch} from 'vue';
	const person=ref({
		name:"张三",
		age:20
	});
	// watch(()=>person.value.name,(newValue)=>{
	// 	console.log(newValue);
		
	// })
	watch(person,(newValue)=>{
		console.log(newValue);
	},{deep:true})
</script>

<style lang="scss">

</style>

实现监听:(不加的话是没有显示的,也就是监听不到)

image.png

3.组件创建

image.png uniapp和vue官方的组件创立是有区别的

引入组件只需要在建component文件夹,然后在下面建组件就可以用了

<template>
	<view class="content">
		<component1 v-for="item in 3"></component1>
	</view>
</template>

<script>
	
</script>

<style>
	
</style>

<template>
	<view>
		张三
	</view>
</template>

<script>
	export default {
		name:"component1",
		data() {
			return {
				
			};
		}
	}
</script>

<style>

</style>

实现效果:

image.png

4.组件中使用props进行数据传递

具体看vue官网的cn.vuejs.org/guide/compo…

基本使用(字符串的传递):

主页:(图片路径选用../相对路径)

<template>
	<view class="content">
		<UserInfo username="xiaoming" pic="../../static/pic1.png"></UserInfo>
		<UserInfo></UserInfo>
		<UserInfo :username="name"></UserInfo>
	</view>
</template>

<script setup>
	import {ref} from "vue";
	 const  name=ref("xiaolan");
</script>

<style lang="scss">
	
</style>

组件:

<template>
	<view class="userinfo">
		<image :src="pic" class="pic"></image>
		<view class="username">{{username}}</view>
	</view>
</template>

<script setup>
	
	const props=defineProps({
		username:{
			type:String,
			default:"匿名"
		},
		pic:{
			type:String,
			default:"../../static/logo.png"
		}
	});
</script>

<style lang="scss" scoped>
.userinfo {
	width: 100%;
	height: 200px;
	background-color: #ccc;
	display: flex;
	flex-direction: column;
	align-items: center;
	justify-content: center;
	image {
		width: 100px;
		height: 100px;
	}
}
</style>

props中换成传入的是数组对象的写法:

组件:

<template>
	<view class="userinfo">
		<image :src="obj.pic" class="pic"></image>
		<view class="username">{{obj.name}}</view>
		
	</view>
</template>

<script setup>
	
	defineProps({
		obj:{
			type:Object,
			default(){
				return {name:"匿名",pic:"../../static/logo.png"}
			}
		}
	});
</script>

<style lang="scss" scoped>
.userinfo {
	width: 100%;
	height: 200px;
	background-color: #ccc;
	display: flex;
	flex-direction: column;
	align-items: center;
	justify-content: center;
	image {
		width: 100px;
		height: 100px;
	}
}
</style>

主页:

<template>
	<view class="content">
		
		
		<UserInfo v-for="(item,index) in userinfo" :obj="item"></UserInfo>
	</view>
</template>

<script setup>
	import {ref} from "vue";
	 const  userinfo=ref([
		 {
		 		 name:"张三",
		 		 pic:"../../static/pic1.png"
		 },
		 {
			 name:"李四",
			 pic:"../../static/pic2.png"
		 },
		 {
		 			 name:"王五",
		 			 pic:"../../static/pic3.png"
		 }
	 ])
</script>

<style lang="scss">
	
</style>

实现效果:

image.png

5.Slots

为没有定义组件的地方预留一个大小不限的空间

layout组件:

<template>
	<view class="layout">
		<view class="header">头部</view>
		<view class="main">
			<slot></slot>
		</view>
		<view class="footer">底部</view>
	</view>
</template>

<script setup>
	
</script>

<style lang="scss">
.layout {
	.header {
		height:100px;
		background: #cfcfcf;
	}
	.main {
		min-height: 200px;
	}
	.footer {
		height: 120px;
		background-color: orange;
	}
}
</style>

demo1:不同的地方就在页面中写上

<template>
	<view>
		<lyt-layout>
			<view class="row" v-for="item in 20">每一行{{item}}</view>
		</lyt-layout>
	</view>
</template>

<script setup>
	
</script>

<style lang="scss">

</style>

demo2:

<template>
	<view>
		<lyt-layout></lyt-layout>
	</view>
</template>

<script setup>
	
</script>

<style lang="scss">

</style>

两个页面区别:

image.png

image.png

具名插槽: 如果除了中心有插槽,头部或者底部也有,就要使用具名插槽了,否则插入的东西位置就会乱。

使用方法:(用template)

<template>
	<view>
		<lyt-layout>
			<template v-slot:header>demo2的头部</template>
			<template #main>demo2的中心</template>
		</lyt-layout>
	</view>
</template>

<script setup>
	
</script>

<style lang="scss">
.box1 {
	width:50px;
	height: 50px;
	background-color: pink;
}
.box2 {
	width:50px;
	height: 50px;
	background-color: skyblue;
}
</style>

6.组件中的emit的声明和触发

前面学的是props是父组件给子组件传, 现在学习怎么让子组件给父组件传

场景:比如说在搜索栏是一个子组件,我们输入内容的时候相当于子组件的数据变化了,那么就需要让搜索页面(父组件)做出相应的变化。

子组件: @click="$emit('add',123)"

<template>
	<view>
		子组件
		<button @click="$emit('add',123)">按钮</button>
	</view>
</template>

<script setup>
	
</script>

<style lang="scss" scoped>

</style>

父页面:@add="onAdd"

<template>
	<view>
		<component-child @add="onAdd"></component-child>
		<view>{{num}}</view>
	</view>
</template>

<script setup>
	import {ref} from "vue";
	const num=ref("");
	const onAdd=function(e){
		console.log(e);
		num.value=e;
	}
</script> 

<style lang="scss">

</style>

点击前:

image.png

点击后:(收到子组件传来的数据了)

image.png 实现随机色:(子组件传来的是随机数)

<template>
	<view>
		<component-child @add="onAdd"></component-child>
		<view class="box" :style="{background:color}">{{num}}</view>
	</view>
</template>

<script setup>
	import {ref} from "vue";
	const num=ref("");
	const color=ref("")
	const onAdd=function(e){
		console.log(e);
		num.value=e;
		color.value='#'+String(e).substring(3,6);
	}
</script> 

<style lang="scss">
.box {
	width: 100px;
	height: 100px;
	background-color: pink;
}
</style>

一般子组件是在script的代码块中传的: const emit=defineEmits(["add"])

和使用defineProps是类似的

<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>

input的emit:实现在input框输入数据的同时,把数据传给父组件

<template>
	<view>
		子组件
		<button @click="onClick">按钮</button>
		<input @input="onInput"/>
	</view>
</template>

<script setup>
	const emit=defineEmits(["add","change"])
	function onClick(){
		emit("add",Math.random());
	}
	function onInput(e){
		// console.log(e.detail.value);
		emit("change",e.detail.value)
	}
</script>

<style lang="scss" scoped>
input {
	border: 1px solid #fcfcfc;
	height: 40px;
}
</style>
<template>
	<view>
		<component-child @add="onAdd" @change="onChange"></component-child>
		<view class="box" :style="{background:color,fontSize:size+'px'}">{{num}}</view>
	</view>
</template>

<script setup>
	import {ref} from "vue";
	const num=ref("");
	const color=ref("");
	const size=ref(10);
	const onAdd=function(e){
		console.log(e);
		num.value=e;
		color.value='#'+String(e).substring(3,6);
	}
	const onChange=function(e){
		console.log(e);
		size.value=e;
	}
</script> 

<style lang="scss">
.box {
	width: 100px;
	height: 100px;
	background-color: pink;
}
</style>

image.png

7.组件生命周期

image.png

image.png

8.使用defineExpose暴露子组件的属性和方法

暴露属性:

子组件:用defineExpose

<template>
	<view class="out">
		子组件
	</view>
</template>

<script setup>
	import {ref} from "vue";
	const count=ref(100);
	defineExpose({
		count,
		str:"lyt"
	})
</script>

<style lang="scss" scoped>

</style>

父页面:必须要使用在onMounted中写,因为这个时候组件才挂载完成

<template>
	<view>
		<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>

</style>

image.png 暴露方法:这个时候组件已经渲染完毕,可以直接调用它暴露出来的函数,所以不用在onMounted中写了

<template>
	<view class="out">
		子组件{{count}}
	</view>
</template>

<script setup>
	import {ref} from "vue";
	const count=ref(100);
	const updateCount=function (){
		count.value++;
	}
	defineExpose({
		count,
		str:"lyt",
		updateCount
	})
</script>

<style lang="scss" scoped>

</style>
<template>
	<view>
		<demo-child ref="child" ></demo-child>
		<button @click="update">点击修改子元素的值</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>

</style>

image.png image.png

9.页面生命周期

onLoad: 可以用来接收上一个页面的参数:

demo5中跳转的时候传递参数:

<template>
	<view>
		<demo-child ref="child" ></demo-child>
		<button @click="update">点击修改子元素的值</button>
		<navigator url="/pages/demo6/demo6?name=王五&age=20"><button>跳转到demo6</button></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>

</style>

demo6:

<template>
	<view>
		姓名:{{name}}
		年龄:{{age}}
	</view>
</template>

<script setup>
	import {onLoad} from "@dcloudio/uni-app";
	import {ref} from "vue";
	const name=ref("张三");
	const age=ref(10)
	onLoad((e)=>{
		name.value="李四"
		console.log("onLoad函数");
		console.log(e);
		name.value=e.name;
		age.value=e.age;
	})
</script>

<style lang="scss" scoped>
	

</style>

显示结果:

image.png