mirror of
https://github.com/tenrok/vue-meta.git
synced 2026-06-14 18:22:24 +03:00
feat: first work on vue v3 composition metainfo app
This commit is contained in:
@@ -0,0 +1,4 @@
|
||||
node_modules
|
||||
__build__
|
||||
dist
|
||||
|
||||
@@ -10,8 +10,8 @@
|
||||
<!-- li><a href="basic">Basic</a></li>
|
||||
<li><a href="basic-render">Basic Render</a></li>
|
||||
<li><a href="keep-alive">Keep alive</a></li>
|
||||
<li><a href="multiple-apps">Usage with multiple apps</a></li -->
|
||||
<li><a href="ssr">SSR</a></li>
|
||||
<li><a href="multiple-apps">Usage with multiple apps</a></li>
|
||||
<li><a href="ssr">SSR</a></li -->
|
||||
<li><a href="vue-router">Usage with vue-router</a></li>
|
||||
<!-- li><a href="vuex">Usage with vuex</a></li>
|
||||
<li><a href="vuex-async">Usage with vuex + async actions</a></li>
|
||||
|
||||
+18
-18
@@ -1,23 +1,23 @@
|
||||
window.users.push({
|
||||
'id': 1,
|
||||
'name': 'Leanne Graham',
|
||||
'username': 'Bret',
|
||||
'email': 'Sincere@april.biz',
|
||||
'address': {
|
||||
'street': 'Kulas Light',
|
||||
'suite': 'Apt. 556',
|
||||
'city': 'Gwenborough',
|
||||
'zipcode': '92998-3874',
|
||||
'geo': {
|
||||
'lat': '-37.3159',
|
||||
'lng': '81.1496'
|
||||
id: 1,
|
||||
name: 'Leanne Graham',
|
||||
username: 'Bret',
|
||||
email: 'Sincere@april.biz',
|
||||
address: {
|
||||
street: 'Kulas Light',
|
||||
suite: 'Apt. 556',
|
||||
city: 'Gwenborough',
|
||||
zipcode: '92998-3874',
|
||||
geo: {
|
||||
lat: '-37.3159',
|
||||
lng: '81.1496'
|
||||
}
|
||||
},
|
||||
'phone': '1-770-736-8031 x56442',
|
||||
'website': 'hildegard.org',
|
||||
'company': {
|
||||
'name': 'Romaguera-Crona',
|
||||
'catchPhrase': 'Multi-layered client-server neural-net',
|
||||
'bs': 'harness real-time e-markets'
|
||||
phone: '1-770-736-8031 x56442',
|
||||
website: 'hildegard.org',
|
||||
company: {
|
||||
name: 'Romaguera-Crona',
|
||||
catchPhrase: 'Multi-layered client-server neural-net',
|
||||
bs: 'harness real-time e-markets'
|
||||
}
|
||||
})
|
||||
|
||||
+18
-18
@@ -1,23 +1,23 @@
|
||||
window.users.push({
|
||||
'id': 2,
|
||||
'name': 'Ervin Howell',
|
||||
'username': 'Antonette',
|
||||
'email': 'Shanna@melissa.tv',
|
||||
'address': {
|
||||
'street': 'Victor Plains',
|
||||
'suite': 'Suite 879',
|
||||
'city': 'Wisokyburgh',
|
||||
'zipcode': '90566-7771',
|
||||
'geo': {
|
||||
'lat': '-43.9509',
|
||||
'lng': '-34.4618'
|
||||
id: 2,
|
||||
name: 'Ervin Howell',
|
||||
username: 'Antonette',
|
||||
email: 'Shanna@melissa.tv',
|
||||
address: {
|
||||
street: 'Victor Plains',
|
||||
suite: 'Suite 879',
|
||||
city: 'Wisokyburgh',
|
||||
zipcode: '90566-7771',
|
||||
geo: {
|
||||
lat: '-43.9509',
|
||||
lng: '-34.4618'
|
||||
}
|
||||
},
|
||||
'phone': '010-692-6593 x09125',
|
||||
'website': 'anastasia.net',
|
||||
'company': {
|
||||
'name': 'Deckow-Crist',
|
||||
'catchPhrase': 'Proactive didactic contingency',
|
||||
'bs': 'synergize scalable supply-chains'
|
||||
phone: '010-692-6593 x09125',
|
||||
website: 'anastasia.net',
|
||||
company: {
|
||||
name: 'Deckow-Crist',
|
||||
catchPhrase: 'Proactive didactic contingency',
|
||||
bs: 'synergize scalable supply-chains'
|
||||
}
|
||||
})
|
||||
|
||||
+18
-18
@@ -1,23 +1,23 @@
|
||||
window.users.push({
|
||||
'id': 3,
|
||||
'name': 'Clementine Bauch',
|
||||
'username': 'Samantha',
|
||||
'email': 'Nathan@yesenia.net',
|
||||
'address': {
|
||||
'street': 'Douglas Extension',
|
||||
'suite': 'Suite 847',
|
||||
'city': 'McKenziehaven',
|
||||
'zipcode': '59590-4157',
|
||||
'geo': {
|
||||
'lat': '-68.6102',
|
||||
'lng': '-47.0653'
|
||||
id: 3,
|
||||
name: 'Clementine Bauch',
|
||||
username: 'Samantha',
|
||||
email: 'Nathan@yesenia.net',
|
||||
address: {
|
||||
street: 'Douglas Extension',
|
||||
suite: 'Suite 847',
|
||||
city: 'McKenziehaven',
|
||||
zipcode: '59590-4157',
|
||||
geo: {
|
||||
lat: '-68.6102',
|
||||
lng: '-47.0653'
|
||||
}
|
||||
},
|
||||
'phone': '1-463-123-4447',
|
||||
'website': 'ramiro.info',
|
||||
'company': {
|
||||
'name': 'Romaguera-Jacobson',
|
||||
'catchPhrase': 'Face to face bifurcated interface',
|
||||
'bs': 'e-enable strategic applications'
|
||||
phone: '1-463-123-4447',
|
||||
website: 'ramiro.info',
|
||||
company: {
|
||||
name: 'Romaguera-Jacobson',
|
||||
catchPhrase: 'Face to face bifurcated interface',
|
||||
bs: 'e-enable strategic applications'
|
||||
}
|
||||
})
|
||||
|
||||
+18
-18
@@ -1,23 +1,23 @@
|
||||
window.users.push({
|
||||
'id': 4,
|
||||
'name': 'Patricia Lebsack',
|
||||
'username': 'Karianne',
|
||||
'email': 'Julianne.OConner@kory.org',
|
||||
'address': {
|
||||
'street': 'Hoeger Mall',
|
||||
'suite': 'Apt. 692',
|
||||
'city': 'South Elvis',
|
||||
'zipcode': '53919-4257',
|
||||
'geo': {
|
||||
'lat': '29.4572',
|
||||
'lng': '-164.2990'
|
||||
id: 4,
|
||||
name: 'Patricia Lebsack',
|
||||
username: 'Karianne',
|
||||
email: 'Julianne.OConner@kory.org',
|
||||
address: {
|
||||
street: 'Hoeger Mall',
|
||||
suite: 'Apt. 692',
|
||||
city: 'South Elvis',
|
||||
zipcode: '53919-4257',
|
||||
geo: {
|
||||
lat: '29.4572',
|
||||
lng: '-164.2990'
|
||||
}
|
||||
},
|
||||
'phone': '493-170-9623 x156',
|
||||
'website': 'kale.biz',
|
||||
'company': {
|
||||
'name': 'Robel-Corkery',
|
||||
'catchPhrase': 'Multi-tiered zero tolerance productivity',
|
||||
'bs': 'transition cutting-edge web services'
|
||||
phone: '493-170-9623 x156',
|
||||
website: 'kale.biz',
|
||||
company: {
|
||||
name: 'Robel-Corkery',
|
||||
catchPhrase: 'Multi-tiered zero tolerance productivity',
|
||||
bs: 'transition cutting-edge web services'
|
||||
}
|
||||
})
|
||||
|
||||
@@ -5,12 +5,12 @@ const {
|
||||
processIf,
|
||||
getBaseTransformPreset,
|
||||
createObjectExpression,
|
||||
createObjectProperty,
|
||||
createObjectProperty
|
||||
} = require('@vue/compiler-core')
|
||||
|
||||
const { parse } = require('@vue/compiler-dom')
|
||||
|
||||
function headTransform(node, context) {
|
||||
function headTransform (node, context) {
|
||||
console.log('NODE', node)
|
||||
if (node.type === 1 /* NodeTypes.ELEMENT */) {
|
||||
return () => {
|
||||
@@ -24,7 +24,7 @@ function headTransform(node, context) {
|
||||
node.children.length === 1 ? node.children[0] : 'null'
|
||||
)
|
||||
|
||||
//options.properties.push(option)
|
||||
// options.properties.push(option)
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -32,7 +32,7 @@ function headTransform(node, context) {
|
||||
module.exports = function (source, map) {
|
||||
// TODO: add options
|
||||
const ast = parse(source)
|
||||
//console.log('AST', ast)
|
||||
// console.log('AST', ast)
|
||||
|
||||
const [nodeTransforms, directiveTransforms] = getBaseTransformPreset({
|
||||
prefixIdentifiers: true
|
||||
@@ -49,7 +49,7 @@ module.exports = function (source, map) {
|
||||
|
||||
const result = generate(ast, { mode: 'module' })
|
||||
|
||||
console.log(result.code)
|
||||
console.log(result.code)
|
||||
|
||||
this.callback(null, `
|
||||
import { computed } from 'vue'
|
||||
|
||||
@@ -77,6 +77,6 @@ setTimeout(() => {
|
||||
|
||||
setTimeout(() => {
|
||||
console.log('trigger app 4')
|
||||
const App = Vue.extend({ template: `<div>app 4</div>` })
|
||||
const App = Vue.extend({ template: '<div>app 4</div>' })
|
||||
new App().$mount()
|
||||
}, 10000)
|
||||
|
||||
@@ -0,0 +1,57 @@
|
||||
<script>
|
||||
import { h, ref, computed, inject, Teleport } from 'vue'
|
||||
import { renderMeta } from './render'
|
||||
|
||||
export default {
|
||||
props: {
|
||||
metainfo: {
|
||||
type: Object,
|
||||
required: true
|
||||
}
|
||||
},
|
||||
setup() {
|
||||
const mapping = inject('__vueMetaConfig')
|
||||
|
||||
return {
|
||||
mapping
|
||||
}
|
||||
},
|
||||
render() {
|
||||
const targets = {}
|
||||
|
||||
for (const key in this.metainfo) {
|
||||
const config = this.mapping[key] || {}
|
||||
|
||||
const vnodes = renderMeta(this, key, this.metainfo[key], config)
|
||||
let target = (key !== 'base' && this.metainfo[key].target) || config.target || 'head'
|
||||
|
||||
if (Array.isArray(vnodes)) {
|
||||
for (const vnode of vnodes) {
|
||||
if (vnode.__vm_target) {
|
||||
target = vnode.__vm_target
|
||||
delete vnode.__vm_target
|
||||
}
|
||||
|
||||
if (!targets[target]) {
|
||||
targets[target] = []
|
||||
}
|
||||
|
||||
targets[target].push(vnode)
|
||||
}
|
||||
continue
|
||||
}
|
||||
|
||||
if (!targets[target]) {
|
||||
targets[target] = []
|
||||
}
|
||||
|
||||
targets[target].push(vnodes)
|
||||
continue
|
||||
}
|
||||
console.log('TARGETS', targets)
|
||||
return Object.keys(targets).map(target => {
|
||||
return h(Teleport, { to: target }, targets[target])
|
||||
})
|
||||
}
|
||||
}
|
||||
</script>
|
||||
@@ -0,0 +1,119 @@
|
||||
const defaults = {
|
||||
title: {
|
||||
contentAttributes: false
|
||||
},
|
||||
base: {
|
||||
contentAttributes: [
|
||||
'href',
|
||||
'target'
|
||||
]
|
||||
},
|
||||
meta: {
|
||||
nameAttribute: 'name',
|
||||
contentAttributes: [
|
||||
'content',
|
||||
'name',
|
||||
'http-equiv',
|
||||
'charset'
|
||||
]
|
||||
},
|
||||
link: {
|
||||
contentAttributes: [
|
||||
'href',
|
||||
'crossorigin',
|
||||
'rel',
|
||||
'media',
|
||||
'integrity',
|
||||
'hreflang',
|
||||
'type',
|
||||
'referrerpolicy',
|
||||
'sizes',
|
||||
'imagesrcset',
|
||||
'imagesizes',
|
||||
'as',
|
||||
'color'
|
||||
]
|
||||
},
|
||||
style: {
|
||||
contentAttributes: [
|
||||
'media'
|
||||
]
|
||||
},
|
||||
script: {
|
||||
contentAttributes: [
|
||||
'src',
|
||||
'type',
|
||||
'nomodule',
|
||||
'async',
|
||||
'defer',
|
||||
'crossorigin',
|
||||
'integrity',
|
||||
'referrerpolicy'
|
||||
]
|
||||
},
|
||||
noscript: {
|
||||
contentAttributes: false
|
||||
}
|
||||
}
|
||||
|
||||
const defaultMapping = {
|
||||
body: {
|
||||
tag: 'script',
|
||||
target: 'body'
|
||||
},
|
||||
base: {
|
||||
contentAttribute: 'href'
|
||||
},
|
||||
charset: {
|
||||
tag: 'meta',
|
||||
nameless: true,
|
||||
contentAttribute: 'charset'
|
||||
},
|
||||
description: {
|
||||
tag: 'meta'
|
||||
},
|
||||
og: {
|
||||
group: true,
|
||||
namespacedAttribute: true,
|
||||
tag: 'meta',
|
||||
nameAttribute: 'property'
|
||||
},
|
||||
twitter: {
|
||||
group: true,
|
||||
namespacedAttribute: true,
|
||||
tag: 'meta'
|
||||
}
|
||||
}
|
||||
|
||||
export {
|
||||
defaults,
|
||||
defaultMapping
|
||||
}
|
||||
|
||||
export function hasConfig (name) {
|
||||
return !!defaults[name] || !!defaultMapping[name]
|
||||
}
|
||||
|
||||
export function getConfigKey (name, key, config, dontLog) {
|
||||
if (!dontLog) {
|
||||
// console.log('getConfigKey', name, key, getConfigKey(name, key, config, true), config)
|
||||
}
|
||||
|
||||
if (config && key in config) {
|
||||
return config[key]
|
||||
}
|
||||
|
||||
if (Array.isArray(name)) {
|
||||
for (const _name of name) {
|
||||
if (_name && _name in defaults) {
|
||||
return defaults[_name][key]
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (name in defaults) {
|
||||
return defaults[name][key]
|
||||
}
|
||||
|
||||
return undefined
|
||||
}
|
||||
+18
-13
@@ -1,24 +1,30 @@
|
||||
import { markRaw, reactive, computed, onMounted } from 'vue'
|
||||
import { markRaw, reactive, onMounted } from 'vue'
|
||||
import { defaultMapping } from './config'
|
||||
|
||||
const apps = {}
|
||||
let appId = 1
|
||||
|
||||
export function createMeta () {
|
||||
const id = Symbol()
|
||||
export function createMeta ({ config }) {
|
||||
const id = Symbol(`vue-meta-${appId++}`)
|
||||
|
||||
const Meta = {
|
||||
id,
|
||||
|
||||
install(app) {
|
||||
install (app) {
|
||||
let watchersAdded = false
|
||||
|
||||
app.provide('__vueMetaConfig', {
|
||||
...defaultMapping,
|
||||
...config
|
||||
})
|
||||
|
||||
app.mixin({
|
||||
created() {
|
||||
created () {
|
||||
if (this === this.$root) {
|
||||
|
||||
watchersAdded = true
|
||||
}
|
||||
|
||||
if (!this.metaData) {
|
||||
if (!this.metaData || watchersAdded) {
|
||||
return
|
||||
}
|
||||
|
||||
@@ -33,7 +39,7 @@ export function createMeta () {
|
||||
}
|
||||
}
|
||||
|
||||
const __meta = markRaw({
|
||||
this.__meta = markRaw({
|
||||
depth
|
||||
})
|
||||
console.log('CREATED', this, this.metaData, depth)
|
||||
@@ -49,15 +55,14 @@ export function createMeta () {
|
||||
return Meta
|
||||
}
|
||||
|
||||
export function useMeta () {
|
||||
export function useMeta (rawMetainfo) {
|
||||
onMounted(vmMounted)
|
||||
|
||||
const metaData = reactive([])
|
||||
console.log(this)
|
||||
const metainfo = reactive(rawMetainfo)
|
||||
|
||||
return metaData
|
||||
return metainfo
|
||||
}
|
||||
|
||||
function vmMounted() {
|
||||
function vmMounted () {
|
||||
console.log('MOUNTED', this, arguments)
|
||||
}
|
||||
|
||||
@@ -0,0 +1,154 @@
|
||||
import { h } from 'vue'
|
||||
import { getConfigKey } from './config'
|
||||
|
||||
export function renderMeta (ctx, key, data, config) {
|
||||
console.info('renderMeta', key, data, config)
|
||||
|
||||
if (config.group) {
|
||||
return renderGroup(ctx, key, data, config)
|
||||
}
|
||||
|
||||
return renderTag(ctx, key, data, config)
|
||||
}
|
||||
|
||||
export function renderGroup (ctx, key, data, config) {
|
||||
console.info('renderGroup', key, data, config)
|
||||
|
||||
if (Array.isArray(data)) {
|
||||
config.contentAttributes = getConfigKey([key, config.tag], 'contentAttributes', config)
|
||||
return data.map(_data => renderTag(ctx, key, _data, config))
|
||||
}
|
||||
|
||||
return Object.keys(data).map((childKey) => {
|
||||
const groupConfig = {
|
||||
group: key,
|
||||
data
|
||||
}
|
||||
|
||||
if (config.namespaced || config.namespacedAttribute) {
|
||||
let namespace
|
||||
if (config.namespaced) {
|
||||
namespace = config.namespaced === true ? key : config.namespaced
|
||||
groupConfig.tagNamespace = namespace
|
||||
} else {
|
||||
namespace = config.namespacedAttribute === true ? key : config.namespacedAttribute
|
||||
groupConfig.fullName = `${namespace}:${childKey}`
|
||||
groupConfig.slotName = `${namespace}(${childKey})`
|
||||
}
|
||||
}
|
||||
|
||||
return renderTag(ctx, key, data[childKey], config, groupConfig)
|
||||
})
|
||||
}
|
||||
|
||||
export function renderTag (ctx, key, data, config = {}, groupConfig = {}) {
|
||||
if (!config.group && Array.isArray(data)) {
|
||||
return renderTag(ctx, key, { content: data }, config, groupConfig)
|
||||
}
|
||||
|
||||
const { tag = config.tag || key } = data
|
||||
const {
|
||||
slotName = key,
|
||||
fullName = key
|
||||
} = groupConfig
|
||||
|
||||
let content, hasChilds
|
||||
|
||||
if (Array.isArray(data)) {
|
||||
return data.map((child) => {
|
||||
return renderTag(ctx, key, child, config, groupConfig)
|
||||
})
|
||||
} else if (data.content && Array.isArray(data.content)) {
|
||||
content = data.content.map((child) => {
|
||||
if (typeof child === 'string') {
|
||||
return child
|
||||
}
|
||||
return renderTag(ctx, key, child, config, groupConfig)
|
||||
})
|
||||
hasChilds = true
|
||||
} else {
|
||||
content = data
|
||||
}
|
||||
|
||||
let { attrs: attributes } = data
|
||||
if (!attributes && typeof data === 'object') {
|
||||
attributes = {
|
||||
...data
|
||||
}
|
||||
delete attributes.tag
|
||||
delete attributes.content
|
||||
delete attributes.target
|
||||
} else {
|
||||
attributes = {}
|
||||
}
|
||||
|
||||
if (hasChilds) {
|
||||
content = getSlotContent(ctx, slotName, content, config, data)
|
||||
} else {
|
||||
const contentAttributes = getConfigKey(tag, 'contentAttributes', config)
|
||||
|
||||
if (contentAttributes) {
|
||||
if (!config.nameless) {
|
||||
const nameAttribute = getConfigKey(tag, 'nameAttribute', config)
|
||||
if (nameAttribute) {
|
||||
attributes[nameAttribute] = fullName
|
||||
}
|
||||
}
|
||||
|
||||
const contentAttribute = config.contentAttribute || contentAttributes[0]
|
||||
attributes[contentAttribute] = getSlotContent(ctx, slotName, attributes[contentAttribute] || content, config, groupConfig)
|
||||
content = undefined
|
||||
} else {
|
||||
content = getSlotContent(ctx, slotName, content, config, data, true)
|
||||
}
|
||||
}
|
||||
|
||||
const finalTag = groupConfig.tagNamespace
|
||||
? `${groupConfig.tagNamespace}:${tag}`
|
||||
: tag
|
||||
|
||||
console.info('FINAL TAG', finalTag)
|
||||
console.log(' ATTRIBUTES', attributes)
|
||||
console.log(' CONTENT', content)
|
||||
// console.log(data, attributes, config)
|
||||
|
||||
if (hasChilds) {
|
||||
for (const child of content) {
|
||||
if (typeof child === 'string') {
|
||||
continue
|
||||
}
|
||||
|
||||
if (child.type === finalTag) {
|
||||
return content
|
||||
}
|
||||
|
||||
break
|
||||
}
|
||||
}
|
||||
|
||||
const vnode = h(finalTag, attributes, content)
|
||||
|
||||
if (data.target) {
|
||||
vnode.__vm_target = data.target
|
||||
}
|
||||
|
||||
return vnode
|
||||
}
|
||||
|
||||
export function getSlotContent ({ metainfo, $slots }, slotName, content, config, groupConfig) {
|
||||
if (!$slots[slotName]) {
|
||||
return content
|
||||
}
|
||||
|
||||
const slotProps = {
|
||||
content,
|
||||
metainfo
|
||||
}
|
||||
|
||||
if (groupConfig.group) {
|
||||
slotProps[groupConfig.group] = groupConfig.data
|
||||
}
|
||||
|
||||
content = $slots[slotName](slotProps)
|
||||
return content[0].children
|
||||
}
|
||||
+5
-6
@@ -1,11 +1,10 @@
|
||||
import { createSSRApp } from 'vue'
|
||||
import { createRouter, createMemoryHistory } from 'vue-router'
|
||||
import VueMeta from '../../'
|
||||
|
||||
/*Vue.use(Router)
|
||||
/* Vue.use(Router)
|
||||
Vue.use(VueMeta, {
|
||||
tagIDKeyName: 'hid'
|
||||
})*/
|
||||
}) */
|
||||
|
||||
export default function createMyApp () {
|
||||
const Home = {
|
||||
@@ -116,7 +115,7 @@ export default function createMyApp () {
|
||||
users: process.server ? [] : window.users
|
||||
}
|
||||
},
|
||||
mounted() {
|
||||
mounted () {
|
||||
const { set, remove } = this.$meta().addApp('client-only')
|
||||
set({
|
||||
bodyAttrs: { class: 'client-only' }
|
||||
@@ -149,11 +148,11 @@ export default function createMyApp () {
|
||||
|
||||
app.use(router)
|
||||
|
||||
/*const { set } = app.$meta().addApp('custom')
|
||||
/* const { set } = app.$meta().addApp('custom')
|
||||
set({
|
||||
bodyAttrs: { class: 'custom-app' },
|
||||
meta: [{ charset: 'utf-8' }]
|
||||
})*/
|
||||
}) */
|
||||
|
||||
return { app, router }
|
||||
}
|
||||
|
||||
@@ -1,8 +1,8 @@
|
||||
import path from 'path'
|
||||
import fs from 'fs-extra'
|
||||
import template from 'lodash/template'
|
||||
const { renderToString } = require('@vue/server-renderer')
|
||||
import createApp from './App'
|
||||
const { renderToString } = require('@vue/server-renderer')
|
||||
|
||||
const templateFile = path.resolve(__dirname, 'app.template.html')
|
||||
const templateContent = fs.readFileSync(templateFile, { encoding: 'utf8' })
|
||||
@@ -18,12 +18,12 @@ export async function renderPage ({ url }) {
|
||||
await router.push(url.substr(4))
|
||||
|
||||
await router.isReady()
|
||||
/*console.log(router)
|
||||
/* console.log(router)
|
||||
const matchedComponents = router.getMatchedComponents()
|
||||
// no matched routes, reject with 404
|
||||
if (!matchedComponents.length) {
|
||||
return reject({ code: 404 })
|
||||
}*/
|
||||
} */
|
||||
|
||||
const appHtml = await renderToString(app)
|
||||
|
||||
@@ -41,7 +41,7 @@ export async function renderPage ({ url }) {
|
||||
head: () => {},
|
||||
bodyPrepend: () => {},
|
||||
bodyAppend: () => {}
|
||||
//...app.$meta().inject()
|
||||
// ...app.$meta().inject()
|
||||
})
|
||||
|
||||
return pageHtml
|
||||
|
||||
@@ -3,12 +3,13 @@
|
||||
<p>Has metaInfo been updated due to navigation? {{ metaUpdated }}</p>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
/*
|
||||
<head type="template">
|
||||
<title v-if="title">{{ title }}</title>
|
||||
<meta v-for="meta in metas" :name="meta.name" :content="meta.content" />
|
||||
</head>
|
||||
|
||||
<script>
|
||||
*/
|
||||
import { ref, onMounted } from 'vue'
|
||||
import { useRoute } from 'vue-router'
|
||||
|
||||
|
||||
+103
-74
@@ -1,27 +1,17 @@
|
||||
import { createApp, defineComponent, reactive, toRefs, h, onMounted } from 'vue'
|
||||
import VueMeta from 'vue-meta'
|
||||
import { createApp, defineComponent, reactive, toRefs, h } from 'vue'
|
||||
import { createRouter, createWebHistory } from 'vue-router'
|
||||
import About from './about.vue'
|
||||
import Metainfo from '../next/Metainfo.vue'
|
||||
import { createMeta, useMeta } from '../next'
|
||||
|
||||
/*Vue.use(VueMeta, {
|
||||
refreshOnceOnNavigation: true
|
||||
})*/
|
||||
|
||||
const meta = createMeta({
|
||||
|
||||
})
|
||||
import About from './about.vue'
|
||||
|
||||
let metaUpdated = 'no'
|
||||
|
||||
const ChildComponent = defineComponent({
|
||||
name: 'child-component',
|
||||
props: {
|
||||
page: String
|
||||
},
|
||||
template: `
|
||||
<metainfo>
|
||||
<title>Another Title</title>
|
||||
</metainfo>
|
||||
<div>
|
||||
<h3>You're looking at the <strong>{{ page }}</strong> page</h3>
|
||||
<p>Has metaInfo been updated due to navigation? {{ metaUpdated }}</p>
|
||||
@@ -38,41 +28,20 @@ const ChildComponent = defineComponent({
|
||||
}
|
||||
},
|
||||
created () {
|
||||
console.log(this)
|
||||
//console.log(this)
|
||||
},
|
||||
setup () {
|
||||
const metaData = useMeta({
|
||||
})
|
||||
|
||||
const state = reactive({
|
||||
date: null,
|
||||
metaUpdated
|
||||
})
|
||||
|
||||
|
||||
onMounted(function vmMounted() {
|
||||
console.log('MOUNTED', this, arguments)
|
||||
})
|
||||
|
||||
|
||||
return {
|
||||
metaData,
|
||||
...toRefs(state)
|
||||
}
|
||||
},
|
||||
/*mounted () {
|
||||
this.interval = setInterval(() => {
|
||||
this.date = new Date()
|
||||
}, 1000)
|
||||
},
|
||||
destroyed () {
|
||||
clearInterval(this.interval)
|
||||
}*/
|
||||
}
|
||||
})
|
||||
|
||||
// this wrapper function is not a requirement for vue-router,
|
||||
// just a demonstration that render-function style components also work.
|
||||
// See https://github.com/nuxt/vue-meta/issues/9 for more info.
|
||||
function view (page) {
|
||||
return {
|
||||
name: `section-${page}`,
|
||||
@@ -82,6 +51,86 @@ function view (page) {
|
||||
}
|
||||
}
|
||||
|
||||
const App = {
|
||||
setup () {
|
||||
const metainfo = useMeta({
|
||||
base: { href: '/vue-router', target: '_blank' },
|
||||
charset: 'utf8',
|
||||
title: 'My Title',
|
||||
description: 'The Description',
|
||||
og: {
|
||||
title: 'Og Title',
|
||||
description: 'Bla bla',
|
||||
image: [
|
||||
'https://picsum.photos/600/400/?image=80',
|
||||
'https://picsum.photos/600/400/?image=82'
|
||||
]
|
||||
},
|
||||
twitter: {
|
||||
title: 'Twitter Title'
|
||||
},
|
||||
noscript: [
|
||||
'<!-- // A code comment -->',
|
||||
{ tag: 'link', rel: 'stylesheet', href: 'style.css' }
|
||||
],
|
||||
otherNoscript: {
|
||||
tag: 'noscript',
|
||||
'data-test': 'hello',
|
||||
content: [
|
||||
'<!-- // Another code comment -->',
|
||||
{ tag: 'link', rel: 'stylesheet', href: 'style2.css' }
|
||||
]
|
||||
},
|
||||
body: 'body-script1.js',
|
||||
script: [
|
||||
'<!--[if IE]>',
|
||||
{ src: 'head-script1.js' },
|
||||
'<![endif]>',
|
||||
{ src: 'body-script2.js', target: 'body' },
|
||||
{ src: 'body-script3.js', target: '#put-it-here' }
|
||||
],
|
||||
esi: {
|
||||
content: [{
|
||||
tag: 'choose',
|
||||
content: [{
|
||||
tag: 'when',
|
||||
test: '$(HTTP_COOKIE{group})=="Advanced"',
|
||||
content: [{
|
||||
tag: 'include',
|
||||
src: 'http://www.example.com/advanced.html'
|
||||
}]
|
||||
}]
|
||||
}]
|
||||
}
|
||||
})
|
||||
|
||||
setTimeout(() => (metainfo.title = 'My Updated Title'), 2000)
|
||||
|
||||
return {
|
||||
metainfo
|
||||
}
|
||||
},
|
||||
template: `
|
||||
<metainfo :metainfo="metainfo">
|
||||
<template v-slot:base="{ content, metainfo }">http://nuxt.dev:3000{{ content }}</template>
|
||||
<template v-slot:title="{ content, metainfo }">{{ content }} - {{ metainfo.description }} - Hello</template>
|
||||
<template v-slot:og(title)="{ content, metainfo, og }">
|
||||
{{ content }} - {{ og.description }} - {{ metainfo.description }} - Hello Again
|
||||
</template>
|
||||
</metainfo>
|
||||
|
||||
<div id="app">
|
||||
<h1>vue-router</h1>
|
||||
<router-link to="/">Home</router-link>
|
||||
<!-- router-link to="/about">About</router-link -->
|
||||
<transition name="page" mode="out-in">
|
||||
<router-view></router-view>
|
||||
</transition>
|
||||
<p>Inspect Element to see the meta info</p>
|
||||
</div>
|
||||
`
|
||||
}
|
||||
|
||||
const router = createRouter({
|
||||
history: createWebHistory('/vue-router'),
|
||||
routes: [
|
||||
@@ -90,45 +139,27 @@ const router = createRouter({
|
||||
]
|
||||
})
|
||||
|
||||
const Metadata = {
|
||||
template: `
|
||||
<teleport to="head">
|
||||
<slot />
|
||||
</teleport>
|
||||
|
||||
<teleport to="body">
|
||||
<slot name="body" />
|
||||
</teleport>
|
||||
`
|
||||
}
|
||||
|
||||
const App = {
|
||||
template: `
|
||||
<metainfo>
|
||||
<title>My Title</title>
|
||||
<meta name="charset" content="utf8" />
|
||||
|
||||
<template v-slot:body>
|
||||
<script>var a = 1</script>
|
||||
</template>
|
||||
</metainfo>
|
||||
|
||||
<div id="app">
|
||||
<h1>vue-router</h1>
|
||||
<router-link to="/">Home</router-link>
|
||||
<router-link to="/about">About</router-link>
|
||||
<transition name="page" mode="out-in">
|
||||
<router-view></router-view>
|
||||
</transition>
|
||||
<p>Inspect Element to see the meta info</p>
|
||||
</div>
|
||||
`
|
||||
}
|
||||
const meta = createMeta({
|
||||
config: {
|
||||
esi: {
|
||||
group: true,
|
||||
namespaced: true,
|
||||
contentAttributes: [
|
||||
'src',
|
||||
'test',
|
||||
'text'
|
||||
]
|
||||
}
|
||||
}
|
||||
})
|
||||
|
||||
const app = createApp(App)
|
||||
app.component('metainfo', Metadata)
|
||||
app.component('metainfo', Metainfo)
|
||||
app.use(router)
|
||||
app.use(meta)
|
||||
app.mount('#app')
|
||||
|
||||
// old stuff:
|
||||
/*
|
||||
const { set, remove } = app.$meta().addApp('custom')
|
||||
|
||||
@@ -142,8 +173,6 @@ set({
|
||||
})
|
||||
setTimeout(() => remove(), 3000)
|
||||
*/
|
||||
app.mount('#app')
|
||||
|
||||
/*
|
||||
const waitFor = time => new Promise(r => setTimeout(r, time || 1000))
|
||||
const o = {
|
||||
|
||||
@@ -14,6 +14,7 @@
|
||||
<body>
|
||||
<a href="/">← Examples index</a>
|
||||
<div id="app"></div>
|
||||
<div id="put-it-here"></div>
|
||||
<script src="/__build__/vue-router.js"></script>
|
||||
</body>
|
||||
</html>
|
||||
|
||||
@@ -4,7 +4,7 @@ import webpack from 'webpack'
|
||||
import WebpackBar from 'webpackbar'
|
||||
import { VueLoaderPlugin } from 'vue-loader'
|
||||
|
||||
const srcDir = path.join(__dirname, '..', 'src')
|
||||
// const srcDir = path.join(__dirname, '..', 'src')
|
||||
|
||||
export default {
|
||||
devtool: 'inline-source-map',
|
||||
@@ -63,7 +63,7 @@ export default {
|
||||
// is a simple `export * from '@vue/runtime-dom`. However having this
|
||||
// extra re-export somehow causes webpack to always invalidate the module
|
||||
// on the first HMR update and causes the page to reload.
|
||||
'vue': 'vue/dist/vue.esm.js',
|
||||
vue: 'vue/dist/vue.esm-bundler.js',
|
||||
'vue-meta': path.resolve(__dirname, './next/')
|
||||
}
|
||||
},
|
||||
|
||||
+1
-1
@@ -49,7 +49,7 @@
|
||||
"dev": "cd examples && yarn dev && cd ..",
|
||||
"docs": "vuepress dev --host 0.0.0.0 --port 3000 docs",
|
||||
"docs:build": "vuepress build docs",
|
||||
"lint": "eslint src test",
|
||||
"lint": "eslint src test examples",
|
||||
"prerelease": "git checkout master && git pull -r",
|
||||
"release": "yarn lint && yarn test && standard-version",
|
||||
"test": "yarn test:unit && yarn test:e2e-ssr && yarn test:e2e-browser",
|
||||
|
||||
Reference in New Issue
Block a user