sv-print可视化打印组件不完全指南③

上篇sv-print可视化打印组件不完全指南② 已经了解了 sv-print 组件库参数及插槽 。本期继续带你深入了解 sv-print自定义扩展 vue3、react 使用的组件

前言

sv-print 是一个使用 Svelte 构建的打印设计器组件。它也可以用于其他 UI 库/框架,如 ReactVue原生html

首先我们要清楚 sv-print@sv-print/vue3@sv-print/react 它们之间的关系:

  1. @sv-print/vue 依赖: sv-print 插件, 给 vue2 提供 Designer 组件。

  2. @sv-print/vue3 依赖: sv-print 插件, 给 vue3 提供 Designer 组件。

  3. @sv-print/react 依赖: sv-print 插件, 给 react 提供 Designer 组件。

总结来说: @sv-print/vue3@sv-print/react 只是一个中间件 ,它只是为特定环境提供一个组件。

所以咱们可以不依赖这些,仅依赖sv-print来自定义自己的 Designer 组件

开干

以 vue3 为例:

先安装依赖: pnpm i sv-print

创建个组件文件: designer.vue

html 复制代码
<template>
  <div ref="el" style="height: 100%"></div>
</template>

<script lang="ts">
import { createApp, defineComponent, onMounted, ref, watch, createVNode, h } from "vue";
import * as SVPrint from "sv-print";
  
export default defineComponent({
  name: "designer",
  // 参数我就不多说了,会的人,知道看文档
  // 更会的人 知道去看 依赖 .d.ts 文件
  props: {
    // 可以根据项目情况,自己设定默认值
    autoConnect: {
      type: Boolean,
      default: true,
    },
    config: Object,
    providers: Array,
    providerMap: Object,
    clearProviderContainer: Boolean,
    showPanels: Boolean,
    plugins: Array,
    template: Object,
    designOptions: Object,
    printData: {
      type: Object || Array,
      default: () => {},
    },
    templateKey: {
      type: String,
      default: "default-template",
    },
    title: {
      type: String,
      default: "默认模板",
    },
    authKey: String,
    headerLogoHtml: String,
    headerTitle: String,
    events: Object,
    tags: Array,
    styleOption: Object,
    showOption: Object,
    paperList: Array,
    theme: String,
    themeList: Array,
    miniMapOriginMode: {
      type: Boolean,
      default: false,
    },
    previewOptions: Object,
    onPreviewClick: Function,
    onImageChooseClick: Function,
    onPanelAddClick: Function,
    onFunctionClick: Function,
  },
  emits: ["onDesigned"],
  setup(props, { slots, emit }) {
    // DOM 实例
    const el = ref(null);
    // 设计器
    const designerRef = ref(null);
    
    // 这是关键方法
    const createSlots = () => {
      let temp = {};
      for (const key in slots) {
        let vNode = createVNode(slots[key]);
        let Slot = defineComponent({
          render() {
            return h("div", vNode);
          },
        });
        let slot = createApp(Slot).mount(document.createElement("div"));
        // HTMLCollection not Array
        temp[key] =
          slot.$el.children.length > 1 ? [...slot.$el.children] : slot.$el.firstElementChild;
      }
      // 创建 svelte slot
      return SVPrint.createSlots(temp);
    };
    // 监听参数变化
    watch(
      () => props,
      (newValue) => {
        const copy = { ...newValue };
        for (let k in copy) {
          if (copy[k] === undefined) {
            delete copy[k];
          }
        }
        copy.$$slots = createSlots();
        designerRef.value?.$set(copy);
      },
      { deep: true }
    );
    // 当前el 挂载后,去挂载 sv-print 设计器
    onMounted(() => {
      const designer = new SVPrint.Designer({
        target: el.value!, // 挂载的DOM节点
        props: {
          ...props,
          ...{ $$scope: {}, $$slots: createSlots() },
        },
      });
      // 设计器的回调,再次抛出给 vue 组件
      designer.$on("onDesigned", (e) => {
        emit("onDesigned", e);
      });
      designerRef.value = designer;
    });
    return { el };
  },
});
</script>

这就是 @sv-print/vue3源码

