pinia vue3

修改值

https://blog.csdn.net/qq_42543244/article/details/123407045

只修改一个值:直接修改

请注意,store 是一个用 reactive 包装的对象,这意味着不需要在 getters 后面写 .value。就像 setup 中的 props 一样,我们不能对它进行解构 https://pinia.vuejs.org/zh/core-concepts/#using-the-store

@/stores/app.ts

import { defineStore } from "pinia";
const initState = {
  myState = 0;
  err: {
    msg: "",
    show: false,
  },
  save2tau: false,
};

export const useAppStore = defineStore("app", {
  state: () => initState,
  actions: {
    complexUpdate(x: number) {
	//随便写的复杂逻辑
      this.myState += this.myState + x;
      if (this.save2tau) this.myState %= 5;
      return "I 💔 前端";
    },
  },
  persist: true,
});

index.vue中的<script setup>部分

import { useAppStore } from "@/stores/app";
const appS = useAppStore();
appS.myState=1;

修改多个值:appStore.$patch({})

index.vue中的<script setup>部分

import { useAppStore } from "@/stores/app";
const appS = useAppStore();
appS.$patch({myState: 1, save2tau: true});

逻辑更复杂:actions内定义

见上文的complexUpdate函数

双向绑定ref()

v-bind:某属性是只读单向的
v-model是双向数据更新的,这时需要使用storeToRefs

<input v-model="appR.myState" />
import { useAppStore } from "@/stores/app";
import { storeToRefs } from "pinia";
const appS = useAppStore();
const appR = storeToRefs(appS);

此时appR.myState.value === appS.myState为true,appR.myState.value = 1等价于appS.myState = 1

引用传递/浅拷贝/指针ref()、值传递/深拷贝实际值.value

https://course.rs/basic/ownership/ownership.html#转移所有权
python的=赋值都是浅拷贝
js,基本数据类型number, boolean, string都是深拷贝,其余Object, [], function: () => Object浅拷贝。
rust中,基本数据类型str, slice都是深拷贝,其余包装对象都为浅拷贝。

// 浅拷贝,只复制值
const current = appS.myMediaList[appS.currentMediaId];
// 函数浅拷贝,调用current().myState,精简代码
// 小心appS.currentMediaId<0时,数组会越界访问
const current = () => appS.myMediaList[appS.currentMediaId];

这时watch就能监听到变化了。

watch(current, (current) => {
  pause();
  appS.curTime = 0;
  refresh();
  if (audio) play();
});

打包$patch,执行自定义函数

type MyType = typeof initState;

export const useAppStore = defineStore("app", {
  state: () => initState,
  actions: {
    update(partial: Partial<MyType>) {
      // 更新前
      this.$patch(partial);
      // this.$state = { ...this.$state, ...partial };
      // 更新后
    }
  }
});

watch in pinia

当pinia内部有状态变化时,自动触发某函数
https://github.com/vuejs/pinia/discussions/794

注意,要让 pinia 正确识别 state,你必须在 setup store 中返回 state 的所有属性。这意味着,你不能在 store 中使用私有属性。不完整返回会影响 SSR ,开发工具和其他插件的正常运行。

每个单文件,都可以用不同的api写法

选项式api: export default

  • 写起来像json配置
  • 一般来说,插件配置.ts建议用选项式api,避免出现插件预料之外的情况:如SSR
  • 可以使用多个<script>导出,把同一逻辑代码集中在一起。

组合式api: script setup

  • 项目初期写.vue时会很快,因为写起来更像原生ts代码
  • 共享变量作用域
  • hacky的写法,突破插件限制
  • 但也比选项式更难纠错
  • 需要掌握vue的生命周期:

script setup/export default → template/style → 子组件

reactiveObj = {...reactiveObj, ...partial}
Object.assign(reactiveObj, partial);

Object.assign 会修改,而使用扩展运算符的方法会创建一个新的对象。

很骚的写法

// Utilities
import { defineStore } from "pinia";
import { MediaItem } from "@/types";
import { Store } from "@tauri-apps/plugin-store";

const DEBOUNCE_TIME = 1000;
const REPEAT_TIME = 5000;
const DEFAULT_KEY = "initS";

let tauS = new Store("app.bin");

export const useAppStore = defineStore(
  "app",
  () => {
    const isTauri = inject("isTauri") as boolean;
    async function tauSet(val: any, key: string = DEFAULT_KEY) {
      if (isTauri) return await tauS.set(key, val);
    }
    async function tauGet(key: string = DEFAULT_KEY) {
      if (isTauri) return await tauS.get(key);
    }

    /**
     * @param key: string, will be abandoned. 临时方案
     */
    class Watch{
      timerDebounce?: NodeJS.Timeout | null;
      timerRepeat?: NodeJS.Timeout | null;

      stop(
        ref: Ref<any> | UnwrapNestedRefs,
        func?: Function | null,
        key?: string
      ) {
        return watch(ref, (newState) => {
          // console.log("watch", this.timerRepeat, timerDebounce);
          if (this.timerDebounce) {
            clearTimeout(this.timerDebounce);
            this.timerDebounce = null;
          }
          this.timerDebounce = setTimeout(() => {
            if (this.timerRepeat) {
              clearInterval(this.timerRepeat);
              this.timerRepeat = null; // 坑爹
            }
            if (func) func(newState, key);
            console.log("saved after debounce mode", this.timerDebounce, key);
            // console.log("saved in debounce mode", ref);
          }, DEBOUNCE_TIME);
          if (!this.timerRepeat) {
            this.timerRepeat = setInterval(() => {
              if (func) func(newState, key);
              console.log("saved in repeat mode", this.timerRepeat, key);
            }, REPEAT_TIME);
            if (func) func(newState, key);
            console.log("saved before repeat mode", this.timerRepeat, key);
          }
        });
      }
    }


    const curTime = ref(0);

    /**
     * ### Put non-continuously updated variables into initS~~tatic~~
     * 将非持续性的变量放到initS中。
     *
     * 假设:进度条秒数在播放后,是持续更新的;那么偶尔更新的变量,就叫非持续性变量.
     */
    let initS = reactive({
      myMediaList: [] as MediaItem[],
      currentMediaId: 0,
      selected: [] as number[],
      isLoop: false,
      isRandom: false,
      err: {
        msg: "",
        show: false,
      },
      save2tau: false,
    });
    type UnwrapNestedRefs = typeof initS;
    const initR = toRefs(initS);

    function addCurrentId(x: number) {
      initS.currentMediaId += initS.myMediaList.length + x;
      initS.currentMediaId %= initS.myMediaList.length;
    }

    if (isTauri) {
      tauGet().then((data) => {
        if (data) {
          console.log("hydrate", data);
          initS = { ...initS, ...(data as UnwrapNestedRefs) };
        }
      });
      tauGet("curTime").then((data) => {
        if (data) {
          console.log("hydrate", data);
          curTime.value = data as number;
        }
      });
    }

    const stop_curTime = new Watch().stop(curTime, tauSet, "curTime");
    const stop_S = new Watch().stop(curTime, tauSet);

    return {
      ...initR,
      curTime,
      stop_curTime,
      stop_S,
      addCurrentId,
    };
  },
  {
    persist: true,
  }
);
posted @ 2024-04-19 16:17  Nolca  阅读(17)  评论(0编辑  收藏  举报