-
Notifications
You must be signed in to change notification settings - Fork 168
/
head.ts
95 lines (87 loc) · 2.61 KB
/
head.ts
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
import { defineComponent, inject, isVNode, onBeforeUnmount, VNode } from "vue";
import { ssrRenderComponent } from "@vue/server-renderer";
import util from "../../shared/util.ts";
export const Head = defineComponent({
name: "Head",
setup(_props, ctx) {
const ssrHeadCollection: string[] | undefined = inject("ssrHeadCollection");
if (ctx.slots.default && ssrHeadCollection) {
const children = ctx.slots.default();
children.forEach((vnode) => {
const { props } = vnode;
// add srr attr
vnode.props = { ...props, ssr: "" };
const s = ssrRenderComponent({ render: () => vnode }) as string[];
ssrHeadCollection.push(...s);
});
}
},
beforeMount() {
// remove ssr head elements
const { head } = window.document;
Array.from(head.children).forEach((el: Element) => {
if (el.hasAttribute("ssr")) {
head.removeChild(el);
}
});
if (this.$slots.default) {
const { document } = window;
const insertedEls: Array<HTMLElement> = [];
const children = parseSlots(this.$slots.default());
children.forEach((vnode) => {
const { type, props, children } = vnode;
const el = document.createElement(type);
if (children) el.innerText = children;
if (props) {
Object.keys(props).forEach((key) => {
const value = props[key];
el.setAttribute(key, String(value || ""));
});
}
document.head.appendChild(el);
insertedEls.push(el);
});
onBeforeUnmount(() => {
insertedEls.forEach((el) => document.head.removeChild(el));
});
}
},
render() {
return [];
},
});
type ParseSlots = {
type: string;
props?: Record<string, unknown> | null;
children?: string | null;
};
function parseSlots(vnodes: VNode[]): ParseSlots[] {
const els: ParseSlots[] = [];
const walk = (vnode: VNode) => {
if (!isVNode(vnode)) {
return;
}
const { type, props, children } = vnode;
switch (type) {
// ingore `script` and `no-script` tag
// ingore `style` tag
case "base":
case "meta":
case "link":
// remove the children of base/meta/link elements
els.push({ type, props });
break;
case "title":
if (util.isFilledString(children)) {
els.push({ type, props, children });
} else if (util.isFilledArray(children)) {
els.push({ type, props, children: children.join("") });
} else {
els.push({ type, props });
}
break;
}
};
vnodes.forEach((vnode) => walk(vnode));
return els;
}