IT俱乐部 JavaScript vue3静态渲染组件与动态渲染组件的区别小结

vue3静态渲染组件与动态渲染组件的区别小结

静态渲染组件

    
    

动态渲染组件

// ========== 计算属性(Getter) =========
// 根据 acceptType 选择受理样品表格组件
const currentTableComponent = computed(() => {
  // 受理样品表格-水质检材
  if (waterSampleTypes.includes(acceptType.value)) return ApplySampleSZTable;
  // 受理样品表格-公共场所
  if (publicPlaceTypes.includes(acceptType.value)) return ApplySampleCSTable;
  // 受理样品表格-消毒监测
  if (disinfectionTypes.includes(acceptType.value)) return ApplySampleXDTable;
  // 受理样品表格-其他
  return ApplySampleOtherTable;
});


    
    

完整代码示例

srccomponentscommonCommonApplySampleTable.vue

/**
 * 受理样品表格
 */
defineOptions({
  name: "CommonApplySampleTable"
});

import { addApplySample as addApplySampleService, addBlankApplySample, batchDeleteApplySamples } from "@/api";
import { ApplySampleCSTable, ApplySampleOtherTable, ApplySampleSZTable, ApplySampleXDTable } from "@/components";
import type { ApplySampleCreateRequest, ApplySampleResponse } from "@/openapi/types.gen";
import {
  acceptTypeSymbol,
  applySampleSelectionSymbol,
  applySamplesSymbol,
  applySymbol,
  operationCommandSymbol,
  scrollTopSymbol,
  transmitApplySamplesSymbol,
  transmitCurrentApplySampleSymbol
} from "@/symbols";
import { CommonOperationCommand } from "@/types";
import { ElMessage } from "element-plus";
import { cloneDeep, omit } from "lodash-es";
import { computed, inject, nextTick, provide, ref, toRaw, watch } from "vue";

// ========== 依赖注入(Inject:来自祖先组件) ==========
// ---------- 接收祖先提供的数据 ----------
// 受理样品列表
// const applySamples = inject<Ref>("applySamples", ref([]));
const applySamples = inject(applySamplesSymbol, ref([]));
// 受理类别
// const acceptType = inject<Ref>("acceptType", ref(""));
const acceptType = inject(acceptTypeSymbol, ref(""));
// 操作指令
// const operateCommand = inject<Ref>("operateCommand", ref("info-add"));
const operateCommand = inject(operationCommandSymbol, ref(CommonOperationCommand.ADD));
// 滚动到离顶部表头的距离
// const scrollTop = inject<Ref>("scrollTop", ref(0));
const scrollTop = inject(scrollTopSymbol, ref(0));
// 子受理
// const apply = inject<Ref>("apply", ref(null));
const apply = inject(applySymbol, ref(null));
// 受理样品选集
// const applySampleSelection = inject<Ref>("applySampleSelection", ref([]));
const applySampleSelection = inject(applySampleSelectionSymbol, ref([]));

// ---------- 接收祖先提供的方法 ----------
/**
 * 发送受理样品列表数据给祖先
 */
// const sendApplySamples = inject("transmitApplySamplesSymbol", (_data: ApplySampleResponse[]) => {});
const sendApplySamples = inject(transmitApplySamplesSymbol, (_data: ApplySampleResponse[]) => {});

// ========== 响应式状态(Ref / Reactive) ==========
// 本地副本受理样品列表
const localApplySamples = ref([]);
// 受理样品当前选择行
const currentApplySample = ref(null);

// 进度条的进度值:0~100
const percentage = ref(0);
// 进度条的颜色
const customColors = ref([
  { color: "#909399", percentage: 20 },
  { color: "#e6a23c", percentage: 40 },
  { color: "#409eff", percentage: 60 },
  { color: "#67c23a", percentage: 80 },
  { color: "#67c23a", percentage: 100 }
]);

// 水质检材类型
const waterSampleTypes = ["B01", "B02", "B03", "B04"];
// 公共场所类型
const publicPlaceTypes = [
  "G01",
  "G02",
  "G03",
  "G04",
  "G05",
  "G06",
  "G07",
  "G08",
  "G09",
  "Q01",
  "Q02",
  "Q03",
  "Q04",
  "Q05",
  "Q06",
  "Q07",
  "Q08",
  "Q09",
  "Q10"
];
// 消毒监测类型
const disinfectionTypes = ["S01", "S02", "S03", "S04", "S05", "S06", "S07", "S08", "S09", "S10", "S11"];

