2
0
mirror of https://github.com/tenrok/vue-meta.git synced 2026-06-13 10:12:24 +03:00

fix: better ssr support

This commit is contained in:
pimlie
2021-05-17 02:09:34 +02:00
parent 8069449119
commit 1d847870e9
8 changed files with 47 additions and 41 deletions
+1 -1
View File
@@ -1,6 +1,6 @@
import { defineComponent } from 'vue'
import type { VNodeProps, AllowedComponentProps, ComponentCustomProps } from 'vue'
import { getCurrentManager } from './useApi'
import type { VNodeProps, AllowedComponentProps, ComponentCustomProps } from 'vue'
export const MetainfoImpl = defineComponent({
name: 'Metainfo',
+1 -1
View File
@@ -1,5 +1,5 @@
import type { MetaTagConfigKey, MetaTagName } from '../types'
import { tags } from './tags'
import type { MetaTagConfigKey, MetaTagName } from '../types'
export function getTagConfigItem (
tagOrName: Array<MetaTagName>,
+25 -21
View File
@@ -1,13 +1,13 @@
import { h, reactive, onUnmounted, App, Teleport, Comment, getCurrentInstance, ComponentInternalInstance, Slots } from 'vue'
import type { VNode, ComponentPublicInstance } from 'vue'
import { isArray, isFunction } from '@vue/shared'
import { createMergedObject, MergedObjectBuilder } from './object-merge'
import { renderMeta } from './render'
import { metaActiveKey } from './symbols'
import { Metainfo } from './Metainfo'
import type { ResolveMethod } from './object-merge'
import { defaultConfig } from './config/default'
import * as defaultResolver from './resolvers/deepest'
import type { VNode, ComponentPublicInstance } from 'vue'
import type { ResolveMethod } from './object-merge'
import type {
MetaActive,
@@ -18,7 +18,6 @@ import type {
MetaResolveContext,
MetaTeleports,
MetaSource,
MetaResolverSetup,
MetaProxy
} from './types'
@@ -26,10 +25,10 @@ export const ssrAttribute = 'data-vm-ssr'
export const active: MetaActive = reactive({})
export function addVnode (teleports: MetaTeleports, to: string, vnodes: VNode | Array<VNode>): void {
export function addVnode (isSSR: boolean, teleports: MetaTeleports, to: string, vnodes: VNode | Array<VNode>): void {
const nodes = (isArray(vnodes) ? vnodes : [vnodes]) as Array<VNode>
if (__BROWSER__) {
if (!isSSR) {
// Comments shouldnt have any use on the client as they are not reactive anyway
nodes.forEach((vnode, idx) => {
if (vnode.type === Comment) {
@@ -54,27 +53,29 @@ export function addVnode (teleports: MetaTeleports, to: string, vnodes: VNode |
}
// eslint-disable-next-line no-use-before-define
export type createMetaManagerMethod = (config: MetaConfig, resolver: MetaResolver | ResolveMethod) => MetaManager
export type CreateMetaManagerMethod = (isSSR: boolean, config: MetaConfig, resolver: MetaResolver | ResolveMethod) => MetaManager
export const createMetaManager = (config?: MetaConfig, resolver?: MetaResolver): MetaManager => MetaManager.create(config || defaultConfig, resolver || (defaultResolver as MetaResolver))
export const createMetaManager = (isSSR = false, config?: MetaConfig, resolver?: MetaResolver): MetaManager => MetaManager.create(isSSR, config || defaultConfig, resolver || (defaultResolver as MetaResolver))
export class MetaManager {
isSSR = false
config: MetaConfig
target: MergedObjectBuilder<MetaSource>
resolver?: MetaResolverSetup
resolver?: MetaResolver
ssrCleanedUp: boolean = false
constructor (config: MetaConfig, target: MergedObjectBuilder<MetaSource>, resolver: MetaResolver | ResolveMethod) {
constructor (isSSR: boolean, config: MetaConfig, target: MergedObjectBuilder<MetaSource>, resolver: MetaResolver | ResolveMethod) {
this.isSSR = isSSR
this.config = config
this.target = target
if (resolver && 'setup' in resolver && isFunction(resolver.setup)) {
this.resolver = resolver as unknown as MetaResolverSetup
this.resolver = resolver
}
}
static create: createMetaManagerMethod = (config, resolver) => {
static create: CreateMetaManagerMethod = (isSSR, config, resolver) => {
const resolve: ResolveMethod = (options, contexts, active, key, pathSegments) => {
if (isFunction(resolver)) {
return resolver(options, contexts, active, key, pathSegments)
@@ -86,7 +87,7 @@ export class MetaManager {
const mergedObject = createMergedObject<MetaSource>(resolve, active)
// TODO: validate resolver
const manager = new MetaManager(config, mergedObject, resolver)
const manager = new MetaManager(isSSR, config, mergedObject, resolver)
return manager
}
@@ -107,8 +108,9 @@ export class MetaManager {
})
const resolveContext: MetaResolveContext = { vm }
if (this.resolver) {
this.resolver.setup(resolveContext)
const { resolver } = this
if (resolver && resolver.setup) {
resolver.setup(resolveContext)
}
// TODO: optimize initial compute (once)
@@ -169,9 +171,10 @@ export class MetaManager {
render ({ slots }: { slots?: Slots } = {}): VNode[] {
// TODO: clean this method
const { isSSR } = this
// cleanup ssr tags if not yet done
if (__BROWSER__ && !this.ssrCleanedUp) {
if (!isSSR && !this.ssrCleanedUp) {
this.ssrCleanedUp = true
// Listen for DOM loaded because tags in the body couldnt
@@ -181,9 +184,9 @@ export class MetaManager {
const ssrTags = document.querySelectorAll(`[${ssrAttribute}]`)
if (ssrTags && ssrTags.length) {
Array.from(ssrTags).forEach(el => el.parentNode && el.parentNode.removeChild(el))
ssrTags.forEach(el => el.parentNode && el.parentNode.removeChild(el))
}
})
}, { once: true })
}
const teleports: MetaTeleports = {}
@@ -192,7 +195,7 @@ export class MetaManager {
const config = this.config[key] || {}
let renderedNodes = renderMeta(
{ metainfo: active, slots },
{ isSSR, metainfo: active, slots },
key,
active[key],
config
@@ -217,7 +220,7 @@ export class MetaManager {
}
for (const { to, vnode } of renderedNodes) {
addVnode(teleports, to || defaultTo || 'head', vnode)
addVnode(this.isSSR, teleports, to || defaultTo || 'head', vnode)
}
}
@@ -232,13 +235,14 @@ export class MetaManager {
const slot = slots[slotName]
if (isFunction(slot)) {
addVnode(teleports, tagName, slot({ metainfo: active }))
addVnode(this.isSSR, teleports, tagName, slot({ metainfo: active }))
}
}
}
return Object.keys(teleports).map((to) => {
return h(Teleport, { to }, teleports[to])
const teleport = teleports[to]
return h(Teleport, { to }, teleport)
})
}
}
+8 -4
View File
@@ -197,7 +197,7 @@ export function renderTag (
// console.info('FINAL TAG', finalTag)
// console.log(' ATTRIBUTES', attributes)
// console.log(' CONTENT', content)
// // console.log(data, attributes, config)
// console.log(data, attributes, config)
if (isRaw && content) {
attributes.innerHTML = content
@@ -221,11 +221,11 @@ export function renderAttributes (
// console.info('renderAttributes', key, data, config)
const { attributesFor } = config
if (!attributesFor) {
if (!attributesFor || !data) {
return
}
if (!__BROWSER__) {
if (context.isSSR) {
// render attributes in a placeholder vnode so Vue
// will render the string for us
return {
@@ -257,7 +257,11 @@ export function renderAttributes (
const { el, attrs } = cachedElements[attributesFor]
for (const attr in data) {
const content = getSlotContent(context, `${key}(${attr})`, data[attr], data)
let content = getSlotContent(context, `${key}(${attr})`, data[attr], data)
if (isArray(content)) {
content = content.join(',')
}
el.setAttribute(attr, content || '')
+5 -6
View File
@@ -1,13 +1,12 @@
import { renderToString } from '@vue/server-renderer'
import type { App } from 'vue'
import type { SSRContext } from '@vue/server-renderer'
export async function renderToStringWithMeta (app: App, ctx: SSRContext = {}): Promise<[string, SSRContext]> {
const html = await renderToString(app, ctx)
export async function renderMetaToString (app: App, ctx: SSRContext = {}): Promise<SSRContext> {
// TODO: better way of determining whether meta was rendered with the component or not
if (!ctx.teleports || !ctx.teleports.head) {
const teleports = app.config.globalProperties.$metaManager.render()
const { renderToString } = await import('@vue/server-renderer')
const teleports = app.config.globalProperties.$metaManager?.render()
await Promise.all(teleports.map((teleport: any) => renderToString(teleport, ctx)))
}
@@ -21,5 +20,5 @@ export async function renderToStringWithMeta (app: App, ctx: SSRContext = {}): P
}
}
return [html, ctx]
return ctx
}
+4 -5
View File
@@ -42,7 +42,7 @@ export interface MetaActive {
* Context passed to the meta resolvers
*/
export type MetaResolveContext = ResolveContext & {
vm: ComponentInternalInstance | undefined
vm?: ComponentInternalInstance
}
export type MetaResolveSetup = (context: MetaResolveContext) => void
@@ -52,9 +52,7 @@ export type MetaResolver = {
resolve: ResolveMethod
}
export type MetaResolverSetup = Modify<MetaResolver, {
setup: MetaResolveSetup
}>
export type MetaResolverSetup = Required<MetaResolver>
/**
* @internal
@@ -74,8 +72,9 @@ export interface MetaGuards {
* @internal
*/
export interface MetaRenderContext {
slots?: Slots
isSSR: boolean
metainfo: MetaActive
slots?: Slots
}
/**
+2 -2
View File
@@ -1,8 +1,8 @@
import { inject, getCurrentInstance, ComponentInternalInstance, isProxy, watch } from 'vue'
import type { MetaManager } from './manager'
import { metaActiveKey } from './symbols'
import type { MetaActive, MetaSource, MetaProxy } from './types'
import { applyDifference } from './utils/diff'
import type { MetaManager } from './manager'
import type { MetaActive, MetaSource, MetaProxy } from './types'
export function getCurrentManager (vm?: ComponentInternalInstance): MetaManager | undefined {
if (!vm) {
+1 -1
View File
@@ -3,7 +3,7 @@ export const pluck = <T extends Record<string, any>>(collection: T[], key: strin
const plucked: T[] = []
for (const row of collection) {
if (key in row) {
if (row && key in row) {
plucked.push(row[key])
if (callback) {