Vue3-pinia、组件通信、插槽

pinia

集中式状态(数据)管理
组件间的数据共享

  • 搭建pinia环境
import { createPinia } from "pinia";
const pinia = createPinia();
//安装pinia
app.use(pinia);
  • 存储、读取数据
    在src/store 这个仓库下创建count.ts
    pinia中state函数存储数据

若是在reactive包裹ref,则reactive自动解包了,不需要.value

import { defineStore } from "pinia";
export const useCountStore = defineStore("count", {
  state() {
    //state函数,真正存储数据的地方
    return {
      sum: 6,
    };
  },
});

然后调用

import axios from "axios";
import { nanoid } from "nanoid";
import { useLoveStore } from "@/store/love";
const LoveStore = useLoveStore();
// function add() {
//   let id = talkList.length + 1;
//   talkList.push({ id, content: `这是第${id}条留言` });
// }
async function fetchTalks() {
  let result = await axios.get(
    "https://api.uomg.com/api/rand.qinghua?format=json"
  );
  console.log(result.data.content);
  let obj = { id: nanoid(), content: result.data.content };
  //   talkList.push({
  //     id: talkList.length + 1,
  //     content: result.data.content,
  //   });
  LoveStore.talkList.unshift(obj);
}
  • 修改数据
    三种方法
import { ref } from "vue";
import { useCountStore } from "@/store/count";
const countStore = useCountStore();
// console.log("@@@", countStore);  //等于countStore.$state.sum

let sum = ref(0);
let n = ref(1);
function add() {
  //方法一直接修改
  // countStore.sum += n.value;

  //方法二——适合多种属性同时修改
  // countStore.$patch({
  //   sum: 888,
  //   school: "尚硅谷",
  // });

  //方法三 利于复用
  countStore.increment(n.value);
}

function sub() {
  countStore.sum -= n.value;
}
import { defineStore } from "pinia";
export const useCountStore = defineStore("count", {
  //actions里存放一个个方法用于响应组件中的动作
  actions: {
    increment(value: any) {
      console.log("incremnet被调用了", value);
      if (this.sum < 10) {
      } //这个修改方法利于 以后的复用
      //this是当前的countStore
      console.log("this", this.sum);
      this.sum += value;
    },
  },
  state() {
    //state函数,真正存储数据的地方
    return {
      sum: 6,
      school: "hh",
    };
  },
});

  • storeToRefs
    const {sum,school} =storeToRefs(countStore) 只包裹store中的,而不对其他方法进行ref包裹

  • getters
    对数据不满意时可以进行getters函数改变

  • $subscribe

LoveStore.$subscribe((mutate, state) => {
  //mutate本次修改的信息,state数据
  // console.log("数据发生变化", mutate, state);
  //将数据存储到localStorage中,做到刷新不丢失
  //注意:localStorage只能存储字符串,所以需要将对象转换为字符串
  localStorage.setItem("talkList", JSON.stringify(state.talkList));
});
state() {
    return {
      // talkList: [
      //   { id: "njn1j1", content: "你怪怪的" },
      //   { id: "njkjb2", content: "我去" },
      //   { id: "hnjnjj3", content: "鹅鹅鹅饿" },
      // ],
      // 使用localStorage来持久化数据
      talkList: JSON.parse(localStorage.getItem("talkList") || "[]"),
    };
  • store 组合式使用
export const useLoveStore = defineStore("love", () => {
  const talkList = reactive(
    JSON.parse(localStorage.getItem("talkList") || "[]")
  );
  async function increment() {
    let result = await axios.get(
      "https://api.uomg.com/api/rand.qinghua?format=json"
    );
    console.log(result.data.content);
    let obj = { id: nanoid(), content: result.data.content };
    talkList.unshift(obj);
  }
  return { talkList, increment };
});

ref定义的数据在模板里面可以不用.value

组件通信

  • props :父子组件间
    • 父传子:
      父:<Child :car='car'/>
      子:defineProps(['car']) <h4>{{car}}
    • 子传父:
      父:
<Child :car='car' :sendToy='getToy' />
function getToy(value:string){
  toy.value=value;
}

子:

<button @click='sendToy(toy)' >玩具给父亲</button>
defineProps(['car','sendToy'])
  • 自定义事件实现子传父
    • 父:```
      <Son @send-toy="getToy" />
      import Son from "./son-self.vue";
      import { ref } from "vue";
      let toy = ref("");
      function getToy(value: string) {
      toy.value = value || "默认玩具";
      }
    - 子:```
<button @click="emit('send-toy', toy)">触发</button>
import { ref, onMounted } from "vue";
const toy = ref("遥控车");
//声明事件
const emit = defineEmits(["send-toy"]);
onMounted(() => {
  setTimeout(() => {
    emit("send-toy");
  }, 3000);
});
  • mitt
import mitt, { type Emitter } from "mitt";
type Events = {
  "send-toy": string;
  // 可以添加更多事件类型
};
const emitter: Emitter<Events> = mitt<Events>();
export default emitter;
<button @click="emitter.emit('send-toy', toy)">触发</button>
import emitter from "../utils/emitter";
import emitter from "../utils/emitter";
emitter.on("send-toy", (value: string) => {
  toy.value = value || "默认玩具";
});
onUnmounted(() => {
  emitter.off("send-toy");
});
  • v-model
    可以实现组件间的数据传递

是一个动态的value值和一个@input事件
$event.target
对于原生事件,$event就是事件对象=>可以.target
对于自定义事件,$event就是触发事件时所传递的数据=>不能.target
父:
<Son v-model="username" />
子:(ui组件库)

<input type="text" :value="modelValue" @input="emit('update:modelValue',
        (<HTMLInputElement>$event.target).value)"></input>
