父传子

@defineProps()
通过编译宏 defineProps() 的形式实现通信|参数传递|数据传递
遵循 单向数据流 - One-Way Data Flow 的原则
更多信息,请访问 Props
Procedure
子组件在 <script setup> 顶级作用域下,使用宏 defineProps() 定义/声明属性来接收传递的参数并使用
父组件以属性的方式向子组件传递参数:可以是静态数据 Static ,也可以是响应式数据 Dynamic ;可以是简单数据,也可以是复合数据
props 应该是只读的,不推荐子组件直接修改 props:当父组件更新 props 时,子组件不应该反过来改变这个值
建议使用小驼峰命名方式 - camelCase
为了和普通变量区分,建议:传递的参数以 prop 开始,如 propName
只有数据的拥有者才可以修改数据:子组件不能直接修改父组件传递的属性参数;如果父组件把这个属性参数还分配了其它子组件。。。
子组件
1. 声明属性参数
可以是数组或者对象
建议使用一个变量接收,便于在脚本中进一步处理
可以使用解构,参考文末的总结和练习
方案1:使用 数组 声明 - 仅指定属性名
const props = defineProps(['propMsg', 'propId'])
方案2:使用 对象 声明 - 指定属性名、属性类型 type、默认值 default 和是否必须 required;同时存在时,required 无效
const props = defineProps({
    propMsg: String,
    propId: {
      type: Number,
      required: false,
      default: 18
    }
})
2. 使用传递来的参数|数据
结构中,可以直接使用;也可以使用属性变量获取
<div>{{ propMsg }} - {{ props.propId }}</div>
<div>computed {{ comp }}</div>
逻辑中,如果要处理接收的参数,必须使用定义的变量 props 获取,否则提示数据未定义(ReferenceError: msg is not defined)
可以根据需要对数据2次处理,如利用计算属性将接收的参数转换为大写
思考:为什么不可以直接修改父组件传递的属性参数?
import { computed } from 'vue';
const props = defineProps({
  msg: String
})
const comp = computed(() => {
    //return msg.toUpperCase()
    return props.msg.toUpperCase()
})
父组件
1. 引入子组件,准备数据
import { ref } from 'vue';
import Test from './components/Test.vue';
let msg = ref('hi,there')
2. 使用子组件;以数据绑定的形式传递参数:1个静态数据 propId、1个响应式数据 propMsg
<Test :propMsg="msg" propId="18"></Test>
特别的,如果传递的数据和属性参数同名,可以简写
<Test :msg="msg"></Test>
<Test :msg></Test>
值的类型
1. 数值 Number
<Test :propId="18" />
<Test :propId="item.id" />
2. 布尔 Boolean
<Test isDone />
<Test :isDone="false" />
<Test :isDone="item.flag" />
3. 数组 Array
<Test :arr="[1,2,3,4,5]" />
<Test :arr="item.list" />
4. 对象 Object
<Test :obj="{id:1001, name:'glpla'}" />
<Test :obj="item.author" />
5. 函数 Function
除了传递普通数据外,还可以传递方法 - 子组件执行父组件的方法,如:让父组件修改属性参数。。。
还可以使用 自定义事件 - emits 实现
子组件:定义/声明并使用方法;类型为函数;注意是 Function,不是 function
const props = defineProps({
  closeFn: Function,
  confirmFn: Function
})
<button @click="closeFn">×</button>
<button @click="confirmFn">confirm</button>
父组件:定义事件处理函数并传递
const closeFn = () => { }
const confirmFn = () => { }
<Msg :closeFn="closeFn" :confirmFn="confirmFn"></Msg>
Summary & Homework
Summary
defineProps() - 数组或对象
子组件声明并使用
父组件传递

. 普通数据

. 函数/方法

Homework
[] 比较并分析下面两种声明属性方法的特点
使用解构
const { msg } = defineProps({
  msg: { type: String, default: 'Vue.js' }
})
不使用解构
const props = defineProps({
  msg: { type: String, default: 'Vue.js' }
})
[] 轮播组件 Swiper.vue
阶段1:在原轮播组件中,封装指示器组件 Indictator.vue
const props = defineProps({
  items: {
    type: Array,
    default: () => []
  },
  currentInd: {
    type: Number,
    default: 0
  }
})
<div class="indictator">
  <span class="dot" :class="{ 'active': currentInd === ind }" 
  v-for="(item, ind) in items" :key="ind">{{ ind + 1
  }}</span>
</div>
轮播组件使用
<Indicators :items="imgsUrl" :currentInd="currentInd"/>
阶段2:为不同应用场景的组件提供不同的图片列表数据
const props = defineProps(['imgsUrl'])
父组件使用
<Swiper :imgsUrl="imgsUrl"/>
思考:为指示器增加更多属性控制,如位置 pos - 底部 bottom、右侧 right、顶部 top、类型 type - 数字 number、分数 fraction
思考:如何封装第三方库
[] 商品列表页 Goods.vue
封装商品项组件 GoodsItem.vue 并遍历
<template v-if="goods.length">
  <GoodsItem :product="item" v-for="(item, ind) in goods" :key="item.id" />
  <footer class="f-s-s">我是有底线的~</footer>
</template>
<div v-else>商品获取失败,请刷新页面</div>
[] 版权组件
根据作者头像或者学号等信息展示相应的版权信息
如果不指定头像,则使用默认头像
如果不指定学号,则使用大树小站的域名 glpla.github.io
<div class="copyright">
  <img class="img" :src='src' alt="">
  <div class="desc">2024 - 2026 © Copyright, powered by {{ id }}</div>
</div>
const props = defineProps({
  src: {
    type: String,
    default: 'https://glpla.github.io/imgs/avatar.jpg'
  },
  id: {
    type: String,
    default: 'glpla.github.io'
  }
})
[] 标题组件 Title.vue
1. 组件定义
<div class="title">
  <h3>{{ title }}</h3>
  <button @click="handle">
    <span>查看全部</span>
    <span class="iconfont icon-jiantou_liebiaoxiangyou_o"></span>
  </button>
</div>
const props = defineProps({
  title: {
    type: String,
    default: ''
  },
  handle: {
    type: Function,
    default: () => { }
  }
})
2. 组件使用:首页视图 HomeView.vue 使用了两次标题组件,每个组件分配不同的标题内容和事件
2.1 我的优惠专区
<Title title="我的优惠专区" :handle="toCoupon"></Title>
const toCoupon = () => {
  alert('to coupon')
}
2.2 福利中心
<Title title="福利中心" :handle="toWelfare"></Title>
const toWelfare = () => {
  alert('to welfare')
}
[] 菜单视图 MenuView.vue
1. 配送方式切换组件 Switch.vue
<Switch :switchs="switchs"></Switch>
2. 地图组件 Map.vue
使用第三方库 腾讯位置服务
3. 嵌套路由组件 Tabbar.vue
<Tabbar :tabs="tabs"></Tabbar>
4. 购物车组件 Cart.vue
使用 状态管理
[] 爆品组件 Recommend.vue
每个产品推荐一定数量的相应的其它产品
<Recommend :reco="goods.recommend" />
[] 地图组件 Map.vue 接收 配送组件 LocationView.vue 传递的 中心点 center 和点标记 geometries
<Map class="map" :center="center" :geometries="geometries"></Map>
[] 子组件如何修改/处理父组件传递的参数?