2
0
mirror of https://github.com/tenrok/vue-meta.git synced 2026-06-25 02:30:34 +03:00

feat: improve useApi

This commit is contained in:
pimlie
2020-05-24 23:42:37 +02:00
parent b0edfbe6bd
commit 303eae1603
3 changed files with 117 additions and 96 deletions
+1 -6
View File
@@ -10,17 +10,12 @@ export default {
} }
}, },
setup() { setup() {
const mapping = inject('__vueMetaConfig')
return {
mapping
}
}, },
render() { render() {
const targets = {} const targets = {}
for (const key in this.metainfo) { for (const key in this.metainfo) {
const config = this.mapping[key] || {} const config = this.$metaInfo.config[key] || {}
const vnodes = renderMeta(this, key, this.metainfo[key], config) const vnodes = renderMeta(this, key, this.metainfo[key], config)
let target = (key !== 'base' && this.metainfo[key].target) || config.target || 'head' let target = (key !== 'base' && this.metainfo[key].target) || config.target || 'head'
+91 -62
View File
@@ -1,9 +1,12 @@
import { markRaw, reactive, onMounted, customRef, getCurrentInstance } from 'vue' import { markRaw, reactive, onUnmounted, getCurrentInstance } from 'vue'
import { def, hasOwn, isObject, isArray, isPlainObject } from '@vue/shared' import { hasOwn, isObject, isArray, isPlainObject } from '@vue/shared'
import { defaultMapping } from './config' import { defaultMapping } from './config'
let appId = 0 let appId = 0
const shadow = markRaw({})
const metainfo = reactive({})
export function createMeta ({ resolver, config }) { export function createMeta ({ resolver, config }) {
const id = Symbol(`vueMeta[${appId++}]`) const id = Symbol(`vueMeta[${appId++}]`)
@@ -12,116 +15,142 @@ export function createMeta ({ resolver, config }) {
const $metaInfo = { const $metaInfo = {
id, id,
resolver, resolver,
shadow: markRaw({}), config: {
metainfo: reactive({}) ...defaultMapping,
...config
}
} }
app.config.globalProperties.$metaInfo = $metaInfo app.config.globalProperties.$metaInfo = $metaInfo
app.provide('metainfo', $metaInfo.metainfo) app.provide('metainfo', metainfo)
app.provide('__vueMetaConfig', {
id,
...defaultMapping,
...config
})
app.config.globalProperties.$meta = this
} }
} }
} }
export function useMeta (obj) { export function useMeta (obj, context) {
const vm = getCurrentInstance() // set empty object to remove everything
const unmount = () => setMetainfoByObject(context, {}, shadow, metainfo)
const { shadow, metainfo } = vm.ctx.$metaInfo if (!context) {
addMetainfoRecursive(obj, vm, shadow, metainfo) context = getCurrentInstance()
return createProxy(obj, createHandler(vm)) onUnmounted(unmount)
}
if (!context) {
context = {}
}
setMetainfoByObject(context, obj, shadow, metainfo)
const meta = createProxy(obj, createHandler(context))
return {
meta,
unmount
}
} }
function createProxy (obj, handler) { function createProxy (obj, handler) {
return new Proxy(obj, handler) return markRaw(new Proxy(obj, handler))
} }
function createHandler (vm, keyPath = []) { function createHandler (context, keyPath = []) {
return { return {
get (target, prop) { get (target, prop) {
const value = target[prop] const value = target[prop]
if (isObject(value)) { if (!isObject(value)) {
if (!value.__vm_proxy) { return value
const newKeyPath = [...keyPath, prop]
const handler = createHandler(vm, newKeyPath)
value.__vm_proxy = createProxy(value, handler)
}
return value.__vm_proxy
} }
return value if (!value.__vm_proxy) {
const newKeyPath = [...keyPath, prop]
const handler = createHandler(context, newKeyPath)
value.__vm_proxy = createProxy(value, handler)
}
return value.__vm_proxy
}, },
set (target, prop, value) { set (target, prop, value) {
updateMetainfo(keyPath, vm, prop, value) updateMetainfo(keyPath, context, prop, value)
return true return true
} }
} }
} }
function addMetainfoRecursive (obj, vm, shadowParent, liveParent) { function setMetainfo (context, key, value, shadowParent, liveParent, keyTree = []) {
for (const key in obj) { if (isPlainObject(value)) {
if (isPlainObject(obj[key])) { if (!shadowParent[key]) {
if (!shadowParent[key]) { shadowParent[key] = {}
shadowParent[key] = {} liveParent[key] = {}
liveParent[key] = {} }
}
addMetainfoRecursive(obj[key], vm, shadowParent[key], liveParent[key]) return setMetainfoByObject(context, value, shadowParent[key], liveParent[key], keyTree)
}
let idx = -1
if (!hasOwn(shadowParent, key)) {
shadowParent[key] = []
} else {
idx = shadowParent[key].findIndex(({ context: $context }) => $context === context)
}
if (idx > -1 && value === undefined) {
shadowParent[key].splice(idx, 1)
} else if (idx > -1) {
shadowParent[key][idx].value = value
} else if (value) {
shadowParent[key].push({ context, value })
}
resolveActiveMetainfo(context, key, keyTree, shadowParent, liveParent)
}
function setMetainfoByObject (context, obj, shadowParent, liveParent, keyTree = []) {
for (const key in shadowParent) {
if (hasOwn(obj, key)) {
continue continue
} }
if (!shadowParent[key]) { if (isPlainObject(shadowParent[key])) {
shadowParent[key] = [] setMetainfoByObject(context, {}, shadowParent[key], liveParent[key], [...keyTree, key])
continue
} }
shadowParent[key].push({ vm, value: obj[key] }) setMetainfo(context, key, undefined, shadowParent, liveParent, [...keyTree, key])
}
setLive(vm, key, shadowParent, liveParent) for (const key in obj) {
setMetainfo(context, key, obj[key], shadowParent, liveParent, [...keyTree, key])
} }
} }
function updateMetainfo (keyPath, vm, key, value) { function updateMetainfo (keyPath, context, key, value) {
let { shadow: shadowParent, metainfo: liveParent } = vm.ctx.$metaInfo let shadowParent = shadow
let liveParent = metainfo
for (const _key of keyPath) { for (const _key of keyPath) {
shadowParent = shadowParent[_key] shadowParent = shadowParent[_key]
liveParent = liveParent[_key] liveParent = liveParent[_key]
} }
if (isPlainObject(value)) { setMetainfo(context, key, value, shadowParent, liveParent)
if (!shadowParent[key]) {
shadowParent[key] = {}
liveParent[key] = {}
}
// TODO: fix this shit
addMetainfoRecursive(value, vm, shadowParent[key], liveParent[key])
return
}
const idx = shadowParent[key].findIndex(({ vm: _vm }) => _vm === vm)
shadowParent[key][idx].value = value
setLive(vm, key, shadowParent, liveParent)
} }
function setLive (vm, key, shadowParent, liveParent) { function resolveActiveMetainfo (context, key, keyTree, shadowParent, liveParent) {
let value let value
if (shadowParent[key].length > 1) { if (shadowParent[key].length > 1) {
value = vm.ctx.$metaInfo.resolver(shadowParent[key]) value = context.ctx.$metaInfo.resolver(key, shadowParent[key], liveParent[key])
} else { } else if (shadowParent[key].length) {
value = shadowParent[key][0].value value = shadowParent[key][0].value
} }
if (!hasOwn(liveParent, key) || liveParent[key] !== value) { if (value === undefined) {
delete liveParent[key]
} else if (!hasOwn(liveParent, key) || liveParent[key] !== value) {
liveParent[key] = value liveParent[key] = value
} }
} }
+25 -28
View File
@@ -1,8 +1,8 @@
import { createApp, defineComponent, reactive, inject, markRaw, toRefs, h, customRef, watch, watchEffect } from 'vue' import { createApp, defineComponent, reactive, inject, toRefs, h, watch } from 'vue'
import { createRouter, createWebHistory } from 'vue-router' import { createRouter, createWebHistory } from 'vue-router'
import Metainfo from '../next/Metainfo.vue' import Metainfo from '../next/Metainfo.vue'
import { createMeta, useMeta } from '../next' import { createMeta, useMeta } from '../next'
import About from './about.vue' // import About from './about.vue'
const metaUpdated = 'no' const metaUpdated = 'no'
@@ -16,25 +16,24 @@ const ChildComponent = defineComponent({
<h3>You're looking at the <strong>{{ page }}</strong> page</h3> <h3>You're looking at the <strong>{{ page }}</strong> page</h3>
<p>Has metaInfo been updated due to navigation? {{ metaUpdated }}</p> <p>Has metaInfo been updated due to navigation? {{ metaUpdated }}</p>
</div>`, </div>`,
created () { setup (props) {
// console.log(this)
},
setup () {
const state = reactive({ const state = reactive({
date: null, date: null,
metaUpdated metaUpdated
}) })
const metainfo = useMeta({ const title = props.page[0].toUpperCase() + props.page.slice(1)
useMeta({
charset: 'utf16', charset: 'utf16',
description: 'Description 2', title,
description: 'Description ' + props.page,
og: { og: {
title: 'Og Title 2' title: 'Og Title ' + props.page
} }
}) })
return { return {
metainfo,
...toRefs(state) ...toRefs(state)
} }
} }
@@ -51,7 +50,7 @@ function view (page) {
const App = { const App = {
setup () { setup () {
/* const metainfo = useMeta({ const { meta } = useMeta({
base: { href: '/vue-router', target: '_blank' }, base: { href: '/vue-router', target: '_blank' },
charset: 'utf8', charset: 'utf8',
title: 'My Title', title: 'My Title',
@@ -83,7 +82,7 @@ const App = {
script: [ script: [
'<!--[if IE]>', '<!--[if IE]>',
{ src: 'head-script1.js' }, { src: 'head-script1.js' },
'<![endif]>', '<![endif]-->',
{ src: 'body-script2.js', target: 'body' }, { src: 'body-script2.js', target: 'body' },
{ src: 'body-script3.js', target: '#put-it-here' } { src: 'body-script3.js', target: '#put-it-here' }
], ],
@@ -102,21 +101,12 @@ const App = {
} }
}) })
setTimeout(() => (metainfo.title = 'My Updated Title'), 2000) */ setTimeout(() => (meta.title = 'My Updated Title'), 2000)
const meta = useMeta({
charset: 'utf8',
title: 'Title 1',
og: {
title: 'Og Title 1'
}
})
setTimeout(() => (meta.charset = 'utf17'), 2000)
setTimeout(() => (meta.og = { title: 'Updated Og Title 1' }), 3000) // TODO: fix
const metainfo = inject('metainfo') const metainfo = inject('metainfo')
window.$metainfo = metainfo
watch(metainfo, (newValue, oldValue) => { watch(metainfo, (newValue, oldValue) => {
console.log('UPDATE', newValue) console.log('UPDATE', newValue)
}) })
@@ -137,7 +127,7 @@ const App = {
<div id="app"> <div id="app">
<h1>vue-router</h1> <h1>vue-router</h1>
<router-link to="/">Home</router-link> <router-link to="/">Home</router-link>
<!-- router-link to="/about">About</router-link --> <router-link to="/about">About</router-link>
<transition name="page" mode="out-in"> <transition name="page" mode="out-in">
<router-view></router-view> <router-view></router-view>
</transition> </transition>
@@ -146,15 +136,16 @@ const App = {
` `
} }
function decisionMaker5000000 (options) { function decisionMaker5000000 (key, options, currentValue) {
let theChosenOne let theChosenOne
for (const option of options) { for (const option of options) {
if (!theChosenOne || theChosenOne.vm.id < option.vm.id) { if (!theChosenOne || theChosenOne.context.uid < option.context.uid) {
theChosenOne = option theChosenOne = option
} }
} }
console.log(key, currentValue, options.map(({ value }) => value))
console.log(theChosenOne.value) console.log(theChosenOne.value)
return theChosenOne.value return theChosenOne.value
} }
@@ -178,10 +169,16 @@ const router = createRouter({
history: createWebHistory('/vue-router'), history: createWebHistory('/vue-router'),
routes: [ routes: [
{ name: 'home', path: '/', component: view('home') }, { name: 'home', path: '/', component: view('home') },
{ name: 'about', path: '/about', component: About } { name: 'about', path: '/about', component: view('about') }
] ]
}) })
useMeta({
og: {
something: 'test'
}
})
const app = createApp(App) const app = createApp(App)
app.component('metainfo', Metainfo) app.component('metainfo', Metainfo)
app.use(router) app.use(router)