diff --git a/examples/next/Metainfo.vue b/examples/next/Metainfo.vue index 38ddfe9..fde5855 100644 --- a/examples/next/Metainfo.vue +++ b/examples/next/Metainfo.vue @@ -48,7 +48,7 @@ export default { targets[target].push(vnodes) continue } -console.log('TARGETS', targets) +// console.log('TARGETS', targets) return Object.keys(targets).map(target => { return h(Teleport, { to: target }, targets[target]) }) diff --git a/examples/next/index.js b/examples/next/index.js index 1eafa88..dc55f6f 100644 --- a/examples/next/index.js +++ b/examples/next/index.js @@ -1,68 +1,127 @@ -import { markRaw, reactive, onMounted } from 'vue' +import { markRaw, reactive, onMounted, customRef, getCurrentInstance } from 'vue' +import { def, hasOwn, isObject, isArray, isPlainObject } from '@vue/shared' import { defaultMapping } from './config' -const apps = {} -let appId = 1 +let appId = 0 -export function createMeta ({ config }) { - const id = Symbol(`vue-meta-${appId++}`) - - const Meta = { - id, +export function createMeta ({ resolver, config }) { + const id = Symbol(`vueMeta[${appId++}]`) + return { install (app) { - let watchersAdded = false + const $metaInfo = { + id, + resolver, + shadow: markRaw({}), + metainfo: reactive({}) + } + app.config.globalProperties.$metaInfo = $metaInfo + app.provide('metainfo', $metaInfo.metainfo) app.provide('__vueMetaConfig', { + id, ...defaultMapping, ...config }) - app.mixin({ - created () { - if (this === this.$root) { - watchersAdded = true - } - - if (!this.metaData || watchersAdded) { - return - } - - let depth = 0 - let parent = this - while (parent) { - parent = parent.$parent - depth++ - - if (parent === this.$root) { - break - } - } - - this.__meta = markRaw({ - depth - }) - console.log('CREATED', this, this.metaData, depth) - } - }) - app.config.globalProperties.$meta = this } } - - apps[id] = Meta - - return Meta } -export function useMeta (rawMetainfo) { - onMounted(vmMounted) +export function useMeta (obj) { + const vm = getCurrentInstance() - const metainfo = reactive(rawMetainfo) + const { shadow, metainfo } = vm.ctx.$metaInfo + addMetainfoRecursive(obj, vm, shadow, metainfo) - return metainfo + return createProxy(obj, createHandler(vm)) } -function vmMounted () { - console.log('MOUNTED', this, arguments) +function createProxy (obj, handler) { + return new Proxy(obj, handler) +} + +function createHandler (vm, keyPath = []) { + return { + get (target, prop) { + const value = target[prop] + + if (isObject(value)) { + if (!value.__vm_proxy) { + const newKeyPath = [...keyPath, prop] + + const handler = createHandler(vm, newKeyPath) + value.__vm_proxy = createProxy(value, handler) + } + + return value.__vm_proxy + } + + return value + }, + set (target, prop, value) { + updateMetainfo(keyPath, vm, prop, value) + return true + } + } +} + +function addMetainfoRecursive (obj, vm, shadowParent, liveParent) { + for (const key in obj) { + if (isPlainObject(obj[key])) { + if (!shadowParent[key]) { + shadowParent[key] = {} + liveParent[key] = {} + } + + addMetainfoRecursive(obj[key], vm, shadowParent[key], liveParent[key]) + continue + } + + if (!shadowParent[key]) { + shadowParent[key] = [] + } + + shadowParent[key].push({ vm, value: obj[key] }) + + setLive(vm, key, shadowParent, liveParent) + } +} + +function updateMetainfo (keyPath, vm, key, value) { + let { shadow: shadowParent, metainfo: liveParent } = vm.ctx.$metaInfo + + for (const _key of keyPath) { + shadowParent = shadowParent[_key] + liveParent = liveParent[_key] + } + + if (isPlainObject(value)) { + 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) { + let value + if (shadowParent[key].length > 1) { + value = vm.ctx.$metaInfo.resolver(shadowParent[key]) + } else { + value = shadowParent[key][0].value + } + + if (!hasOwn(liveParent, key) || liveParent[key] !== value) { + liveParent[key] = value + } } diff --git a/examples/next/render.js b/examples/next/render.js index c87591a..9e87b06 100644 --- a/examples/next/render.js +++ b/examples/next/render.js @@ -2,7 +2,7 @@ import { h } from 'vue' import { getConfigKey } from './config' export function renderMeta (ctx, key, data, config) { - console.info('renderMeta', key, data, config) + // console.info('renderMeta', key, data, config) if (config.group) { return renderGroup(ctx, key, data, config) @@ -12,7 +12,7 @@ export function renderMeta (ctx, key, data, config) { } export function renderGroup (ctx, key, data, config) { - console.info('renderGroup', key, data, config) + // console.info('renderGroup', key, data, config) if (Array.isArray(data)) { config.contentAttributes = getConfigKey([key, config.tag], 'contentAttributes', config) @@ -107,10 +107,10 @@ export function renderTag (ctx, key, data, config = {}, groupConfig = {}) { ? `${groupConfig.tagNamespace}:${tag}` : tag - console.info('FINAL TAG', finalTag) - console.log(' ATTRIBUTES', attributes) - console.log(' CONTENT', content) - // console.log(data, attributes, config) + // 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) { diff --git a/examples/package.json b/examples/package.json index 156d8ac..6e0c91a 100644 --- a/examples/package.json +++ b/examples/package.json @@ -20,15 +20,15 @@ }, "homepage": "https://github.com/nuxt/vue-meta#readme", "devDependencies": { - "@babel/core": "^7.9.0", + "@babel/core": "^7.9.6", "@babel/node": "^7.8.7", "@babel/plugin-syntax-dynamic-import": "^7.8.3", - "@babel/preset-env": "^7.9.5", + "@babel/preset-env": "^7.9.6", "@vue/compiler-sfc": "^3.0.0-alpha.10", "@vue/server-renderer": "^3.0.0-alpha.10", "babel-loader": "^8.1.0", - "babel-plugin-dynamic-import-node": "^2.3.0", - "consola": "^2.11.3", + "babel-plugin-dynamic-import-node": "^2.3.3", + "consola": "^2.12.1", "core-js": "3", "cross-env": "^7.0.2", "express": "^4.17.1", @@ -40,9 +40,9 @@ "vue-meta": "^2.3.3", "vue-router": "next", "vue-template-compiler": "^2.6.11", - "vuex": "^3.1.3", - "webpack": "^4.42.1", - "webpack-dev-server": "^3.10.3", + "vuex": "^3.4.0", + "webpack": "^4.43.0", + "webpack-dev-server": "^3.11.0", "webpackbar": "^4.0.0" } } diff --git a/examples/vue-router/app.js b/examples/vue-router/app.js index 76d3155..8931311 100644 --- a/examples/vue-router/app.js +++ b/examples/vue-router/app.js @@ -1,10 +1,10 @@ -import { createApp, defineComponent, reactive, toRefs, h } from 'vue' +import { createApp, defineComponent, reactive, inject, markRaw, toRefs, h, customRef, watch, watchEffect } from 'vue' import { createRouter, createWebHistory } from 'vue-router' import Metainfo from '../next/Metainfo.vue' import { createMeta, useMeta } from '../next' import About from './about.vue' -let metaUpdated = 'no' +const metaUpdated = 'no' const ChildComponent = defineComponent({ name: 'child-component', @@ -16,19 +16,8 @@ const ChildComponent = defineComponent({

