【BlogAdmin升级3】组件通讯与引用

2024-02-22 15:40:36 浏览数 (2)

父传子

1.父组件中给子组件绑定属性

2.子组件内部通过props选项接收

子传父

1.父组件中给子组件标签通过@绑定事件

2.子组件内部通过emit方法触发事件

子组件

代码语言:javascript复制
<script setup>
// 由于写setup,所以无法直接配置props选项
// 所以需要借助"编译器宏"函数接收父组件传递的数据
const props = defineProps({
    car: String,
    money: Number,
    addMoney: Function
})
console.log(props.car)




//子传父
const emit = defineEmits(['change-Money'])
const buy = () => {
    emit('change-Money', 5)
}
</script>
<template>
    <div class="son">
        <!-- 子组件可以直接使用传递过来的数据 -->
        我是子组件 - {{ car }} - {{ money }} - <button @click="addMoney"> 1</button>
        <div>  <button @click="buy">-Money</button></div>
    </div>
  
</template>
<style>
.son {
    border: 1px solid #000;
    padding: 30px;
}
</style>

父组件

代码语言:javascript复制
<script setup>
//父传子
// 1.给子组件,添加属性方式传值
// 2.子子组件,通过props接收
import mychild from '@/components/mychild.vue'


// 传递动态值
import { ref } from 'vue';
const money = ref(100)


const addMoney = () => {
  money.value  
}




//子传父
// 1.在子组件内部,emit触发事件
// 2.在父组件通过@监听
const changeFn = (m) => {
  money.value -= m
}
</script>


<template>
  <div>
    父组件 - {{ money }} - <button @click="addMoney"> 1</button>
    <mychild car="我是父组件传过来的参数" :money="money" :addMoney="addMoney" @change-Money="changeFn"></mychild>
  </div>
</template>

defineProps原理: 就是编译阶段的一个标识,实际编译器解析时,遇到后就会进行编译转换

模板引用

通过ref标识获取真实的dom对象或者组件实例对象

1.调用ref函数生成一个ref对象

2.通过ref表示绑定ref对象到标签

defineExpose()

默认情况下<script setup>语法糖下组件的属性和方法是不开放给父组件的

可以用过defineExpose编译宏指定哪些属性和方法允许访问

子组件

代码语言:javascript复制
<script setup>
const count = 999
const sayHi = () => {
    console.log("你好")
}
defineExpose({
    count,
    sayHi
})
</script>
<template>
    <div>
        我是子组件 - {{ 999 }}
    </div>
</template>

父组件

代码语言:javascript复制
<script setup>
import myRefChild from '@/components/my-ref-child.vue'
import { ref, onMounted } from 'vue';
// 模板引用,可以获取dom,也可以获取组件
// 1.调用ref函数,生成一个ref对象
// 2.通过ref标识进行标定
// 3.通过ref对象.value即可访问到绑定的元素(必须渲染后才能拿到)
const myInput = ref(null)


onMounted(() => {
  console.log("myInput")
  // myInput.value.focus()
})


const clickFn = () => {
  myInput.value.focus()
}




const myChild = ref(null)


const getMyDom = () => {
  console.log(myChild.value)
  myChild.value.sayHi()
}
onMounted(() => {
  console.log(myChild.value)


})
</script>


<template>
  <div>
    <input ref="myInput" type="text" />
    <button @click="clickFn">点击输入聚焦</button>


    <div>
      <myRefChild ref="myChild"></myRefChild>
      <button @click="getMyDom">获取组件信息</button>
    </div>
  </div>
</template>

provide和inject

顶层组件向任意的底层组件传递数据和方法,实现跨层组件通信

底层

代码语言:javascript复制
<script setup>


import { inject } from 'vue';


const themeColor = inject('theme-color')


const count = inject('count')
const changeCount = inject('changeCount')
const changeFn = () => {
    changeCount(1000)
}
</script>
<template>
    <h3>我是底层组件 - {{ themeColor }} - {{ count }}</h3>
    <button @click="changeFn">更新count</button>
</template>

中间

代码语言:javascript复制
<script setup>
import childBottom from '@/components/child-bottom.vue'
</script>
<template>
    <h2>我是中间组件</h2>
    <childBottom></childBottom>
</template>

顶层

代码语言:javascript复制
<script setup>
import childCenter from '@/components/child-center.vue'


import { provide, ref } from 'vue';


// 跨层传递普通数据
provide('theme-color', 'pink')


// 跨层传递响应式数据
const count = ref(100)
provide('count', count)


setTimeout(() => {
  count.value = 200
}, 2000);


//跨层级传递函数,方便子组件修改父组件的数据,提供方法进行修改
provide('changeCount', (newCount) => {
  count.value = newCount
})
</script>
<template>
  <h1>我是顶层组件 - {{ count }}</h1>
  <childCenter></childCenter>
