一、组件封装通讯
1. 父传子(直接传,父组件引入并引用子组件,子组件通过props接收)
父组件.vue文件
xml
<template>
<view class="g-container">
<!-- 标签 小程序 -->
<input type="text" placeholder="请输入内容" v-model="inputValue"><button @click="onConfirm">确定</button>
</view>
<view class="g-list">
<view v-for="item in list" :key="item.id">
<GoodsItem :goods="item"></GoodsItem>
</view>
</view>
</template>
<script setup>
import {ref} from 'vue'
import GoodsItem from '../../components/GoodsItem.vue'
const list = ref([
{id:1001,name:'javascript高级编程',price:88.89,num:1},
{id:1002,name:'vue高级编程',price:188.89,num:1},
])
const inputValue = ref('')
//确定
const onConfirm = ()=>{
const good = {
id:Math.floor(Math.random()*(9999-1000)+1000),
name:inputValue.value,
price:Math.floor(Math.random()*(999-100)+100),
num:Math.floor(Math.random()*10)
}
list.value.push(good)
inputValue.value = '' // 清除数据
}
</script>
<style scoped lang="scss">
.g-container{
margin: 5px;
display: flex;
input{
border: 1px solid gray;
height: 40px;
flex: 1;
}
button{
width: 100px;
}
}
</style>
根目录下创建一个components文件夹,文件夹中创建.vue文件,用于封装组件
子组件.vue文件
xml
<template>
<view class="g-container">
<image src="../static/logo.png" ></image>
<view class="g-right">
<text>ID: {{goods.id}}</text>
<view class="">名称: {{goods.name}}</view>
<view class="">价格: {{goods.price}}</view>
<view class="">数量: {{goods.num}}</view>
</view>
</view>
</template>
<script setup>
const props = defineProps(['goods'])
// const props = defineProps({
// goods: Object,
// })
</script>
<style scoped lang="scss">
.g-container{
display: flex;
image{
width: 80px;
height: 80px;
}
.g-right{
flex: 1;
}
}
</style>
2.子传父(父组件定义一个自定义事件,子组件接收并触发这个自定义事件,将参数传给父组件)
父组件.vue文件
xml
<template>
<view class="g-container">
<!-- 标签 小程序 -->
<input type="text" placeholder="请输入内容" v-model="inputValue"><button @click="onConfirm">确定</button>
</view>
<view class="g-list">
<view v-for="item in list" :key="item.id">
<GoodsItem :goods="item" @binddelete="onDelete()"></GoodsItem>
</view>
</view>
</template>
<script setup>
import {ref} from 'vue'
import GoodsItem from '../../components/GoodsItem.vue'
const list = ref([
{id:1001,name:'javascript高级编程',price:88.89,num:1},
{id:1002,name:'vue高级编程',price:188.89,num:1},
])
const inputValue = ref('')
// 确定
const onConfirm = ()=>{
const good = {
id:Math.floor(Math.random()*(9999-1000)+1000),
name:inputValue.value,
price:Math.floor(Math.random()*(999-100)+100),
num:Math.floor(Math.random()*10)
}
list.value.push(good)
inputValue.value = '' // 清除数据
}
// 删除
const onDelete = (id)=>{
const index = list.value.findIndex(item=>item.id===id)
list.value.splice(index,1) //
}
</script>
<style scoped lang="scss">
.g-container{
margin: 5px;
display: flex;
input{
border: 1px solid gray;
height: 40px;
flex: 1;
}
button{
width: 100px;
}
}
</style>
子组件.vue文件
xml
<template>
<view class="g-container">
<image src="../static/logo.png" ></image>
<view class="g-right">
<text>ID: {{goods.id}}</text>
<view class="">名称: {{goods.name}}</view>
<view class="">价格: {{goods.price}}</view>
<view class="">数量: {{goods.num}}</view>
<button type="default" size="mini" @click="onDelete">删除</button>
</view>
</view>
</template>
<script setup>
const props = defineProps(['goods'])
// const props = defineProps({
// goods: Object,
// })
const emits = defineEmits(['binddelete'])
// 删除
const onDelete = ()=>{
emits('binddelete', props.goods.id)
}
</script>
<style scoped lang="scss">
.g-container{
display: flex;
image{
width: 80px;
height: 80px;
}
.g-right{
flex: 1;
}
}
</style>
二、计算属性与侦听器
1.计算属性computed
应用场景:
实现权限菜单
-
登录成功, 调用权限菜单接口获取菜单列表,保存到store
-
跳转到主界面,显示菜单列表
问题: 菜单列表获取是异步操作,如果没有保存store成功, 直接跳转到主界面,菜单不显示
解决办法: 1. store封装promise, 成功保存store返回后跳转
- 计算属性动态计算store菜单列表
下面是一个计算总价的小demo
父组件.vue文件
xml
<template>
<view class="g-container">
<!-- 标签 小程序 -->
<input type="text" placeholder="请输入内容" v-model="inputValue"><button @click="onConfirm">确定</button>
</view>
<view class="g-list">
<view v-for="item in list" :key="item.id">
<GoodsItem :goods="item" @binddelete="onDelete()"></GoodsItem>
</view>
</view>
<view class="g-totalprice">总价:{{totalPrice}}</view>
</template>
<script setup>
import {ref,computed} from 'vue'
import GoodsItem from '../../components/GoodsItem.vue'
const list = ref([
{id:1001,name:'javascript高级编程',price:88.89,num:1},
{id:1002,name:'vue高级编程',price:188.89,num:1},
])
const inputValue = ref('')
// 计算总价,累加
const totalPrice = computed(()=>{
const total = list.value.reduce((prvious,current)=> prvious + current.price*current.num ,0)
return total.toFixed(2)
})
// 确定
const onConfirm = ()=>{
const good = {
id:Math.floor(Math.random()*(9999-1000)+1000),
name:inputValue.value,
price:Math.floor(Math.random()*(999-100)+100),
num:Math.floor(Math.random()*10)
}
list.value.push(good)
inputValue.value = '' // 清除数据
}
// 删除
const onDelete = (id)=>{
const index = list.value.findIndex(item=>item.id===id)
list.value.splice(index,1) //
}
</script>
<style scoped lang="scss">
.g-container{
margin: 5px;
display: flex;
input{
border: 1px solid gray;
height: 40px;
flex: 1;
}
button{
width: 100px;
}
}
</style>
子组件.vue文件
xml
<template>
<view class="g-container">
<image src="../static/logo.png" ></image>
<view class="g-right">
<text>ID: {{goods.id}}</text>
<view class="">名称: {{goods.name}}</view>
<view class="">价格: {{goods.price}}</view>
<view class="">数量: {{goods.num}}</view>
<button type="default" size="mini" @click="onDelete">删除</button>
</view>
</view>
</template>
<script setup>
const props = defineProps(['goods'])
// const props = defineProps({
// goods: Object,
// })
const emits = defineEmits(['binddelete'])
// 删除
const onDelete = ()=>{
emits('binddelete', props.goods.id)
}
</script>
<style scoped lang="scss">
.g-container{
display: flex;
image{
width: 80px;
height: 80px;
}
.g-right{
flex: 1;
}
}
</style>
2.侦听器watch
应用场景:
搜索框(防抖与节流)
防抖: 触发高频事件时,n秒内再次触发,重新计算时间(只执行最后一次)
节流:触发高频事件时, n秒内再次触发,无效
根目录新建utils文件夹,utils文件夹中新建util.js文件,用于封装工具
kotlin
/**
* 手写防抖
* 高频事件函数fun
* 时间 n秒
*
* 返回防抖函数
* 改变this指向
call apply bind
*/
export const debounce = ( fun,n)=>{
let timer = null
return function(){
clearTimeout(timer)
timer = setTimeout(()=>{
fun.apply(this)
},n)
}
}
父组件.vue文件
xml
<template>
<view class="g-container">
<!-- 标签 小程序 -->
<input type="text" placeholder="请输入内容" v-model="inputValue"><button @click="onConfirm">确定</button>
</view>
<view class="g-list">
<view v-for="item in list" :key="item.id">
<GoodsItem :goods="item" @binddelete="onDelete()"></GoodsItem>
</view>
</view>
<view class="g-totalprice">总价:{{totalPrice}}</view>
</template>
<script setup>
import {ref,computed,watch} from 'vue'
import GoodsItem from '../../components/GoodsItem.vue'
import { debounce } from '../../utils/util.js'
const list = ref([
{id:1001,name:'javascript高级编程',price:88.89,num:1},
{id:1002,name:'vue高级编程',price:188.89,num:1},
])
const inputValue = ref('')
// const bindInput = debounce(()=>{
// console.log('bindInput >>. ',inputValue.value);
// },500)
/**
* 侦听器
* 1. 响应式数据
* 2. route 面包屑
* 3. 立即侦听
* 4. 深度侦听
*/
// 获取输入框内容,调用搜索接口获取列表数据,显示
// 问题: 每输入一个内容,都会触发侦听,调用接用,请优化一下
// 防抖: 触发高频事件时, n秒内再次触发,重新计算时间(只执行最后一次)
// 节流: 触发高频事件时, n秒内再次触发,无效
// 实现防抖
// watch(inputValue,(newValue)=>{
// console.log('newValue >>>',newValue);
// })
watch(inputValue,debounce(()=>{
console.log('bindInput >>. ',inputValue.value);
},500))
// 计算总价,累加
const totalPrice = computed(()=>{
const total = list.value.reduce((prvious,current)=> prvious + current.price*current.num ,0)
return total.toFixed(2)
})
// 确定
const onConfirm = ()=>{
const good = {
id:Math.floor(Math.random()*(9999-1000)+1000),
name:inputValue.value,
price:Math.floor(Math.random()*(999-100)+100),
num:Math.floor(Math.random()*10)
}
list.value.push(good)
inputValue.value = '' // 清除数据
}
// 删除
const onDelete = (id)=>{
const index = list.value.findIndex(item=>item.id===id)
list.value.splice(index,1) //
}
</script>
<style scoped lang="scss">
.g-container{
margin: 5px;
display: flex;
input{
border: 1px solid gray;
height: 40px;
flex: 1;
}
button{
width: 100px;
}
}
</style>
子组件.vue文件
xml
<template>
<view class="g-container">
<image src="../static/logo.png" ></image>
<view class="g-right">
<text>ID: {{goods.id}}</text>
<view class="">名称: {{goods.name}}</view>
<view class="">价格: {{goods.price}}</view>
<view class="">数量: {{goods.num}}</view>
<button type="default" size="mini" @click="onDelete">删除</button>
</view>
</view>
</template>
<script setup>
const props = defineProps(['goods'])
// const props = defineProps({
// goods: Object,
// })
const emits = defineEmits(['binddelete'])
// 删除
const onDelete = ()=>{
emits('binddelete', props.goods.id)
}
</script>
<style scoped lang="scss">
.g-container{
display: flex;
image{
width: 80px;
height: 80px;
}
.g-right{
flex: 1;
}
}
</style>