You're looking at the {{ page }} page

Has metaInfo been updated due to navigation? {{ metaUpdated }}

`, - metaInfo () { - return { - title: `${this.page} - ${this.date && this.date.toTimeString()}`, - bodyAttrs: { - class: 'child-component' - }, - afterNavigation () { - metaUpdated = 'yes' - } - } - }, created () { - //console.log(this) + // console.log(this) }, setup () { const state = reactive({ @@ -36,7 +25,16 @@ const ChildComponent = defineComponent({ metaUpdated }) + const metainfo = useMeta({ + charset: 'utf16', + description: 'Description 2', + og: { + title: 'Og Title 2' + } + }) + return { + metainfo, ...toRefs(state) } } @@ -53,7 +51,7 @@ function view (page) { const App = { setup () { - const metainfo = useMeta({ + /* const metainfo = useMeta({ base: { href: '/vue-router', target: '_blank' }, charset: 'utf8', title: 'My Title', @@ -104,7 +102,24 @@ const App = { } }) - setTimeout(() => (metainfo.title = 'My Updated Title'), 2000) + setTimeout(() => (metainfo.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') + + watch(metainfo, (newValue, oldValue) => { + console.log('UPDATE', newValue) + }) return { metainfo @@ -131,15 +146,21 @@ const App = { ` } -const router = createRouter({ - history: createWebHistory('/vue-router'), - routes: [ - { name: 'home', path: '/', component: view('home') }, - { name: 'about', path: '/about', component: About } - ] -}) +function decisionMaker5000000 (options) { + let theChosenOne + + for (const option of options) { + if (!theChosenOne || theChosenOne.vm.id < option.vm.id) { + theChosenOne = option + } + } + + console.log(theChosenOne.value) + return theChosenOne.value +} const meta = createMeta({ + resolver: decisionMaker5000000, config: { esi: { group: true, @@ -153,6 +174,14 @@ const meta = createMeta({ } }) +const router = createRouter({ + history: createWebHistory('/vue-router'), + routes: [ + { name: 'home', path: '/', component: view('home') }, + { name: 'about', path: '/about', component: About } + ] +}) + const app = createApp(App) app.component('metainfo', Metainfo) app.use(router)