defineProps(['modelValue']);
const emit=defineEmits(['update:modelValue']);  
  • $sttrs
    用于实现祖->孙的通信
    父:
 <Son :a="a" :b="b" v-bind="{ x: 10, y: 20 }" :updateA="changeA" />
import Son from "./zz.vue";
let a = ref(1);
let b = ref(2);
function changeA(value: number) {
  a.value += value;
}

子:
<grandson v-bind="$attrs" />
则孙可以直接:

 <h4>{{ x }}--{{ y }}--{{ a }}</h4>
    <button @click="updateA(6)">点击改变A</button>
defineProps(["x", "y", "a", "b", "updateA"]);
  • $refs、$parent

$refs:父->子
$parent:子->父
父传子:

let house = ref(4);
let c1 = ref();
let c2 = ref();
function changeToy() {
  console.log(c1.value);
  c1.value.toy = "芭比娃娃";
  // 通过 mitt 发送事件,传递新的玩具名称
  //   emitter.emit("send-toy", "芭比娃娃");
}
function changecom() {
  console.log(c2.value);
  c2.value.computer = "Dell XPS";
}
function all(refs: { [key: string]: any }) {
  //   console.log(refs);
  for (let key in refs) {
    console.log(key, refs[key]);
    refs[key].book += 3;
  }
}

defineExpose({ toy, book });
子传父:

<button @click="subHouse($parent)">干掉房产</button>
function subHouse(parent: any) {
  console.log(parent);
  parent.house -= 1;
}
  • provide inject
    实现子孙间直接的通信
    父组件provide
// provide("qian", money.value); //向后代提供数据
function updateMoney(num: number) {
  money.value -= num;
}
provide("che", car);
//不可以写money:money.value  ->这是将.value一个值传过去了,而不是money这个响应式对象
provide("moneyContext", { money, updateMoney });

孙组件inject

let car = inject("che", { brand: "未知", price: 0 }); //获取祖先组件提供的数据
let { money, updateMoney } = inject("moneyContext", {
  money: 0,
  updateMoney: (x: number) => {},
});
  • 插槽
    • 默认插槽
      父:
<Category title="今日美食城市">
        <img :src="imgURL" alt="美食图片" />
      </Category>

子做插槽

<div class="category">
    <h3>{{ title }}</h3>
    <slot></slot>
    <!--默认插槽-->
  </div>
- 具名插槽

<slot name='s2'></slot>
此时需要在需插的内上标记:v-slot:s2 v-slot ->也可以写为#s2
默认插槽的name='default'

- 作用域插槽

在子组件中做插槽,但数据在子组件中。父组件想用子组件中的数据决定样式进行插入
父:

<Game>
      <template v-slot:qwe="a">
        <ul>
          <li v-for="game in a.youxi" :key="game.id">
            {{ game.name }}
          </li>
        </ul>
      </template>
    </Game>

子:
slot name='qwe' :youxi="games"></slot>

posted on 2025-08-20 18:22  Siannnn  阅读(23)  评论(0)    收藏  举报