上篇sv-print可视化打印组件不完全指南② 已经了解了 sv-print 组件库 的参数及插槽 。本期继续带你深入了解 sv-print ,自定义扩展 vue3、react 使用的组件
前言
sv-print 是一个使用 Svelte 构建的打印设计器组件。它也可以用于其他 UI 库/框架,如 React 、Vue 和 原生html。
首先我们要清楚 sv-print 、 @sv-print/vue3 、 @sv-print/react 它们之间的关系:
-
@sv-print/vue
依赖:sv-print
插件, 给 vue2 提供 Designer 组件。 -
@sv-print/vue3
依赖:sv-print
插件, 给 vue3 提供 Designer 组件。 -
@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 去创建 对应的组件就好。
本期应该学会去翻阅依赖源码,不要仅仅停留在使用阶段。
去发掘、发现惊喜。
问题并没有你想象的那么难处理,重要的是找对方法。
如果看到这里,你还是疑问,想要一对一技术指导,欢迎私信联系我。
记得点个赞咯~
评论区也可交流,想要不简说说什么技术,疑难解答。
下期再见👋🏻