// 标识:是否来自祖先的更新。防循环处理:通过 isUpdatingFromAncestry 和 nextTick() 确保祖先组件更新不会触发后代组件同步
let isUpdatingFromAncestry = false;

// ========== 计算属性(Getter) =========
// 根据 acceptType 选择受理样品表格组件
const currentTableComponent = computed(() => {
  // 受理样品表格-水质检材
  if (waterSampleTypes.includes(acceptType.value)) return ApplySampleSZTable;
  // 受理样品表格-公共场所
  if (publicPlaceTypes.includes(acceptType.value)) return ApplySampleCSTable;
  // 受理样品表格-消毒监测
  if (disinfectionTypes.includes(acceptType.value)) return ApplySampleXDTable;
  // 受理样品表格-其他
  return ApplySampleOtherTable;
});

// ========== 公共方法(对外暴露) ==========
// ---------- 提供给后代组件 ----------
/**
 * 接收后代组件传递的受理样品当前选择行
 * @param applySample 当前选中的受理样品(null 表示取消选中)
 */
const receiveCurrentApplySample = (applySample: ApplySampleResponse | null) => {
  currentApplySample.value = applySample;
};

// ---------- 暴露给父组件 ----------
/**
 * 增加样品
 */
const addApplySample = async () => {
  // 生成样品编号,两位编号:如 01、02、03
  const sampleNo = (localApplySamples.value.length + 1).toString().padStart(2, "0");
  const applySample: ApplySampleResponse = {
    id: 0,
    outerApplyNo: apply.value!.outerApplyNo,
    applyNo: apply.value!.applyNo,
    sampleNo: sampleNo,
    sampleName: ""
  };
  try {
    const result = await addBlankApplySample({
      outerApplyNo: apply.value!.outerApplyNo,
      applyNo: apply.value!.applyNo,
      sampleNo: sampleNo
    });
    // 获取自增 ID,如果该样品已存在,返回 0
    applySample.id = result.data;

    // 服务端自增成功,获得自增 ID
    if (applySample.id) {
      // 增加数据行
      localApplySamples.value.push(applySample);
      scrollTop.value = 50 * localApplySamples.value.length + 100;
      sendApplySamples(localApplySamples.value);
    } else {
      ElMessage({
        type: "warning",
        message: `增加失败!新增的样品编号按序号递增为【${sampleNo}】,与现有的样品编号【${sampleNo}】重复`
      });
    }
  } catch (e) {
    // 因为全局异常已经处理了,这里不需要再提示,静默就可以了
    // ElMessage.warning(`增加失败!${e}`); // 页面会弹出以下提示:增加失败!AxiosError: Request failed with status code 400
  }
};

/**
 * 复增样品
 */
const copyAddApplySample = async () => {
  if (!currentApplySample.value) {
    ElMessage.warning("请选择复增的样品");
    return;
  }

  // 复制当前选择的样品数据
  const copyAddApplySample = cloneDeep(currentApplySample.value);
  // 生成样品编号,两位编号:如 01、02、03
  const newSampleNo = (localApplySamples.value.length + 1).toString().padStart(2, "0");

  // 前端检查样品编号是否存在
  if (localApplySamples.value.find((item) => item.sampleNo === newSampleNo)) {
    ElMessage({
      type: "warning",
      message: `增加失败!新增的样品编号按序号递增为【${newSampleNo}】,与现有的样品编号【${newSampleNo}】重复`
    });
    return;
  }

  // 修改样品编号
  copyAddApplySample.sampleNo = newSampleNo;
  try {
    // Response -> CreateRequest,使用 lodash 的 omit 去掉不需要的字段:id
    const createRequest = omit(copyAddApplySample, ["id"]) as ApplySampleCreateRequest;
    // 这里的 addApplySampleService 是由 addApplySample as addApplySampleService 别名导出
    const result = await addApplySampleService(createRequest);
    // 获取自增的 id,如果该样品已存在,返回 0
    copyAddApplySample.id = result.data;

    // 服务端自增成功,获得 id
    if (copyAddApplySample.id) {
      // 增加数据行
      localApplySamples.value.push(copyAddApplySample);
      scrollTop.value = 50 * localApplySamples.value.length + 100;
      sendApplySamples(localApplySamples.value);
    } else {
      ElMessage.warning(
        `增加失败!新增的样品编号按序号递增为【${newSampleNo}】,与现有的样品编号【${newSampleNo}】重复`
      );
    }
  } catch (error) {
    ElMessage.warning(`增加失败!${error}`);
  }
};