</template>

defineOptions(vue 3.3)

有<script setup>之前,如果要定义 props,emits 可以轻而易举地添加一个与 setup 平级的属性

但是用了<script setup> 后,就没法这么干了 setup 属性已经没有了,自然无法添加与其平级的属性

为了解决这一问题,引入了 defineProps 与 defineEmits 这两个宏。

但这只解决了 props 与emits 这两个属性如果我们要定义组件的 name 或其他自定义的属性,

还是得回到最原始的用法一再添加一个普通的<script>标签这样就会存在两个<script> 标签。

让人无法接受

所以在 Vue 3.3 中新引入了 defineOptions 宏。

顾名思义,主要是用来定义 Options API 的选项。可以用define0ptions定义任意的选项,props,emits,expose, slots 除外(因为这些可以使用 defineXXX来做到)

代码语言:javascript复制


// <script>
// export default {
//   name: "test"
// }
// </script>


<script setup>
defineOptions({
  name: 'test'
})
</script>
<template>
  <div>Hello</div>
</template>

defineModel

在Vue3中,自定义组件上使用v-model,相当于传递一个modelValue属性,同时触发 update:modelvalue 事件<Child v-model="isVisible">

相当于

代码语言:javascript复制
<Child :modelValue="isVisible" @update:modelValue="isVisible=$event">

我们需要先定义props,

再定义emits。其中有许多重复的代码。

如果需要修改此值,还需要手动调用 emit 函数

子组件

代码语言:javascript复制
<script setup>
import { defineModel } from 'vue';
const modelValue = defineModel()
const changeFn = () => {
    modelValue.value  = 1
}
</script>
<template>
    <div>
        <input type="text" :value="modelValue" @input="e => modelValue = e.target.value" />
        {{ modelValue }}
        <button @click="changeFn">子组件更新</button>
    </div>
</template>

父组件

代码语言:javascript复制
<script setup>
import { ref } from 'vue';
import myInput from '@/components/my-input.vue'


const count = ref(100)


const changeFn = () => {
  count.value  = 1
}
</script>
<template>
  <div>
    <myInput v-model="count"></myInput>
    <div>{{ count }} - <button @click="changeFn">父组件更新</button></div>
  </div>
</template>

Pinia

Pinia是Vue 的最新状态管理工具,是Vuex的替代品

特点

1.提供更加简单的API (去掉了 mutation )

2.提供符合,组合式风格的API (和Vue3 新语法统一)

3.去掉了 modules 的概念,每一个 store 都是一个独立的模块

4.配合TypeScript 更加友好,提供可靠的类型推断

配置

手动添加Pinia到Vue项目

在实际开发项目的时候,关于pinia的配置,可以在项目创建时自动添加现在我们初次学习,从零开始:

1使用 Vite 创建一个空的 Vue3 项目

npm create vue@latest

2按照官方文档安装 pinia 到项目中

Pinia基础使用 - 计数器案例

1.定义store

2.组件使用store

实现步骤

引入

代码语言:javascript复制


import { createApp } from 'vue'
import persist from 'pinia-plugin-persistedstate'
import { createPinia } from 'pinia'
const pinia = createPinia(); //创建pinia实例
//导入pinia持久化插件
pinia.use(persist)


import App from './App.vue'


createApp(App).use(pinia).mount('#app')

定义

代码语言:javascript复制
import { defineStore } from "pinia";
import { ref } from "vue";
import { computed } from 'vue';
//定义store
// defineStore(仓库唯一标识,()=>{...})


export const useCounterStore = defineStore("counter", () => {
  //声明数据 state - count
  const count = ref(100)
  //声明操作数据的方法 action(普通函数)
  const addCount = () => {
    count.value  
  }
  const subCount = () => {
    count.value--
  }
  //声明基于数据派生的计算属性 getters(计算属性computed)
  const double = computed(() => count.value * 2)
  //声明数据 state - msg
  const msg = ref("hello pinia")


  return {
    count,
    msg,
    addCount,
    subCount,
    double
  }
}, {
  //persist: true //开启持久化
  persist:{
      key:'自定义持久化key',
      paths:['count'] //定义某个变量持久化
  }
)

使用

代码语言:javascript复制
<script setup>


import Son1 from './components/Son1.vue';
import Son2 from './components/Son2.vue';


import { useCounterStore } from '@/store/counter.js'
const counterStore = useCounterStore();






console.log(counterStore)
</script>


<template>
  <h1>{{ counterStore.count }}</h1>
  <h1>{{ counterStore.msg }}</h1>
  <son-1></son-1>
  <son-2></son-2>
</template>


<style scoped></style>

0 人点赞