mirror of
https://github.com/nuxt/nuxt.git
synced 2024-11-27 08:02:01 +00:00
Use vue-meta for meta tags
This commit is contained in:
parent
e34d9d69bb
commit
39f431efdf
@ -151,24 +151,6 @@ export function flatRoutes (router, path = '', routes = []) {
|
|||||||
return routes
|
return routes
|
||||||
}
|
}
|
||||||
|
|
||||||
export function attrsStr (attrObj = {}, exclude = []) {
|
|
||||||
return Object.keys(attrObj)
|
|
||||||
.filter(attr => !exclude.includes(attr))
|
|
||||||
.map(attr => {
|
|
||||||
if (typeof attrObj[attr] !== 'string') {
|
|
||||||
return attr
|
|
||||||
}
|
|
||||||
|
|
||||||
const val = attrObj[attr].replace('"', '\'')
|
|
||||||
|
|
||||||
if (attr === 'hid') {
|
|
||||||
attr = 'data-hid'
|
|
||||||
}
|
|
||||||
|
|
||||||
return `${attr}="${val}"`
|
|
||||||
}).join(' ')
|
|
||||||
}
|
|
||||||
|
|
||||||
export function cleanChildrenRoutes (routes, isChild = false) {
|
export function cleanChildrenRoutes (routes, isChild = false) {
|
||||||
let start = -1
|
let start = -1
|
||||||
let routesIndex = []
|
let routesIndex = []
|
||||||
|
@ -1,5 +1,7 @@
|
|||||||
|
|
||||||
import { attrsStr } from 'utils'
|
import Vue from 'vue'
|
||||||
|
import VueMeta from 'vue-meta'
|
||||||
|
import VueServerRenderer from 'vue-server-renderer'
|
||||||
import LRU from 'lru-cache'
|
import LRU from 'lru-cache'
|
||||||
|
|
||||||
export default class MetaRenderer {
|
export default class MetaRenderer {
|
||||||
@ -7,68 +9,70 @@ export default class MetaRenderer {
|
|||||||
this.nuxt = nuxt
|
this.nuxt = nuxt
|
||||||
this.renderer = renderer
|
this.renderer = renderer
|
||||||
this.options = nuxt.options
|
this.options = nuxt.options
|
||||||
|
this.vueRenderer = VueServerRenderer.createRenderer()
|
||||||
this.cache = LRU({})
|
this.cache = LRU({})
|
||||||
|
|
||||||
|
// Add VueMeta to Vue (this is only for SPA mode)
|
||||||
|
// See lib/app/index.js
|
||||||
|
Vue.use(VueMeta, {
|
||||||
|
keyName: 'head',
|
||||||
|
attribute: 'data-n-head',
|
||||||
|
ssrAttribute: 'data-n-head-ssr',
|
||||||
|
tagIDKeyName: 'hid'
|
||||||
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
render ({ url = '/' }) {
|
getMeta(url) {
|
||||||
let head = this.cache.get(url)
|
return new Promise((resolve, reject) => {
|
||||||
|
const vm = new Vue({
|
||||||
if (head) {
|
render: (h) => h(), // Render empty html tag
|
||||||
return head
|
head: this.options.head || {}
|
||||||
}
|
|
||||||
|
|
||||||
head = ''
|
|
||||||
|
|
||||||
// Title
|
|
||||||
if (typeof this.options.head.title === 'string') {
|
|
||||||
head += `<title data-n-head="true">${this.options.head.title || ''}</title>`
|
|
||||||
}
|
|
||||||
|
|
||||||
// Meta
|
|
||||||
if (Array.isArray(this.options.head.meta)) {
|
|
||||||
this.options.head.meta.forEach(meta => {
|
|
||||||
head += `<meta data-n-head="true" ${attrsStr(meta)}/>`
|
|
||||||
})
|
})
|
||||||
}
|
this.vueRenderer.renderToString(vm, (err) => {
|
||||||
|
if (err) return reject(err)
|
||||||
// Links
|
resolve(vm.$meta().inject())
|
||||||
if (Array.isArray(this.options.head.link)) {
|
|
||||||
this.options.head.link.forEach(link => {
|
|
||||||
head += `<link data-n-head="true" ${attrsStr(link)}/>`
|
|
||||||
})
|
})
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
async render ({ url = '/' }) {
|
||||||
|
let meta = this.cache.get(url)
|
||||||
|
|
||||||
|
if (meta) {
|
||||||
|
return meta
|
||||||
}
|
}
|
||||||
|
|
||||||
// Style
|
meta = {
|
||||||
if (Array.isArray(this.options.head.style)) {
|
HTML_ATTRS: '',
|
||||||
this.options.head.style.forEach(style => {
|
BODY_ATTRS: '',
|
||||||
head += `<style data-n-head="true" ${attrsStr(style, ['cssText'])}>${style.cssText || ''}</style>`
|
HEAD: ''
|
||||||
})
|
|
||||||
}
|
}
|
||||||
|
// Get vue-meta context
|
||||||
// Script
|
const m = await this.getMeta(url)
|
||||||
if (Array.isArray(this.options.head.script)) {
|
// HTML_ATTRS
|
||||||
this.options.head.script.forEach(script => {
|
meta.HTML_ATTRS = 'data-n-head-ssr ' + m.htmlAttrs.text()
|
||||||
head += `<script data-n-head="true" ${attrsStr(script, ['innerHTML'])}>${script.innerHTML || ''}</script>`
|
// BODY_ATTRS
|
||||||
})
|
meta.BODY_ATTRS = m.bodyAttrs.text()
|
||||||
}
|
// HEAD tags
|
||||||
|
meta.HEAD = m.meta.text() + m.title.text() + m.link.text() + m.style.text() + m.script.text() + m.noscript.text()
|
||||||
// Resource Hints
|
// Resource Hints
|
||||||
const clientManifest = this.renderer.resources.clientManifest
|
const clientManifest = this.renderer.resources.clientManifest
|
||||||
if (this.options.render.resourceHints && clientManifest) {
|
if (this.options.render.resourceHints && clientManifest) {
|
||||||
const publicPath = clientManifest.publicPath || '/_nuxt/'
|
const publicPath = clientManifest.publicPath || '/_nuxt/'
|
||||||
// Pre-Load initial resources
|
// Pre-Load initial resources
|
||||||
if (Array.isArray(clientManifest.initial)) {
|
if (Array.isArray(clientManifest.initial)) {
|
||||||
head += clientManifest.initial.map(r => `<link rel="preload" href="${publicPath}${r}" as="script" />`).join('')
|
meta.HEAD += clientManifest.initial.map(r => `<link rel="preload" href="${publicPath}${r}" as="script" />`).join('')
|
||||||
}
|
}
|
||||||
|
|
||||||
// Pre-Fetch async resources
|
// Pre-Fetch async resources
|
||||||
if (Array.isArray(clientManifest.async)) {
|
if (Array.isArray(clientManifest.async)) {
|
||||||
head += clientManifest.async.map(r => `<link rel="prefetch" href="${publicPath}${r}" />`).join('')
|
meta.HEAD += clientManifest.async.map(r => `<link rel="prefetch" href="${publicPath}${r}" />`).join('')
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
this.cache.set(url, head)
|
// Set meta tags inside cache
|
||||||
|
this.cache.set(url, meta)
|
||||||
|
|
||||||
return head
|
return meta
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
74
lib/core/meta.old.js
Normal file
74
lib/core/meta.old.js
Normal file
@ -0,0 +1,74 @@
|
|||||||
|
|
||||||
|
import { attrsStr } from 'utils'
|
||||||
|
import LRU from 'lru-cache'
|
||||||
|
|
||||||
|
export default class MetaRenderer {
|
||||||
|
constructor (nuxt, renderer) {
|
||||||
|
this.nuxt = nuxt
|
||||||
|
this.renderer = renderer
|
||||||
|
this.options = nuxt.options
|
||||||
|
this.cache = LRU({})
|
||||||
|
}
|
||||||
|
|
||||||
|
render ({ url = '/' }) {
|
||||||
|
let head = this.cache.get(url)
|
||||||
|
|
||||||
|
if (head) {
|
||||||
|
return head
|
||||||
|
}
|
||||||
|
|
||||||
|
head = ''
|
||||||
|
|
||||||
|
// Title
|
||||||
|
if (typeof this.options.head.title === 'string') {
|
||||||
|
head += `<title data-n-head="true">${this.options.head.title || ''}</title>`
|
||||||
|
}
|
||||||
|
|
||||||
|
// Meta
|
||||||
|
if (Array.isArray(this.options.head.meta)) {
|
||||||
|
this.options.head.meta.forEach(meta => {
|
||||||
|
head += `<meta data-n-head="true" ${attrsStr(meta)}/>`
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
// Links
|
||||||
|
if (Array.isArray(this.options.head.link)) {
|
||||||
|
this.options.head.link.forEach(link => {
|
||||||
|
head += `<link data-n-head="true" ${attrsStr(link)}/>`
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
// Style
|
||||||
|
if (Array.isArray(this.options.head.style)) {
|
||||||
|
this.options.head.style.forEach(style => {
|
||||||
|
head += `<style data-n-head="true" ${attrsStr(style, ['cssText'])}>${style.cssText || ''}</style>`
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
// Script
|
||||||
|
if (Array.isArray(this.options.head.script)) {
|
||||||
|
this.options.head.script.forEach(script => {
|
||||||
|
head += `<script data-n-head="true" ${attrsStr(script, ['innerHTML'])}>${script.innerHTML || ''}</script>`
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
// Resource Hints
|
||||||
|
const clientManifest = this.renderer.resources.clientManifest
|
||||||
|
if (this.options.render.resourceHints && clientManifest) {
|
||||||
|
const publicPath = clientManifest.publicPath || '/_nuxt/'
|
||||||
|
// Pre-Load initial resources
|
||||||
|
if (Array.isArray(clientManifest.initial)) {
|
||||||
|
head += clientManifest.initial.map(r => `<link rel="preload" href="${publicPath}${r}" as="script" />`).join('')
|
||||||
|
}
|
||||||
|
|
||||||
|
// Pre-Fetch async resources
|
||||||
|
if (Array.isArray(clientManifest.async)) {
|
||||||
|
head += clientManifest.async.map(r => `<link rel="prefetch" href="${publicPath}${r}" />`).join('')
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
this.cache.set(url, head)
|
||||||
|
|
||||||
|
return head
|
||||||
|
}
|
||||||
|
}
|
@ -10,7 +10,7 @@ import _ from 'lodash'
|
|||||||
import { join, resolve } from 'path'
|
import { join, resolve } from 'path'
|
||||||
import fs from 'fs-extra'
|
import fs from 'fs-extra'
|
||||||
import { createBundleRenderer } from 'vue-server-renderer'
|
import { createBundleRenderer } from 'vue-server-renderer'
|
||||||
import { getContext, setAnsiColors, isUrl, attrsStr } from 'utils'
|
import { getContext, setAnsiColors, isUrl } from 'utils'
|
||||||
import Debug from 'debug'
|
import Debug from 'debug'
|
||||||
import Youch from '@nuxtjs/youch'
|
import Youch from '@nuxtjs/youch'
|
||||||
import { SourceMapConsumer } from 'source-map'
|
import { SourceMapConsumer } from 'source-map'
|
||||||
@ -446,13 +446,12 @@ export default class Renderer extends Tapable {
|
|||||||
// Basic response if SSR is disabled or spa data provided
|
// Basic response if SSR is disabled or spa data provided
|
||||||
const spa = context.spa || (context.res && context.res.spa)
|
const spa = context.spa || (context.res && context.res.spa)
|
||||||
if (this.noSSR || spa) {
|
if (this.noSSR || spa) {
|
||||||
const HEAD = this.metaRenderer.render(context)
|
const { HTML_ATTRS, BODY_ATTRS, HEAD } = await this.metaRenderer.render(context)
|
||||||
const HTML_ATTRS = attrsStr(this.options.head.htmlAttrs)
|
|
||||||
const APP = `<div id="__nuxt">${this.resources.loadingHTML}</div>`
|
const APP = `<div id="__nuxt">${this.resources.loadingHTML}</div>`
|
||||||
|
|
||||||
const data = {
|
const data = {
|
||||||
HTML_ATTRS,
|
HTML_ATTRS,
|
||||||
BODY_ATTRS: '',
|
BODY_ATTRS,
|
||||||
HEAD,
|
HEAD,
|
||||||
APP
|
APP
|
||||||
}
|
}
|
||||||
|
Loading…
Reference in New Issue
Block a user