普通视图

发现新文章,点击刷新页面。
今天 — 2026年1月16日首页

Vue3向全局广播数据变化

作者 Rrvive
2026年1月16日 11:12

✅ 功能点

我们要做到:

  • 🧠 ruleForm 仍然是唯一状态源
  • 🔁 formDataToJson 自动跟随 ruleForm
  • ⚡ 子组件 直接拿“已处理好的 params”
  • 🚫 不在每个组件里重复写转换逻辑

一、升级 useFilterState(关键)

✅ 改造点:加入 params(computed)

useFilterState.ts

import { reactive, provide, watch, computed } from "vue";
import { ElMessage } from "element-plus";

export const FILTER_KEY = Symbol("FILTER_STATE");

export function useFilterState() {
  const ruleForm = reactive({
    time_menu: "day",
    timeRange: [] as any[],
    service_info: [] as any[],
  });

  /** ✅ 派生:接口参数 */
  const params = computed(() => {
    if (ruleForm.time_menu === "custom") {
      if (ruleForm.timeRange.length === 2) {
        const [start_time, end_time] = ruleForm.timeRange;
        return {
          start_time,
          end_time,
          service_info: ruleForm.service_info,
          time_menu: ruleForm.time_menu,
        };
      }
      return null;
    }

    return {
      time_menu: ruleForm.time_menu,
      service_info: ruleForm.service_info,
    };
  });

  /** 可扩展的 change hook */
  const onChangeCbs: Function[] = [];

  watch(
    ruleForm,
    () => {
      onChangeCbs.forEach((cb) => cb(ruleForm));
    },
    { deep: true }
  );

  provide(FILTER_KEY, {
    ruleForm,
    params,
    onFilterChange: (cb: Function) => onChangeCbs.push(cb),
  });

  return {
    ruleForm,
    params,
  };
}

useFilterInject.ts

import { inject } from "vue";
import { FILTER_KEY } from "./useFilterState";

export function useFilterInject() {
  const ctx = inject<any>(FILTER_KEY);
  if (!ctx) {
    throw new Error("useFilterInject 必须在 useFilterState 下使用");
  }
  return ctx;
}


二、页面中用法(更干净了)

<script setup lang="ts">
import { useFilterState } from "@/composables/useFilterState";

const { ruleForm, params } = useFilterState();
</script>

三、子组件中如何用(重点)

任意子组件

<script setup lang="ts">
import { watchEffect } from "vue";
import { useFilterInject } from "@/composables/useFilterInject";
import { getRiskInfo } from "@/api/visualizedOperations";

const { params } = useFilterInject();

async function fetchData() {
  if (!params.value) return; // custom 未选时间
  await getRiskInfo(params.value);
}

watchEffect(fetchData);
</script>

🎯 效果:

  • ruleForm 任意字段变化
  • params 自动重新计算
  • watchEffect 自动触发
  • 所有组件 同步刷新

四、如果你还想保留 ElMessage 提示

不要在 computed 里直接弹消息(副作用)
更优雅的方式是:提交或请求前校验

推荐写法

function validateParams(params: any) {
  if (!params) {
    ElMessage.warning("请选择时间范围");
    return false;
  }
  return true;
}

使用时:

if (!validateParams(params.value)) return;

五、为什么这是「最佳实践」

原来 现在
formDataToJson scattered 单一来源
子组件 自己拼参数 直接用
维护成本
复用
状态一致性 易乱

六、结构总结(你现在的架构)

页面
 ├─ useFilterState()
 │   ├─ ruleForm   ✅ 原始状态
 │   ├─ params     ✅ 派生参数
 │
 ├─ AlarmView
 │   └─ watchEffect(params) → 请求
 │
 └─ DataView
     └─ watchEffect(params) → 请求

七、一句话记住这个模式

状态在 ruleForm,逻辑在 computed,请求在组件

这套模式特别适合:

  • 可视化运营页面
  • 多组件联动筛选
  • 不想上 Pinia / URL

❌
❌