mirror of
https://github.com/tenrok/vue-meta.git
synced 2026-06-20 09:50:33 +03:00
feat: convert to ts (wip)
This commit is contained in:
@@ -1,14 +0,0 @@
|
||||
{
|
||||
"presets": [
|
||||
["@babel/preset-env", {
|
||||
useBuiltIns: 'usage',
|
||||
corejs: 3,
|
||||
targets: {
|
||||
ie: 9
|
||||
}
|
||||
}]
|
||||
],
|
||||
"plugins": [
|
||||
"dynamic-import-node"
|
||||
]
|
||||
}
|
||||
@@ -10,14 +10,14 @@ window.users.push({
|
||||
zipcode: '92998-3874',
|
||||
geo: {
|
||||
lat: '-37.3159',
|
||||
lng: '81.1496'
|
||||
}
|
||||
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'
|
||||
}
|
||||
bs: 'harness real-time e-markets',
|
||||
},
|
||||
})
|
||||
|
||||
@@ -10,14 +10,14 @@ window.users.push({
|
||||
zipcode: '90566-7771',
|
||||
geo: {
|
||||
lat: '-43.9509',
|
||||
lng: '-34.4618'
|
||||
}
|
||||
lng: '-34.4618',
|
||||
},
|
||||
},
|
||||
phone: '010-692-6593 x09125',
|
||||
website: 'anastasia.net',
|
||||
company: {
|
||||
name: 'Deckow-Crist',
|
||||
catchPhrase: 'Proactive didactic contingency',
|
||||
bs: 'synergize scalable supply-chains'
|
||||
}
|
||||
bs: 'synergize scalable supply-chains',
|
||||
},
|
||||
})
|
||||
|
||||
@@ -10,14 +10,14 @@ window.users.push({
|
||||
zipcode: '59590-4157',
|
||||
geo: {
|
||||
lat: '-68.6102',
|
||||
lng: '-47.0653'
|
||||
}
|
||||
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'
|
||||
}
|
||||
bs: 'e-enable strategic applications',
|
||||
},
|
||||
})
|
||||
|
||||
@@ -10,14 +10,14 @@ window.users.push({
|
||||
zipcode: '53919-4257',
|
||||
geo: {
|
||||
lat: '29.4572',
|
||||
lng: '-164.2990'
|
||||
}
|
||||
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'
|
||||
}
|
||||
bs: 'transition cutting-edge web services',
|
||||
},
|
||||
})
|
||||
|
||||
@@ -6,7 +6,7 @@ Vue.use(VueMeta)
|
||||
window.users = []
|
||||
|
||||
new Vue({
|
||||
metaInfo () {
|
||||
metaInfo() {
|
||||
return {
|
||||
title: 'Async Callback',
|
||||
titleTemplate: '%s | Vue Meta Examples',
|
||||
@@ -16,56 +16,56 @@ new Vue({
|
||||
vmid: 'potatoes',
|
||||
src: '/user-3.js',
|
||||
async: true,
|
||||
callback: this.updateCounter
|
||||
callback: this.updateCounter,
|
||||
},
|
||||
{
|
||||
skip: this.count < 1,
|
||||
vmid: 'vegetables',
|
||||
src: '/user-2.js',
|
||||
async: true,
|
||||
callback: this.updateCounter
|
||||
callback: this.updateCounter,
|
||||
},
|
||||
{
|
||||
vmid: 'meat',
|
||||
src: '/user-1.js',
|
||||
async: true,
|
||||
callback: el => this.loadCallback(el.getAttribute('data-vmid'))
|
||||
callback: el => this.loadCallback(el.getAttribute('data-vmid')),
|
||||
},
|
||||
...this.scripts
|
||||
]
|
||||
...this.scripts,
|
||||
],
|
||||
}
|
||||
},
|
||||
data () {
|
||||
data() {
|
||||
return {
|
||||
count: 0,
|
||||
scripts: [],
|
||||
users: window.users
|
||||
users: window.users,
|
||||
}
|
||||
},
|
||||
watch: {
|
||||
count (val) {
|
||||
count(val) {
|
||||
if (val === 3) {
|
||||
this.addScript()
|
||||
}
|
||||
}
|
||||
},
|
||||
},
|
||||
methods: {
|
||||
updateCounter () {
|
||||
updateCounter() {
|
||||
this.count++
|
||||
},
|
||||
addScript () {
|
||||
addScript() {
|
||||
this.scripts.push({
|
||||
src: '/user-4.js',
|
||||
callback: () => {
|
||||
this.updateCounter()
|
||||
}
|
||||
},
|
||||
})
|
||||
},
|
||||
loadCallback (vmid) {
|
||||
loadCallback(vmid) {
|
||||
if (vmid === 'meat') {
|
||||
this.updateCounter()
|
||||
}
|
||||
}
|
||||
},
|
||||
},
|
||||
template: `
|
||||
<div id="app">
|
||||
@@ -84,5 +84,5 @@ new Vue({
|
||||
</ul>
|
||||
</div>
|
||||
</div>
|
||||
`
|
||||
`,
|
||||
}).$mount('#app')
|
||||
|
||||
@@ -8,17 +8,17 @@ Vue.component('child', {
|
||||
props: {
|
||||
page: {
|
||||
type: String,
|
||||
default: ''
|
||||
}
|
||||
default: '',
|
||||
},
|
||||
},
|
||||
render (h) {
|
||||
render(h) {
|
||||
return h('h3', null, this.page)
|
||||
},
|
||||
metaInfo () {
|
||||
metaInfo() {
|
||||
return {
|
||||
title: this.page
|
||||
title: this.page,
|
||||
}
|
||||
}
|
||||
},
|
||||
})
|
||||
|
||||
new Vue({
|
||||
@@ -28,5 +28,5 @@ new Vue({
|
||||
<p>Inspect Element to see the meta info</p>
|
||||
<child page="This is a prop"></child>
|
||||
</div>
|
||||
`
|
||||
`,
|
||||
}).$mount('#app')
|
||||
|
||||
+15
-9
@@ -15,18 +15,24 @@ new Vue({
|
||||
titleTemplate: '%s | Vue Meta Examples',
|
||||
htmlAttrs: {
|
||||
lang: 'en',
|
||||
amp: undefined
|
||||
amp: undefined,
|
||||
},
|
||||
headAttrs: {
|
||||
test: true
|
||||
test: true,
|
||||
},
|
||||
meta: [
|
||||
{ name: 'description', content: 'Hello', vmid: 'test' }
|
||||
],
|
||||
meta: [{ name: 'description', content: 'Hello', vmid: 'test' }],
|
||||
script: [
|
||||
{ innerHTML: '{ "@context": "http://www.schema.org", "@type": "Organization" }', type: 'application/ld+json' },
|
||||
{ innerHTML: '{ "body": "yes" }', body: true, type: 'application/ld+json' }
|
||||
{
|
||||
innerHTML:
|
||||
'{ "@context": "http://www.schema.org", "@type": "Organization" }',
|
||||
type: 'application/ld+json',
|
||||
},
|
||||
{
|
||||
innerHTML: '{ "body": "yes" }',
|
||||
body: true,
|
||||
type: 'application/ld+json',
|
||||
},
|
||||
],
|
||||
__dangerouslyDisableSanitizers: ['script']
|
||||
})
|
||||
__dangerouslyDisableSanitizers: ['script'],
|
||||
}),
|
||||
}).$mount('#app')
|
||||
|
||||
@@ -6,18 +6,18 @@ Vue.use(VueMeta)
|
||||
Vue.component('foo', {
|
||||
template: '<p>Foo component</p>',
|
||||
metaInfo: {
|
||||
title: 'Keep me Foo'
|
||||
}
|
||||
title: 'Keep me Foo',
|
||||
},
|
||||
})
|
||||
|
||||
new Vue({
|
||||
data () {
|
||||
data() {
|
||||
return { showFoo: false }
|
||||
},
|
||||
methods: {
|
||||
show () {
|
||||
show() {
|
||||
this.showFoo = !this.showFoo
|
||||
}
|
||||
},
|
||||
},
|
||||
template: `
|
||||
<div id="app">
|
||||
@@ -29,6 +29,6 @@ new Vue({
|
||||
</div>
|
||||
`,
|
||||
metaInfo: () => ({
|
||||
title: 'Keep-alive'
|
||||
})
|
||||
title: 'Keep-alive',
|
||||
}),
|
||||
}).$mount('#app')
|
||||
|
||||
+11
-10
@@ -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 () => {
|
||||
@@ -35,23 +35,22 @@ module.exports = function (source, map) {
|
||||
// console.log('AST', ast)
|
||||
|
||||
const [nodeTransforms, directiveTransforms] = getBaseTransformPreset({
|
||||
prefixIdentifiers: true
|
||||
prefixIdentifiers: true,
|
||||
})
|
||||
|
||||
transform(ast, {
|
||||
prefixIdentifiers: true,
|
||||
nodeTransforms: [
|
||||
...nodeTransforms,
|
||||
headTransform
|
||||
],
|
||||
directiveTransforms
|
||||
nodeTransforms: [...nodeTransforms, headTransform],
|
||||
directiveTransforms,
|
||||
})
|
||||
|
||||
const result = generate(ast, { mode: 'module' })
|
||||
|
||||
console.log(result.code)
|
||||
|
||||
this.callback(null, `
|
||||
this.callback(
|
||||
null,
|
||||
`
|
||||
import { computed } from 'vue'
|
||||
|
||||
${result.code}
|
||||
@@ -70,6 +69,8 @@ export default function (component) {
|
||||
}
|
||||
}
|
||||
|
||||
}`, map)
|
||||
}`,
|
||||
map
|
||||
)
|
||||
/**/
|
||||
}
|
||||
|
||||
@@ -6,50 +6,50 @@ Vue.use(VueMeta)
|
||||
// index.html contains a manual SSR render
|
||||
|
||||
const app1 = new Vue({
|
||||
metaInfo () {
|
||||
metaInfo() {
|
||||
return {
|
||||
title: 'App 1 title',
|
||||
bodyAttrs: {
|
||||
class: 'app-1'
|
||||
class: 'app-1',
|
||||
},
|
||||
meta: [
|
||||
{ name: 'description', content: 'Hello from app 1', vmid: 'test' },
|
||||
{ name: 'og:description', content: this.ogContent }
|
||||
{ name: 'og:description', content: this.ogContent },
|
||||
],
|
||||
script: [
|
||||
{ innerHTML: 'var appId=1.1', body: true },
|
||||
{ innerHTML: 'var appId=1.2', vmid: 'app-id-body' }
|
||||
]
|
||||
{ innerHTML: 'var appId=1.2', vmid: 'app-id-body' },
|
||||
],
|
||||
}
|
||||
},
|
||||
data () {
|
||||
data() {
|
||||
return {
|
||||
ogContent: 'Hello from ssr app'
|
||||
ogContent: 'Hello from ssr app',
|
||||
}
|
||||
},
|
||||
template: `
|
||||
<div id="app1"><h1>App 1</h1></div>
|
||||
`
|
||||
`,
|
||||
})
|
||||
|
||||
const app2 = new Vue({
|
||||
metaInfo: () => ({
|
||||
title: 'App 2 title',
|
||||
bodyAttrs: {
|
||||
class: 'app-2'
|
||||
class: 'app-2',
|
||||
},
|
||||
meta: [
|
||||
{ name: 'description', content: 'Hello from app 2', vmid: 'test' },
|
||||
{ name: 'og:description', content: 'Hello from app 2' }
|
||||
{ name: 'og:description', content: 'Hello from app 2' },
|
||||
],
|
||||
script: [
|
||||
{ innerHTML: 'var appId=2.1', body: true },
|
||||
{ innerHTML: 'var appId=2.2', vmid: 'app-id-body', body: true }
|
||||
]
|
||||
{ innerHTML: 'var appId=2.2', vmid: 'app-id-body', body: true },
|
||||
],
|
||||
}),
|
||||
template: `
|
||||
<div id="app2"><h1>App 2</h1></div>
|
||||
`
|
||||
`,
|
||||
}).$mount('#app2')
|
||||
|
||||
app1.$mount('#app1')
|
||||
@@ -57,7 +57,7 @@ app1.$mount('#app1')
|
||||
const app3 = new Vue({
|
||||
template: `
|
||||
<div id="app3"><h1>App 3 (empty metaInfo)</h1></div>
|
||||
`
|
||||
`,
|
||||
}).$mount('#app3')
|
||||
|
||||
setTimeout(() => {
|
||||
|
||||
@@ -1,52 +0,0 @@
|
||||
<script>
|
||||
import { h, ref, computed, inject, Teleport } from 'vue'
|
||||
import { renderMeta } from './render'
|
||||
|
||||
export default {
|
||||
props: {
|
||||
metainfo: {
|
||||
type: Object,
|
||||
required: true
|
||||
}
|
||||
},
|
||||
setup() {
|
||||
},
|
||||
render() {
|
||||
const targets = {}
|
||||
|
||||
for (const key in this.metainfo) {
|
||||
const config = this.$metaInfo.config[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>
|
||||
@@ -1,119 +0,0 @@
|
||||
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
|
||||
}
|
||||
@@ -1,156 +0,0 @@
|
||||
import { markRaw, reactive, onUnmounted, getCurrentInstance } from 'vue'
|
||||
import { hasOwn, isObject, isArray, isPlainObject } from '@vue/shared'
|
||||
import { defaultMapping } from './config'
|
||||
|
||||
let appId = 0
|
||||
|
||||
const shadow = markRaw({})
|
||||
const metainfo = reactive({})
|
||||
|
||||
export function createMeta ({ resolver, config }) {
|
||||
const id = Symbol(`vueMeta[${appId++}]`)
|
||||
|
||||
return {
|
||||
install (app) {
|
||||
const $metaInfo = {
|
||||
id,
|
||||
resolver,
|
||||
config: {
|
||||
...defaultMapping,
|
||||
...config
|
||||
}
|
||||
}
|
||||
|
||||
app.config.globalProperties.$metaInfo = $metaInfo
|
||||
app.provide('metainfo', metainfo)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
export function useMeta (obj, context) {
|
||||
// set empty object to remove everything
|
||||
const unmount = () => setMetainfoByObject(context, {}, shadow, metainfo)
|
||||
|
||||
if (!context) {
|
||||
context = getCurrentInstance()
|
||||
|
||||
onUnmounted(unmount)
|
||||
}
|
||||
|
||||
if (!context) {
|
||||
context = {}
|
||||
}
|
||||
|
||||
setMetainfoByObject(context, obj, shadow, metainfo)
|
||||
|
||||
const meta = createProxy(obj, createHandler(context))
|
||||
|
||||
return {
|
||||
meta,
|
||||
unmount
|
||||
}
|
||||
}
|
||||
|
||||
function createProxy (obj, handler) {
|
||||
return markRaw(new Proxy(obj, handler))
|
||||
}
|
||||
|
||||
function createHandler (context, keyPath = []) {
|
||||
return {
|
||||
get (target, prop) {
|
||||
const value = target[prop]
|
||||
|
||||
if (!isObject(value)) {
|
||||
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) {
|
||||
updateMetainfo(keyPath, context, prop, value)
|
||||
return true
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
function setMetainfo (context, key, value, shadowParent, liveParent, keyTree = []) {
|
||||
if (isPlainObject(value)) {
|
||||
if (!shadowParent[key]) {
|
||||
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
|
||||
}
|
||||
|
||||
if (isPlainObject(shadowParent[key])) {
|
||||
setMetainfoByObject(context, {}, shadowParent[key], liveParent[key], [...keyTree, key])
|
||||
continue
|
||||
}
|
||||
|
||||
setMetainfo(context, key, undefined, shadowParent, liveParent, [...keyTree, key])
|
||||
}
|
||||
|
||||
for (const key in obj) {
|
||||
setMetainfo(context, key, obj[key], shadowParent, liveParent, [...keyTree, key])
|
||||
}
|
||||
}
|
||||
|
||||
function updateMetainfo (keyPath, context, key, value) {
|
||||
let shadowParent = shadow
|
||||
let liveParent = metainfo
|
||||
|
||||
for (const _key of keyPath) {
|
||||
shadowParent = shadowParent[_key]
|
||||
liveParent = liveParent[_key]
|
||||
}
|
||||
|
||||
setMetainfo(context, key, value, shadowParent, liveParent)
|
||||
}
|
||||
|
||||
function resolveActiveMetainfo (context, key, keyTree, shadowParent, liveParent) {
|
||||
let value
|
||||
|
||||
if (shadowParent[key].length > 1) {
|
||||
value = context.ctx.$metaInfo.resolver(key, shadowParent[key], liveParent[key])
|
||||
} else if (shadowParent[key].length) {
|
||||
value = shadowParent[key][0].value
|
||||
}
|
||||
|
||||
if (value === undefined) {
|
||||
delete liveParent[key]
|
||||
} else if (!hasOwn(liveParent, key) || liveParent[key] !== value) {
|
||||
liveParent[key] = value
|
||||
}
|
||||
}
|
||||
@@ -1,154 +0,0 @@
|
||||
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
|
||||
}
|
||||
@@ -1,48 +0,0 @@
|
||||
{
|
||||
"name": "vue-meta-examples",
|
||||
"version": "1.0.0",
|
||||
"description": "Examples for vue-meta",
|
||||
"main": "server.js",
|
||||
"private": true,
|
||||
"scripts": {
|
||||
"dev": "cross-env NODE_ENV=development babel-node server.js",
|
||||
"start": "babel-node server.js",
|
||||
"ssr": "cross-env NODE_ENV=development babel-node ssr"
|
||||
},
|
||||
"repository": {
|
||||
"type": "git",
|
||||
"url": "git+https://github.com/nuxt/vue-meta.git"
|
||||
},
|
||||
"author": "",
|
||||
"license": "MIT",
|
||||
"bugs": {
|
||||
"url": "https://github.com/nuxt/vue-meta/issues"
|
||||
},
|
||||
"homepage": "https://github.com/nuxt/vue-meta#readme",
|
||||
"devDependencies": {
|
||||
"@babel/core": "^7.9.6",
|
||||
"@babel/node": "^7.8.7",
|
||||
"@babel/plugin-syntax-dynamic-import": "^7.8.3",
|
||||
"@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.3",
|
||||
"consola": "^2.12.1",
|
||||
"core-js": "3",
|
||||
"cross-env": "^7.0.2",
|
||||
"express": "^4.17.1",
|
||||
"express-urlrewrite": "^1.2.0",
|
||||
"fs-extra": "^9.0.0",
|
||||
"lodash": "^4.17.15",
|
||||
"vue": "next",
|
||||
"vue-loader": "next",
|
||||
"vue-meta": "^2.3.3",
|
||||
"vue-router": "next",
|
||||
"vue-template-compiler": "^2.6.11",
|
||||
"vuex": "^3.4.0",
|
||||
"webpack": "^4.43.0",
|
||||
"webpack-dev-server": "^3.11.0",
|
||||
"webpackbar": "^4.0.0"
|
||||
}
|
||||
}
|
||||
+11
-9
@@ -10,18 +10,20 @@ import { renderPage } from './ssr/server'
|
||||
|
||||
const app = express()
|
||||
|
||||
app.use(webpackDevMiddleware(webpack(WebpackConfig), {
|
||||
publicPath: '/__build__/',
|
||||
writeToDisk: true,
|
||||
stats: {
|
||||
colors: true,
|
||||
chunks: false
|
||||
}
|
||||
}))
|
||||
app.use(
|
||||
webpackDevMiddleware(webpack(WebpackConfig), {
|
||||
publicPath: '/__build__/',
|
||||
writeToDisk: true,
|
||||
stats: {
|
||||
colors: true,
|
||||
chunks: false,
|
||||
},
|
||||
})
|
||||
)
|
||||
|
||||
fs.readdirSync(__dirname)
|
||||
.filter(file => file !== 'ssr')
|
||||
.forEach((file) => {
|
||||
.forEach(file => {
|
||||
if (fs.statSync(path.join(__dirname, file)).isDirectory()) {
|
||||
app.use(rewrite(`/${file}/*`, `/${file}/index.html`))
|
||||
}
|
||||
|
||||
+38
-35
@@ -6,7 +6,7 @@ Vue.use(VueMeta, {
|
||||
tagIDKeyName: 'hid'
|
||||
}) */
|
||||
|
||||
export default function createMyApp () {
|
||||
export default function createMyApp() {
|
||||
const Home = {
|
||||
template: `<div>
|
||||
<router-link to="/about">About</router-link>
|
||||
@@ -19,15 +19,15 @@ export default function createMyApp () {
|
||||
{
|
||||
hid: 'og:title',
|
||||
name: 'og:title',
|
||||
content: 'Hello World'
|
||||
content: 'Hello World',
|
||||
},
|
||||
{
|
||||
hid: 'description',
|
||||
name: 'description',
|
||||
content: 'Hello World'
|
||||
}
|
||||
]
|
||||
}
|
||||
content: 'Hello World',
|
||||
},
|
||||
],
|
||||
},
|
||||
}
|
||||
|
||||
const About = {
|
||||
@@ -42,28 +42,28 @@ export default function createMyApp () {
|
||||
{
|
||||
hid: 'og:title',
|
||||
name: 'og:title',
|
||||
content: 'About World'
|
||||
content: 'About World',
|
||||
},
|
||||
{
|
||||
hid: 'description',
|
||||
name: 'description',
|
||||
content: 'About World'
|
||||
}
|
||||
]
|
||||
}
|
||||
content: 'About World',
|
||||
},
|
||||
],
|
||||
},
|
||||
}
|
||||
|
||||
const router = createRouter({
|
||||
history: createMemoryHistory('/ssr'),
|
||||
routes: [
|
||||
{ path: '/', component: Home },
|
||||
{ path: '/about', component: About }
|
||||
]
|
||||
{ path: '/about', component: About },
|
||||
],
|
||||
})
|
||||
|
||||
const app = createSSRApp({
|
||||
router,
|
||||
metaInfo () {
|
||||
metaInfo() {
|
||||
return {
|
||||
title: 'Boring Title',
|
||||
htmlAttrs: { amp: true },
|
||||
@@ -74,59 +74,63 @@ export default function createMyApp () {
|
||||
hid: 'og:title',
|
||||
name: 'og:title',
|
||||
template: chunk => `${chunk} - My Site`,
|
||||
content: 'Default Title'
|
||||
content: 'Default Title',
|
||||
},
|
||||
{
|
||||
hid: 'description',
|
||||
name: 'description',
|
||||
content: 'Say something'
|
||||
}
|
||||
content: 'Say something',
|
||||
},
|
||||
],
|
||||
script: [
|
||||
{
|
||||
hid: 'ldjson-schema',
|
||||
type: 'application/ld+json',
|
||||
innerHTML: '{ "@context": "http://www.schema.org", "@type": "Organization" }'
|
||||
}, {
|
||||
innerHTML:
|
||||
'{ "@context": "http://www.schema.org", "@type": "Organization" }',
|
||||
},
|
||||
{
|
||||
type: 'application/ld+json',
|
||||
innerHTML: '{ "body": "yes" }',
|
||||
body: true
|
||||
}, {
|
||||
body: true,
|
||||
},
|
||||
{
|
||||
hid: 'my-async-script-with-load-callback',
|
||||
src: '/user-1.js',
|
||||
body: true,
|
||||
defer: true,
|
||||
callback: this.loadCallback
|
||||
}, {
|
||||
callback: this.loadCallback,
|
||||
},
|
||||
{
|
||||
skip: this.count < 1,
|
||||
src: '/user-2.js',
|
||||
body: true,
|
||||
callback: this.loadCallback
|
||||
}
|
||||
callback: this.loadCallback,
|
||||
},
|
||||
],
|
||||
__dangerouslyDisableSanitizersByTagID: {
|
||||
'ldjson-schema': ['innerHTML']
|
||||
}
|
||||
'ldjson-schema': ['innerHTML'],
|
||||
},
|
||||
}
|
||||
},
|
||||
data () {
|
||||
data() {
|
||||
return {
|
||||
count: 0,
|
||||
users: process.server ? [] : window.users
|
||||
users: process.server ? [] : window.users,
|
||||
}
|
||||
},
|
||||
mounted () {
|
||||
mounted() {
|
||||
const { set, remove } = this.$meta().addApp('client-only')
|
||||
set({
|
||||
bodyAttrs: { class: 'client-only' }
|
||||
bodyAttrs: { class: 'client-only' },
|
||||
})
|
||||
|
||||
setTimeout(() => remove(), 3000)
|
||||
},
|
||||
methods: {
|
||||
loadCallback () {
|
||||
loadCallback() {
|
||||
this.count++
|
||||
}
|
||||
},
|
||||
},
|
||||
template: `
|
||||
<div id="app">
|
||||
@@ -142,8 +146,7 @@ export default function createMyApp () {
|
||||
</ul>
|
||||
|
||||
<router-view />
|
||||
</div>`
|
||||
|
||||
</div>`,
|
||||
})
|
||||
|
||||
app.use(router)
|
||||
|
||||
@@ -12,7 +12,7 @@ const compiled = template(templateContent, { interpolate: /{{([\s\S]+?)}}/g })
|
||||
|
||||
process.server = true
|
||||
|
||||
export async function renderPage ({ url }) {
|
||||
export async function renderPage({ url }) {
|
||||
const { app, router } = await createApp()
|
||||
|
||||
await router.push(url.substr(4))
|
||||
@@ -30,17 +30,17 @@ export async function renderPage ({ url }) {
|
||||
const pageHtml = compiled({
|
||||
app: appHtml,
|
||||
htmlAttrs: {
|
||||
text: () => {}
|
||||
text: () => {},
|
||||
},
|
||||
headAttrs: {
|
||||
text: () => {}
|
||||
text: () => {},
|
||||
},
|
||||
bodyAttrs: {
|
||||
text: () => {}
|
||||
text: () => {},
|
||||
},
|
||||
head: () => {},
|
||||
bodyPrepend: () => {},
|
||||
bodyAppend: () => {}
|
||||
bodyAppend: () => {},
|
||||
// ...app.$meta().inject()
|
||||
})
|
||||
|
||||
|
||||
+77
-58
@@ -1,7 +1,15 @@
|
||||
import { createApp, defineComponent, reactive, inject, toRefs, h, watch } from 'vue'
|
||||
import {
|
||||
createApp,
|
||||
defineComponent,
|
||||
getCurrentInstance,
|
||||
reactive,
|
||||
inject,
|
||||
toRefs,
|
||||
h,
|
||||
watch,
|
||||
} from 'vue'
|
||||
import { createRouter, createWebHistory } from 'vue-router'
|
||||
import Metainfo from '../next/Metainfo.vue'
|
||||
import { createMeta, useMeta } from '../next'
|
||||
import { createManager, useMeta, useMetainfo } from '../../src'
|
||||
// import About from './about.vue'
|
||||
|
||||
const metaUpdated = 'no'
|
||||
@@ -9,47 +17,48 @@ const metaUpdated = 'no'
|
||||
const ChildComponent = defineComponent({
|
||||
name: 'child-component',
|
||||
props: {
|
||||
page: String
|
||||
page: String,
|
||||
},
|
||||
template: `
|
||||
<div>
|
||||
<h3>You're looking at the <strong>{{ page }}</strong> page</h3>
|
||||
<p>Has metaInfo been updated due to navigation? {{ metaUpdated }}</p>
|
||||
</div>`,
|
||||
setup (props) {
|
||||
setup(props) {
|
||||
const state = reactive({
|
||||
date: null,
|
||||
metaUpdated
|
||||
metaUpdated,
|
||||
})
|
||||
|
||||
const title = props.page[0].toUpperCase() + props.page.slice(1)
|
||||
|
||||
console.log('ChildComponent Setup')
|
||||
useMeta({
|
||||
charset: 'utf16',
|
||||
title,
|
||||
description: 'Description ' + props.page,
|
||||
og: {
|
||||
title: 'Og Title ' + props.page
|
||||
}
|
||||
title: 'Og Title ' + props.page,
|
||||
},
|
||||
})
|
||||
|
||||
return {
|
||||
...toRefs(state)
|
||||
...toRefs(state),
|
||||
}
|
||||
}
|
||||
},
|
||||
})
|
||||
|
||||
function view (page) {
|
||||
function view(page) {
|
||||
return {
|
||||
name: `section-${page}`,
|
||||
render () {
|
||||
render() {
|
||||
return h(ChildComponent, { page })
|
||||
}
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
const App = {
|
||||
setup () {
|
||||
setup() {
|
||||
// console.log('App', getCurrentInstance())
|
||||
const { meta } = useMeta({
|
||||
base: { href: '/vue-router', target: '_blank' },
|
||||
charset: 'utf8',
|
||||
@@ -60,23 +69,23 @@ const App = {
|
||||
description: 'Bla bla',
|
||||
image: [
|
||||
'https://picsum.photos/600/400/?image=80',
|
||||
'https://picsum.photos/600/400/?image=82'
|
||||
]
|
||||
'https://picsum.photos/600/400/?image=82',
|
||||
],
|
||||
},
|
||||
twitter: {
|
||||
title: 'Twitter Title'
|
||||
title: 'Twitter Title',
|
||||
},
|
||||
noscript: [
|
||||
'<!-- // A code comment -->',
|
||||
{ tag: 'link', rel: 'stylesheet', href: 'style.css' }
|
||||
{ tag: 'link', rel: 'stylesheet', href: 'style.css' },
|
||||
],
|
||||
otherNoscript: {
|
||||
tag: 'noscript',
|
||||
'data-test': 'hello',
|
||||
content: [
|
||||
'<!-- // Another code comment -->',
|
||||
{ tag: 'link', rel: 'stylesheet', href: 'style2.css' }
|
||||
]
|
||||
{ tag: 'link', rel: 'stylesheet', href: 'style2.css' },
|
||||
],
|
||||
},
|
||||
body: 'body-script1.js',
|
||||
script: [
|
||||
@@ -84,26 +93,32 @@ const App = {
|
||||
{ src: 'head-script1.js' },
|
||||
'<![endif]-->',
|
||||
{ src: 'body-script2.js', target: 'body' },
|
||||
{ src: 'body-script3.js', target: '#put-it-here' }
|
||||
{ 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'
|
||||
}]
|
||||
}]
|
||||
}]
|
||||
}
|
||||
content: [
|
||||
{
|
||||
tag: 'choose',
|
||||
content: [
|
||||
{
|
||||
tag: 'when',
|
||||
test: '$(HTTP_COOKIE{group})=="Advanced"',
|
||||
content: [
|
||||
{
|
||||
tag: 'include',
|
||||
src: 'http://www.example.com/advanced.html',
|
||||
},
|
||||
],
|
||||
},
|
||||
],
|
||||
},
|
||||
],
|
||||
},
|
||||
})
|
||||
|
||||
setTimeout(() => (meta.title = 'My Updated Title'), 2000)
|
||||
|
||||
const metainfo = inject('metainfo')
|
||||
const metainfo = useMetainfo()
|
||||
|
||||
window.$metainfo = metainfo
|
||||
|
||||
@@ -112,7 +127,7 @@ const App = {
|
||||
})
|
||||
|
||||
return {
|
||||
metainfo
|
||||
metainfo,
|
||||
}
|
||||
},
|
||||
template: `
|
||||
@@ -133,56 +148,60 @@ const App = {
|
||||
</transition>
|
||||
<p>Inspect Element to see the meta info</p>
|
||||
</div>
|
||||
`
|
||||
`,
|
||||
}
|
||||
|
||||
function decisionMaker5000000 (key, options, currentValue) {
|
||||
function decisionMaker5000000(key, pathSegments, getOptions, getCurrentValue) {
|
||||
let theChosenOne
|
||||
|
||||
const options = getOptions()
|
||||
|
||||
for (const option of options) {
|
||||
if (!theChosenOne || theChosenOne.context.uid < option.context.uid) {
|
||||
if (!theChosenOne || theChosenOne.context.vm.uid < option.context.vm.uid) {
|
||||
theChosenOne = option
|
||||
}
|
||||
}
|
||||
|
||||
console.log(key, currentValue, options.map(({ value }) => value))
|
||||
console.log(
|
||||
key,
|
||||
getCurrentValue(),
|
||||
options.map(({ value }) => value)
|
||||
)
|
||||
console.log(theChosenOne.value)
|
||||
return theChosenOne.value
|
||||
}
|
||||
|
||||
const meta = createMeta({
|
||||
const metaManager = createManager({
|
||||
resolver: decisionMaker5000000,
|
||||
config: {
|
||||
esi: {
|
||||
group: true,
|
||||
namespaced: true,
|
||||
contentAttributes: [
|
||||
'src',
|
||||
'test',
|
||||
'text'
|
||||
]
|
||||
}
|
||||
}
|
||||
contentAttributes: ['src', 'test', 'text'],
|
||||
},
|
||||
},
|
||||
})
|
||||
|
||||
useMeta(
|
||||
{
|
||||
og: {
|
||||
something: 'test',
|
||||
},
|
||||
},
|
||||
metaManager
|
||||
)
|
||||
|
||||
const router = createRouter({
|
||||
history: createWebHistory('/vue-router'),
|
||||
routes: [
|
||||
{ name: 'home', path: '/', component: view('home') },
|
||||
{ name: 'about', path: '/about', component: view('about') }
|
||||
]
|
||||
})
|
||||
|
||||
useMeta({
|
||||
og: {
|
||||
something: 'test'
|
||||
}
|
||||
{ name: 'about', path: '/about', component: view('about') },
|
||||
],
|
||||
})
|
||||
|
||||
const app = createApp(App)
|
||||
app.component('metainfo', Metainfo)
|
||||
app.use(router)
|
||||
app.use(meta)
|
||||
app.use(metaManager)
|
||||
app.mount('#app')
|
||||
|
||||
// old stuff:
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
<!DOCTYPE html>
|
||||
<html>
|
||||
<head>
|
||||
<link rel="stylesheet" href="/global.css">
|
||||
<link rel="stylesheet" href="/_static/global.css">
|
||||
<style>
|
||||
.page-enter-active, .page-leave-active {
|
||||
transition: opacity .5s
|
||||
|
||||
@@ -12,6 +12,6 @@ export default new Router({
|
||||
base: '/vuex-async',
|
||||
routes: [
|
||||
{ path: '/', component: Home },
|
||||
{ path: '/posts/:slug', component: Post }
|
||||
]
|
||||
{ path: '/posts/:slug', component: Post },
|
||||
],
|
||||
})
|
||||
|
||||
@@ -14,60 +14,64 @@ export default new Vuex.Store({
|
||||
title: '',
|
||||
content: '',
|
||||
slug: '',
|
||||
published: false
|
||||
published: false,
|
||||
},
|
||||
posts: [{
|
||||
slug: 'a-sample-blog-post',
|
||||
title: 'A Sample Blog Post',
|
||||
content: 'This is the blog post content',
|
||||
published: true
|
||||
}, {
|
||||
slug: 'an-unpublished-blog-post',
|
||||
title: 'An Unpublished Blog Post',
|
||||
content: 'This is the blog post content',
|
||||
published: false
|
||||
}, {
|
||||
slug: 'another-blog-post',
|
||||
title: 'Another Blog Post',
|
||||
content: 'This is the blog post content',
|
||||
published: true
|
||||
}]
|
||||
posts: [
|
||||
{
|
||||
slug: 'a-sample-blog-post',
|
||||
title: 'A Sample Blog Post',
|
||||
content: 'This is the blog post content',
|
||||
published: true,
|
||||
},
|
||||
{
|
||||
slug: 'an-unpublished-blog-post',
|
||||
title: 'An Unpublished Blog Post',
|
||||
content: 'This is the blog post content',
|
||||
published: false,
|
||||
},
|
||||
{
|
||||
slug: 'another-blog-post',
|
||||
title: 'Another Blog Post',
|
||||
content: 'This is the blog post content',
|
||||
published: true,
|
||||
},
|
||||
],
|
||||
},
|
||||
|
||||
// GETTERS
|
||||
getters: {
|
||||
isLoading (state) {
|
||||
isLoading(state) {
|
||||
return state.isLoading
|
||||
},
|
||||
post (state) {
|
||||
post(state) {
|
||||
return state.post
|
||||
},
|
||||
publishedPosts (state) {
|
||||
publishedPosts(state) {
|
||||
return state.posts.filter(post => post.published)
|
||||
},
|
||||
publishedPostsCount (state, getters) {
|
||||
publishedPostsCount(state, getters) {
|
||||
return getters.publishedPosts.length
|
||||
}
|
||||
},
|
||||
},
|
||||
|
||||
// MUTATIONS
|
||||
mutations: {
|
||||
loadingState (state, { isLoading }) {
|
||||
loadingState(state, { isLoading }) {
|
||||
state.isLoading = isLoading
|
||||
},
|
||||
getPost (state, { slug }) {
|
||||
getPost(state, { slug }) {
|
||||
state.post = state.posts.find(post => post.slug === slug)
|
||||
}
|
||||
},
|
||||
},
|
||||
|
||||
// ACTIONS
|
||||
actions: {
|
||||
getPost ({ commit }, payload) {
|
||||
getPost({ commit }, payload) {
|
||||
commit('loadingState', { isLoading: true })
|
||||
setTimeout(() => {
|
||||
commit('getPost', payload)
|
||||
commit('loadingState', { isLoading: false })
|
||||
}, 2000)
|
||||
}
|
||||
}
|
||||
},
|
||||
},
|
||||
})
|
||||
|
||||
@@ -13,6 +13,6 @@ export default new Router({
|
||||
base: '/vuex',
|
||||
routes: [
|
||||
{ path: '/', component: Home },
|
||||
{ path: '/posts/:slug', component: Post }
|
||||
]
|
||||
{ path: '/posts/:slug', component: Post },
|
||||
],
|
||||
})
|
||||
|
||||
+30
-26
@@ -14,50 +14,54 @@ export default new Vuex.Store({
|
||||
title: '',
|
||||
content: '',
|
||||
slug: '',
|
||||
published: false
|
||||
published: false,
|
||||
},
|
||||
posts: [{
|
||||
slug: 'a-sample-blog-post',
|
||||
title: 'A Sample Blog Post',
|
||||
content: 'This is the blog post content',
|
||||
published: true
|
||||
}, {
|
||||
slug: 'an-unpublished-blog-post',
|
||||
title: 'An Unpublished Blog Post',
|
||||
content: 'This is the blog post content',
|
||||
published: false
|
||||
}, {
|
||||
slug: 'another-blog-post',
|
||||
title: 'Another Blog Post',
|
||||
content: 'This is the blog post content',
|
||||
published: true
|
||||
}]
|
||||
posts: [
|
||||
{
|
||||
slug: 'a-sample-blog-post',
|
||||
title: 'A Sample Blog Post',
|
||||
content: 'This is the blog post content',
|
||||
published: true,
|
||||
},
|
||||
{
|
||||
slug: 'an-unpublished-blog-post',
|
||||
title: 'An Unpublished Blog Post',
|
||||
content: 'This is the blog post content',
|
||||
published: false,
|
||||
},
|
||||
{
|
||||
slug: 'another-blog-post',
|
||||
title: 'Another Blog Post',
|
||||
content: 'This is the blog post content',
|
||||
published: true,
|
||||
},
|
||||
],
|
||||
},
|
||||
|
||||
// GETTERS
|
||||
getters: {
|
||||
post (state) {
|
||||
post(state) {
|
||||
return state.post
|
||||
},
|
||||
publishedPosts (state) {
|
||||
publishedPosts(state) {
|
||||
return state.posts.filter(post => post.published)
|
||||
},
|
||||
publishedPostsCount (state, getters) {
|
||||
publishedPostsCount(state, getters) {
|
||||
return getters.publishedPosts.length
|
||||
}
|
||||
},
|
||||
},
|
||||
|
||||
// MUTATIONS
|
||||
mutations: {
|
||||
getPost (state, { slug }) {
|
||||
getPost(state, { slug }) {
|
||||
state.post = state.posts.find(post => post.slug === slug)
|
||||
}
|
||||
},
|
||||
},
|
||||
|
||||
// ACTIONS
|
||||
actions: {
|
||||
getPost ({ commit }, payload) {
|
||||
getPost({ commit }, payload) {
|
||||
commit('getPost', payload)
|
||||
}
|
||||
}
|
||||
},
|
||||
},
|
||||
})
|
||||
|
||||
@@ -1,89 +0,0 @@
|
||||
import fs from 'fs'
|
||||
import path from 'path'
|
||||
import webpack from 'webpack'
|
||||
import WebpackBar from 'webpackbar'
|
||||
import { VueLoaderPlugin } from 'vue-loader'
|
||||
|
||||
// const srcDir = path.join(__dirname, '..', 'src')
|
||||
|
||||
export default {
|
||||
devtool: 'inline-source-map',
|
||||
mode: 'development',
|
||||
entry: fs.readdirSync(__dirname)
|
||||
.reduce((entries, dir) => {
|
||||
const fullDir = path.join(__dirname, dir)
|
||||
|
||||
if (dir === 'ssr') {
|
||||
entries[dir] = path.join(fullDir, 'browser.js')
|
||||
} else if (dir === 'vue-router') {
|
||||
const entry = path.join(fullDir, 'app.js')
|
||||
if (fs.statSync(fullDir).isDirectory() && fs.existsSync(entry)) {
|
||||
entries[dir] = entry
|
||||
}
|
||||
}
|
||||
return entries
|
||||
}, {}),
|
||||
output: {
|
||||
path: path.join(__dirname, '__build__'),
|
||||
filename: '[name].js',
|
||||
chunkFilename: '[id].chunk.js',
|
||||
publicPath: '/__build__/'
|
||||
},
|
||||
module: {
|
||||
rules: [
|
||||
{
|
||||
test: /\.js$/,
|
||||
exclude: /node_modules/,
|
||||
use: {
|
||||
loader: 'babel-loader',
|
||||
options: {
|
||||
presets: [
|
||||
['@babel/preset-env', {
|
||||
useBuiltIns: 'usage',
|
||||
corejs: '3',
|
||||
targets: { ie: 9, safari: '5.1' }
|
||||
}]
|
||||
]
|
||||
}
|
||||
}
|
||||
},
|
||||
{
|
||||
resourceQuery: /blockType=head/,
|
||||
loader: require.resolve('./meta-loader.js')
|
||||
},
|
||||
{
|
||||
test: /\.vue$/,
|
||||
use: 'vue-loader'
|
||||
}
|
||||
]
|
||||
},
|
||||
resolve: {
|
||||
alias: {
|
||||
// this isn't technically needed, since the default `vue` entry for bundlers
|
||||
// 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-bundler.js',
|
||||
'vue-meta': path.resolve(__dirname, './next/')
|
||||
}
|
||||
},
|
||||
// Expose __dirname to allow automatically setting basename.
|
||||
context: __dirname,
|
||||
node: {
|
||||
__dirname: true
|
||||
},
|
||||
plugins: [
|
||||
new WebpackBar(),
|
||||
new VueLoaderPlugin(),
|
||||
new webpack.DefinePlugin({
|
||||
'process.env.NODE_ENV': JSON.stringify(process.env.NODE_ENV || 'development')
|
||||
})
|
||||
],
|
||||
devServer: {
|
||||
inline: true,
|
||||
hot: true,
|
||||
stats: 'minimal',
|
||||
contentBase: __dirname,
|
||||
overlay: true
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user