参数我就不多说了,会的人,知道看文档

更会的人知道去看 依赖 .d.ts 文件

扩展动态更新模板

仔细阅读后,你会发现,可以watch 模板参数,实现动态更新模板。会的人不用多说,为什么我还有多说?

因为这是不简说

我...

html 复制代码
<script lang="ts">
// 省略xxx

// 该缓存就缓存咯~
const designerUtils = ref(null); 

// 监听模板json,调用update
watch(
  () => props.template,
  (newValue) => {
    const json = { ...newValue };
    // 你说为什么,注意执行顺序,还可以优化的哟~
    if(designerUtils.value) {
      designerUtils.printTemplate?.update(json);
    }
  },
  { deep: true }
);
// 改造一下
onMounted(() => {
  const designer = new SVPrint.Designer({
    target: el.value!,
    props: {
      ...props,
      ...{ $$scope: {}, $$slots: createSlots() },
    },
  });
  designer.$on("onDesigned", (e) => {
    designerUtils.value = e.detail.designerUtils;
    emit("onDesigned", e);
  });
  designerRef.value = designer;
});
</script>

自定义 Header 组件

哈? 啥 .d.ts

  • 教你,教你,教你怎么看

创建个组件文件: header.vue

html 复制代码
<template>
  <div ref="el" style="height: 100%"></div>
</template>

<script lang="ts">
import { createApp, defineComponent, onMounted, ref, watch, createVNode, h } from "vue";
import * as SVPrint from "sv-print";
  
export default defineComponent({
  name: "designer",
  props: {
    title: {
      type: String,
      default: "sv-print",
    },
    // 省略 xxx
  },
  setup(props, { slots, emit }) {
    const el = ref(null);
    const headerRef = ref(null);
    
    // 这是关键方法
    const createSlots = () => {
      let temp = {};
      for (const key in slots) {
        let vNode = createVNode(slots[key]);
        let Slot = defineComponent({
          render() {
            return h("div", vNode);
          },
        });
        let slot = createApp(Slot).mount(document.createElement("div"));
        // HTMLCollection not Array
        temp[key] =
          slot.$el.children.length > 1 ? [...slot.$el.children] : slot.$el.firstElementChild;
      }
      // 创建 svelte slot
      return SVPrint.createSlots(temp);
    };
    
    onMounted(() => {
      const header = new SVPrint.Header({
        target: el.value!,
        props: {
          ...props,
          ...{ $$scope: {}, $$slots: createSlots() },
        },
      });
      headerRef.value = header;
    });
    return { el };
  }
});
</script>

仔细看,这个扩展就这样搞完了。

总结

至于 react 组件 原理也是一样的。拿到真是 DOM 去创建 对应的组件就好。

本期应该学会去翻阅依赖源码,不要仅仅停留在使用阶段。

去发掘、发现惊喜。

问题并没有你想象的那么难处理,重要的是找对方法。

如果看到这里,你还是疑问,想要一对一技术指导,欢迎私信联系我。

记得点个赞咯~

评论区也可交流,想要不简说说什么技术,疑难解答。

下期再见👋🏻

相关推荐
前端风云志4 分钟前
TypeScript结构化类型初探
javascript
musk121221 分钟前
electron 打包太大 试试 tauri , tauri 安装打包demo
前端·electron·tauri
翻滚吧键盘1 小时前
js代码09
开发语言·javascript·ecmascript
万少1 小时前
第五款 HarmonyOS 上架作品 奇趣故事匣 来了
前端·harmonyos·客户端
OpenGL1 小时前
Android targetSdkVersion升级至35(Android15)相关问题
前端
rzl022 小时前
java web5(黑马)
java·开发语言·前端
Amy.Wang2 小时前
前端如何实现电子签名
前端·javascript·html5
海天胜景2 小时前
vue3 el-table 行筛选 设置为单选
javascript·vue.js·elementui
今天又在摸鱼2 小时前
Vue3-组件化-Vue核心思想之一
前端·javascript·vue.js
蓝婷儿2 小时前
每天一个前端小知识 Day 21 - 浏览器兼容性与 Polyfill 策略
前端