Skip to content
This repository was archived by the owner on Jul 6, 2025. It is now read-only.

Commit 6ac4b32

Browse files
committed
refactor(framework/vue): support base meta and link tag for Head
1 parent 3ef69ff commit 6ac4b32

File tree

2 files changed

+62
-22
lines changed

2 files changed

+62
-22
lines changed

framework/vue/head.ts

Lines changed: 61 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
import { defineComponent, inject, onBeforeUnmount } from "vue";
1+
import { defineComponent, inject, isVNode, onBeforeUnmount, VNode } from "vue";
22
import { ssrRenderComponent } from "vue/server-renderer";
33
import util from "../../lib/util.ts";
44

@@ -7,7 +7,7 @@ export const Head = defineComponent({
77
setup(_props, ctx) {
88
const ssrHeadCollection: string[] | undefined = inject("ssrHeadCollection");
99
if (ctx.slots.default && ssrHeadCollection) {
10-
const children = ctx.slots.default();
10+
const children = parseSlots(ctx.slots.default());
1111
children.forEach((vnode) => {
1212
const { props } = vnode;
1313
// add srr attr
@@ -25,31 +25,71 @@ export const Head = defineComponent({
2525
head.removeChild(el);
2626
}
2727
});
28-
},
29-
mounted() {
30-
const { document } = window;
31-
const insertedEls: Array<HTMLElement> = [];
28+
3229
if (this.$slots.default) {
33-
const defaultSlots = this.$slots.default();
34-
defaultSlots.forEach((vnode) => {
35-
const { type, children } = vnode;
36-
if (type === "title") {
37-
const el = document.createElement(type);
38-
if (util.isFilledString(children)) {
39-
el.innerText = children;
40-
} else if (util.isFilledArray(children)) {
41-
el.innerText = children.join("");
42-
}
43-
document.head.appendChild(el);
44-
insertedEls.push(el);
30+
const { document } = window;
31+
const insertedEls: Array<HTMLElement> = [];
32+
const children = parseSlots(this.$slots.default());
33+
children.forEach((vnode) => {
34+
const { type, props, children } = vnode;
35+
const el = document.createElement(type);
36+
if (children) el.innerText = children;
37+
if (props) {
38+
Object.keys(props).forEach((key) => {
39+
const value = props[key];
40+
el.setAttribute(key, String(value || ""));
41+
});
4542
}
43+
document.head.appendChild(el);
44+
insertedEls.push(el);
45+
});
46+
onBeforeUnmount(() => {
47+
insertedEls.forEach((el) => document.head.removeChild(el));
4648
});
4749
}
48-
onBeforeUnmount(() => {
49-
insertedEls.forEach((el) => document.head.removeChild(el));
50-
});
5150
},
5251
render() {
5352
return [];
5453
},
5554
});
55+
56+
type ParseSlots = {
57+
type: string;
58+
props?: Record<string, unknown> | null;
59+
children?: string | null;
60+
};
61+
62+
function parseSlots(vnodes: VNode[]): ParseSlots[] {
63+
const els: ParseSlots[] = [];
64+
const walk = (vnode: VNode) => {
65+
if (!isVNode(vnode)) {
66+
return;
67+
}
68+
69+
const { type, props, children } = vnode;
70+
71+
switch (type) {
72+
// ingore `script` and `no-script` tag
73+
// ingore `style` tag
74+
case "base":
75+
case "meta":
76+
case "link":
77+
// remove the children of base/meta/link elements
78+
els.push({ type, props });
79+
break;
80+
case "title":
81+
if (util.isFilledString(children)) {
82+
els.push({ type, props, children });
83+
} else if (util.isFilledArray(children)) {
84+
els.push({ type, props, children: children.join("") });
85+
} else {
86+
els.push({ type, props });
87+
}
88+
break;
89+
}
90+
};
91+
92+
vnodes.forEach((vnode) => walk(vnode));
93+
94+
return els;
95+
}

framework/vue/router.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -55,7 +55,7 @@ const createRouter = (props?: RootProps) => {
5555
const router = defineComponent({
5656
name: "Router",
5757
render() {
58-
return h(defaultExport as Component);
58+
return [h(defaultExport as Component)];
5959
},
6060
});
6161

0 commit comments

Comments
 (0)