/**
 * 删除样品(乐观更新,失败回滚)
 * - 先从前端移除数据,再异步请求后端删除
 * - 请求失败时恢复数据和选中状态
 */
const deleteApplySample = async () => {
  // 获取勾选行的 ID 列表(过滤掉可能的 undefined)
  const ids = applySampleSelection.value.map((item) => item.id).filter((id): id is number => id !== undefined);
  if (ids.length === 0) return;

  // 备份整个数组和选集(用于回滚)
  const originalSamples = [...localApplySamples.value];
  const originalSelection = [...applySampleSelection.value];

  // 移除数据并清空选集(乐观更新)
  localApplySamples.value = localApplySamples.value.filter((item) => !ids.includes(item.id));
  applySampleSelection.value = [];

  // 同步数据给祖先组件
  sendApplySamples(localApplySamples.value);
  try {
    // 发送删除请求
    await batchDeleteApplySamples(ids);
  } catch (error) {
    // 失败回滚:直接恢复整个数组和选集
    localApplySamples.value = originalSamples;
    applySampleSelection.value = originalSelection;
    // 同步数据给祖先组件
    sendApplySamples(localApplySamples.value);
    console.error("删除样品失败:", error);
    ElMessage.error("删除样品失败,数据已恢复");
  }
};

// ========== 监听(Watch) ==========
// 监听祖先组件的 applySamples 变化,更新本地的 localApplySamples
watch(
  () => applySamples.value,
  async () => {
    // 标识是否来自祖先组件的更新为:是
    isUpdatingFromAncestry = true;
    // 使用类型断言,明确告诉 TypeScript:深拷贝后返回的是可变数组
    localApplySamples.value = cloneDeep(toRaw(applySamples.value)) as ApplySampleResponse[];
    // ⏳ 等待 DOM 更新完成
    await nextTick();
    // 标识是否来自祖先组件的更新为:否
    isUpdatingFromAncestry = false;
  },
  { deep: true, immediate: true } // 这里需要开启 immediate 即时监听,受理查询首次查看详情才能看到受理样品信息
);

// 监听本地的 localApplySamples 变化,同步数据给祖先组件
watch(
  () => localApplySamples.value,
  () => {
    // 判断是否来自祖先组件的更新为:否
    // 也就是自身更新的,才同步数据给祖先组件,祖先组件更新过来,则不需要再更新回去,从而防止循环
    if (!isUpdatingFromAncestry) {
      // 同步数据给祖先组件
      sendApplySamples(localApplySamples.value);
    }
  },
  { deep: true }
);

// ========== 对外暴露(Provide / Expose) ==========
// ---------- 提供给后代组件的方法 ----------
// 提供函数给后代,接收受理样品当前行选择
// provide("transmitCurrentApplySample", receiveCurrentApplySample);
provide(transmitCurrentApplySampleSymbol, receiveCurrentApplySample);

// ---------- 暴露给父组件(模板引用) ----------
defineExpose({
  /** 增加样品 */
  addApplySample,
  /** 删除样品 */
  deleteApplySample,
  /** 复增样品 */
  copyAddApplySample
});
.common-apply-sample-table-wrapper { height: 100%; /* 包裹 el-table 的 div,只要设置容器高度为100%,继承父级确定高度,就可以隐藏浏览器的滚动条,显示 el-table 的内置滚动条 */ }

动态菜单代码示例

      
      首页
            
{{ m1.name }} {{ m2.name }} {{ m1.name }} 个人中心 修改密码

到此这篇关于vue3静态渲染组件与动态渲染组件的区别小结的文章就介绍到这了,更多相关vue3静态渲染组件与动态渲染组件内容请搜索IT俱乐部以前的文章或继续浏览下面的相关文章希望大家以后多多支持IT俱乐部!

本文收集自网络,不代表IT俱乐部立场,转载请注明出处。https://www.2it.club/navsub/js/18567.html
上一篇
下一篇
联系我们

联系我们

在线咨询: QQ交谈

邮箱: 1120393934@qq.com

工作时间:周一至周五,9:00-17:30,节假日休息

关注微信
微信扫一扫关注我们

微信扫一扫关注我们

返回顶部