UNPKG

cp-metas

Version:

set website title

255 lines (233 loc) 8.72 kB
// @ts-ignore import { App, inject, watchEffect, getCurrentInstance, ComponentOptions } from 'vue' import { HeadsOptions, MetasOptions, HeadAttrs, isClient } from "../types/index"; function htmlspecialchars(str: string){ str = str.replace(/&/g, '&amp;'); str = str.replace(/</g, '&lt;'); str = str.replace(/>/g, '&gt;'); str = str.replace(/"/g, '&quot;'); str = str.replace(/'/g, '&#039;'); return str; } export class CpMetas { readonly #SSR!: boolean readonly #isPrivateApp: boolean = false private head!: HeadsOptions private readonly defaultTitle: string | undefined constructor(heads: HeadsOptions, isSSR: boolean, isPrivateApp: boolean) { this.head = heads this.#SSR = isSSR this.defaultTitle = heads.title this.#isPrivateApp = isPrivateApp } setHead(heads: HeadsOptions = {}) { !!heads.title && (this.head.title = heads.title || this.defaultTitle) const metas: MetasOptions[] = [] for (const i in heads.meta) { if (heads.meta.hasOwnProperty(i)) { const item: MetasOptions = heads.meta[i] const type = item.name || item.property switch (type) { case 'description': this.head.description = item.content break; case 'keywords': this.head.keywords = item.content break; case 'og:image': this.head.image = item.content break; default: metas.push(item); break; } } } this.head.metas = metas if (!this.#SSR) { const title: HeadAttrs<HTMLElement> = document.getElementsByTagName('title') if (title && title.length) { title[0].innerText = heads.title || this.defaultTitle } else { const title = document.createElement('title'); (title.innerText as any) = heads.title || this.defaultTitle; document.getElementsByTagName('head')[0].appendChild(title) } if (isClient && this.#isPrivateApp) { const oldMetas = document.querySelectorAll('meta[data-mark="cp"]'); for (let i in oldMetas) { if (oldMetas.hasOwnProperty(i)) { const _item = oldMetas[i]; _item.remove(); } } for (let i in this.head.metas) { const item = this.head.metas[i]; let key = ''; let val = ''; let content = ''; if (item.name) { key = 'name'; val = item.name; } else if (item.property) { key = 'property'; val = item.property; } if (!key) { return; } if (item.content) { content = item.content; } const meta = document.createElement('meta'); meta.setAttribute(key, val); meta.setAttribute('content', content); meta.setAttribute('data-mark', 'cp'); document.getElementsByTagName('head')[0].appendChild(meta) } } } } getHead() { let description = this.head.description; let keywords = this.head.keywords; let ogDescription = this.head.description; let ogImage = this.head.image; const metas = []; for (const i in this.head.metas) { if (this.head.metas.hasOwnProperty(i)) { const item: HeadAttrs<keyof MetasOptions> = this.head.metas[parseInt(i)]; const type = item.name || item.property; switch (type) { case 'description': description = htmlspecialchars(item.content); break; case 'keywords': if (typeof item.content === "string" ) { keywords = htmlspecialchars(item.content); } else if (item.content instanceof Array) { let k = []; for (const i in item.content) { k.push(htmlspecialchars(item.content[i])) } keywords = k.join("、"); } break; case 'og:description': ogDescription = htmlspecialchars(item.content); break; case 'og:image': ogImage = item.content; break; default: item.content = htmlspecialchars(item.content); metas.push(item); break; } } } return { title: this.head.title, meta: [ { name: 'description', content: description }, { name: 'keywords', content: keywords }, { property: 'og:title', content: this.head.title }, { property: 'og:description', content: ogDescription }, { property: 'og:image', content: ogImage }, ...metas ], }; } renderToString() { const heads = this.getHead(); let string = `<title>${heads.title}</title>`; for(const i in heads.meta) { const meta = heads.meta[i]; let key = ''; let val = ''; if (meta.name) { key = 'name'; val = meta.name; } else if (meta.property) { key = 'property'; val = meta.property; } if (!key) { return; } string += `<meta ${key}="${val}" content="${meta.content}">`; } const date = new Date(); const time = date.getFullYear().toString() + '-' + (date.getMonth() + 1).toString() + '-' + date.getDate().toString() + ' ' + date.getHours().toString() + ':' + date.getMinutes().toString() + ':' + date.getSeconds().toString(); string += `<meta name="updatetime" content="${time}">`; return string; } } export function usMeta(obj: HeadsOptions | undefined): void { const head: CpMetas | undefined = inject('cpMeta') !!head && head.setHead(obj) } export const cpMeta = { data() { const instance = getCurrentInstance(); return { // @ts-ignore _cpMetaHeadFn: instance?.type.head } }, created() { // @ts-ignore if (this._cpMetaHeadFn) { // @ts-ignore this.$cpMeta.setHead(this._cpMetaHeadFn()); } }, activated() { // @ts-ignore if (this._cpMetaHeadFn) { // @ts-ignore this.$cpMeta.setHead(this._cpMetaHeadFn()); } }, mounted() { // @ts-ignore if (this._cpMetaHeadFn) { watchEffect(() => { // @ts-ignore this.$cpMeta.setHead(this._cpMetaHeadFn()); }) } } } export default function createMeta(heads: HeadsOptions, isSSR: boolean = false, isPrivateApp: boolean = false) { return { install(app: App, option: ComponentOptions) { const $cpMeta = new CpMetas(heads, isSSR, isPrivateApp); if (option.mixin) { app.mixin(cpMeta) } app.provide('cpMeta', $cpMeta); app.config.globalProperties.$cpMeta = $cpMeta; } } }