mirror of
https://github.com/tenrok/vue-meta.git
synced 2026-06-19 22:20:33 +03:00
feat: enable onload callbacks (#414)
* refactor(examples): run ssr example from server * chore: switch to babel for build buble complains too much * feat: enable loaded callbacks feat: add skip option * examples: add async-callback browser example * examples: fix server * examples(ssr): add reactive script with callback * fix: also skip on ssr * chore: remove unused var * feat: only add mutationobserver if DOM is still loading feat: disconnect mutation observer once DOM has loaded * examples: pass vmid to loadCallback instead of el * feat: also support load callbacks for link/style tags * test: add unit tests for load * test: add load e2e test * chore: fix lint * chore: remove unused files * test: fix e2e load callback test * test: fix attempt * examples: ie9 compatiblity destructuring doesnt work in ie9 * fix: add onload attribute on ssr dont rely on mutationobserver * chore: lint ci conf * refactor: remove loadCallbackAttribute config option test: fix coverage for load * test: improve coverage * fix: only use console when it exists (for ie9) * chore: fix coverage
This commit is contained in:
@@ -11,9 +11,11 @@
|
||||
<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="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>
|
||||
<li><a href="async-callback">Async Callback</a></li>
|
||||
</ul>
|
||||
</body>
|
||||
</html>
|
||||
@@ -0,0 +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'
|
||||
}
|
||||
},
|
||||
'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'
|
||||
}
|
||||
})
|
||||
@@ -0,0 +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'
|
||||
}
|
||||
},
|
||||
'phone': '010-692-6593 x09125',
|
||||
'website': 'anastasia.net',
|
||||
'company': {
|
||||
'name': 'Deckow-Crist',
|
||||
'catchPhrase': 'Proactive didactic contingency',
|
||||
'bs': 'synergize scalable supply-chains'
|
||||
}
|
||||
})
|
||||
@@ -0,0 +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'
|
||||
}
|
||||
},
|
||||
'phone': '1-463-123-4447',
|
||||
'website': 'ramiro.info',
|
||||
'company': {
|
||||
'name': 'Romaguera-Jacobson',
|
||||
'catchPhrase': 'Face to face bifurcated interface',
|
||||
'bs': 'e-enable strategic applications'
|
||||
}
|
||||
})
|
||||
@@ -0,0 +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'
|
||||
}
|
||||
},
|
||||
'phone': '493-170-9623 x156',
|
||||
'website': 'kale.biz',
|
||||
'company': {
|
||||
'name': 'Robel-Corkery',
|
||||
'catchPhrase': 'Multi-tiered zero tolerance productivity',
|
||||
'bs': 'transition cutting-edge web services'
|
||||
}
|
||||
})
|
||||
@@ -0,0 +1,88 @@
|
||||
import Vue from 'vue'
|
||||
import VueMeta from 'vue-meta'
|
||||
|
||||
Vue.use(VueMeta)
|
||||
|
||||
window.users = []
|
||||
|
||||
new Vue({
|
||||
metaInfo () {
|
||||
return {
|
||||
title: 'Async Callback',
|
||||
titleTemplate: '%s | Vue Meta Examples',
|
||||
script: [
|
||||
{
|
||||
skip: this.count < 2,
|
||||
vmid: 'potatoes',
|
||||
src: '/user-3.js',
|
||||
async: true,
|
||||
callback: this.updateCounter
|
||||
},
|
||||
{
|
||||
skip: this.count < 1,
|
||||
vmid: 'vegetables',
|
||||
src: '/user-2.js',
|
||||
async: true,
|
||||
callback: this.updateCounter
|
||||
},
|
||||
{
|
||||
vmid: 'meat',
|
||||
src: '/user-1.js',
|
||||
async: true,
|
||||
callback: el => this.loadCallback(el.getAttribute('data-vmid'))
|
||||
},
|
||||
...this.scripts
|
||||
]
|
||||
}
|
||||
},
|
||||
data () {
|
||||
return {
|
||||
count: 0,
|
||||
scripts: [],
|
||||
users: window.users
|
||||
}
|
||||
},
|
||||
watch: {
|
||||
count (val) {
|
||||
if (val === 3) {
|
||||
this.addScript()
|
||||
}
|
||||
}
|
||||
},
|
||||
methods: {
|
||||
updateCounter () {
|
||||
this.count++
|
||||
},
|
||||
addScript () {
|
||||
this.scripts.push({
|
||||
src: '/user-4.js',
|
||||
callback: () => {
|
||||
this.updateCounter()
|
||||
}
|
||||
})
|
||||
},
|
||||
loadCallback (vmid) {
|
||||
if (vmid === 'meat') {
|
||||
this.updateCounter()
|
||||
}
|
||||
}
|
||||
},
|
||||
template: `
|
||||
<div id="app">
|
||||
<h1>Async Callback</h1>
|
||||
<p>{{ count }} scripts loaded</p>
|
||||
|
||||
<div>
|
||||
<h2>Users</h2>
|
||||
<ul>
|
||||
<li
|
||||
v-for="user in users"
|
||||
:key="user.id"
|
||||
>
|
||||
<strong>{{ user.id }}</strong>: {{ user.name }}
|
||||
</li>
|
||||
</ul>
|
||||
</div>
|
||||
</div>
|
||||
`
|
||||
}).$mount('#app')
|
||||
@@ -0,0 +1,13 @@
|
||||
<!DOCTYPE html>
|
||||
<html>
|
||||
<head>
|
||||
<title>Async Callback Title</title>
|
||||
<link rel="stylesheet" href="/global.css">
|
||||
</head>
|
||||
<body>
|
||||
<a href="/">← Examples index</a>
|
||||
<div id="app"></div>
|
||||
|
||||
<script src="/__build__/async-callback.js"></script>
|
||||
</body>
|
||||
</html>
|
||||
@@ -11,10 +11,10 @@ Vue.component('child', {
|
||||
default: ''
|
||||
}
|
||||
},
|
||||
render(h) {
|
||||
render (h) {
|
||||
return h('h3', null, this.page)
|
||||
},
|
||||
metaInfo() {
|
||||
metaInfo () {
|
||||
return {
|
||||
title: this.page
|
||||
}
|
||||
|
||||
@@ -11,11 +11,11 @@ Vue.component('foo', {
|
||||
})
|
||||
|
||||
new Vue({
|
||||
data() {
|
||||
data () {
|
||||
return { showFoo: false }
|
||||
},
|
||||
methods: {
|
||||
show() {
|
||||
show () {
|
||||
this.showFoo = !this.showFoo
|
||||
}
|
||||
},
|
||||
|
||||
@@ -6,7 +6,7 @@ Vue.use(VueMeta)
|
||||
// index.html contains a manual SSR render
|
||||
|
||||
const app1 = new Vue({
|
||||
metaInfo() {
|
||||
metaInfo () {
|
||||
return {
|
||||
title: 'App 1 title',
|
||||
bodyAttrs: {
|
||||
@@ -14,15 +14,15 @@ const app1 = new Vue({
|
||||
},
|
||||
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'
|
||||
}
|
||||
@@ -44,7 +44,7 @@ const app2 = new Vue({
|
||||
],
|
||||
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: `
|
||||
@@ -60,7 +60,6 @@ const app3 = new Vue({
|
||||
`
|
||||
}).$mount('#app3')
|
||||
|
||||
|
||||
setTimeout(() => {
|
||||
console.log('trigger app 1')
|
||||
app1.$data.ogContent = 'Hello from app 1'
|
||||
@@ -75,8 +74,9 @@ setTimeout(() => {
|
||||
console.log('trigger app 3')
|
||||
app3.$meta().refresh()
|
||||
}, 7500)
|
||||
|
||||
setTimeout(() => {
|
||||
console.log('trigger app 4')
|
||||
const App = Vue.extend({ template: `<div>app 4</div>` })
|
||||
const app4 = new App().$mount()
|
||||
new App().$mount()
|
||||
}, 10000)
|
||||
|
||||
+18
-1
@@ -6,11 +6,13 @@ import rewrite from 'express-urlrewrite'
|
||||
import webpack from 'webpack'
|
||||
import webpackDevMiddleware from 'webpack-dev-middleware'
|
||||
import WebpackConfig from './webpack.config'
|
||||
import { renderPage } from './ssr/server'
|
||||
|
||||
const app = express()
|
||||
|
||||
app.use(webpackDevMiddleware(webpack(WebpackConfig), {
|
||||
publicPath: '/__build__/',
|
||||
writeToDisk: false,
|
||||
stats: {
|
||||
colors: true,
|
||||
chunks: false
|
||||
@@ -21,12 +23,27 @@ fs.readdirSync(__dirname)
|
||||
.filter(file => file !== 'ssr')
|
||||
.forEach((file) => {
|
||||
if (fs.statSync(path.join(__dirname, file)).isDirectory()) {
|
||||
app.use(rewrite('/' + file + '/*', '/' + file + '/index.html'))
|
||||
app.use(rewrite(`/${file}/*`, `/${file}/index.html`))
|
||||
}
|
||||
})
|
||||
|
||||
app.use(express.static(path.join(__dirname, '_static')))
|
||||
app.use(express.static(__dirname))
|
||||
|
||||
app.use(async (req, res, next) => {
|
||||
if (!req.url.startsWith('/ssr')) {
|
||||
next()
|
||||
}
|
||||
|
||||
try {
|
||||
const html = await renderPage()
|
||||
res.send(html)
|
||||
} catch (e) {
|
||||
consola.error('SSR Oops:', e)
|
||||
next()
|
||||
}
|
||||
})
|
||||
|
||||
const host = process.env.HOST || 'localhost'
|
||||
const port = process.env.PORT || 3000
|
||||
|
||||
|
||||
@@ -0,0 +1,90 @@
|
||||
import Vue from 'vue'
|
||||
import VueMeta from '../../'
|
||||
|
||||
Vue.use(VueMeta, {
|
||||
tagIDKeyName: 'hid'
|
||||
})
|
||||
|
||||
export default function createApp () {
|
||||
return new Vue({
|
||||
components: {
|
||||
Hello: {
|
||||
template: '<p>Hello World</p>',
|
||||
metaInfo: {
|
||||
title: 'Hello World',
|
||||
meta: [
|
||||
{
|
||||
hid: 'description',
|
||||
name: 'description',
|
||||
content: 'The description'
|
||||
}
|
||||
]
|
||||
}
|
||||
}
|
||||
},
|
||||
metaInfo () {
|
||||
return {
|
||||
title: 'Boring Title',
|
||||
htmlAttrs: { amp: true },
|
||||
meta: [
|
||||
{
|
||||
hid: 'description',
|
||||
name: 'description',
|
||||
content: 'Say something'
|
||||
}
|
||||
],
|
||||
script: [
|
||||
{
|
||||
hid: 'ldjson-schema',
|
||||
type: 'application/ld+json',
|
||||
innerHTML: '{ "@context": "http://www.schema.org", "@type": "Organization" }'
|
||||
}, {
|
||||
type: 'application/ld+json',
|
||||
innerHTML: '{ "body": "yes" }',
|
||||
body: true
|
||||
}, {
|
||||
hid: 'my-async-script-with-load-callback',
|
||||
src: '/user-1.js',
|
||||
body: true,
|
||||
defer: true,
|
||||
callback: this.loadCallback
|
||||
}, {
|
||||
skip: this.count < 1,
|
||||
src: '/user-2.js',
|
||||
body: true,
|
||||
callback: this.loadCallback
|
||||
}
|
||||
],
|
||||
__dangerouslyDisableSanitizersByTagID: {
|
||||
'ldjson-schema': ['innerHTML']
|
||||
}
|
||||
}
|
||||
},
|
||||
data () {
|
||||
return {
|
||||
count: 0,
|
||||
users: process.server ? [] : window.users
|
||||
}
|
||||
},
|
||||
methods: {
|
||||
loadCallback () {
|
||||
this.count++
|
||||
}
|
||||
},
|
||||
template: `
|
||||
<div id="app">
|
||||
<hello/>
|
||||
|
||||
<p>{{ count }} users loaded</p>
|
||||
|
||||
<ul>
|
||||
<li
|
||||
v-for="user in users"
|
||||
:key="user.id"
|
||||
>
|
||||
{{ user.id }}: {{ user.name }}
|
||||
</li>
|
||||
</ul>
|
||||
</div>`
|
||||
})
|
||||
}
|
||||
@@ -1,55 +0,0 @@
|
||||
import Vue from 'vue'
|
||||
|
||||
export default async function createApp() {
|
||||
// the dynamic import is for this example only
|
||||
const vueMetaModule = process.env.NODE_ENV === 'development' ? '../../' : 'vue-meta'
|
||||
const VueMeta = await import(vueMetaModule).then(m => m.default || m)
|
||||
|
||||
Vue.use(VueMeta, {
|
||||
tagIDKeyName: 'hid'
|
||||
})
|
||||
|
||||
return new Vue({
|
||||
components: {
|
||||
Hello: {
|
||||
template: '<p>Hello</p>',
|
||||
metaInfo: {
|
||||
title: 'Coucou',
|
||||
meta: [
|
||||
{
|
||||
hid: 'description',
|
||||
name: 'description',
|
||||
content: 'Coucou'
|
||||
}
|
||||
]
|
||||
}
|
||||
}
|
||||
},
|
||||
template: '<hello/>',
|
||||
metaInfo: {
|
||||
title: 'Hello',
|
||||
htmlAttrs: { amp: true },
|
||||
meta: [
|
||||
{
|
||||
hid: 'description',
|
||||
name: 'description',
|
||||
content: 'Hello World'
|
||||
}
|
||||
],
|
||||
script: [
|
||||
{
|
||||
hid: 'ldjson-schema',
|
||||
type: 'application/ld+json',
|
||||
innerHTML: '{ "@context": "http://www.schema.org", "@type": "Organization" }'
|
||||
}, {
|
||||
type: 'application/ld+json',
|
||||
innerHTML: '{ "body": "yes" }',
|
||||
body: true
|
||||
}
|
||||
],
|
||||
__dangerouslyDisableSanitizersByTagID: {
|
||||
'ldjson-schema': ['innerHTML']
|
||||
}
|
||||
}
|
||||
})
|
||||
}
|
||||
@@ -1,16 +1,22 @@
|
||||
<!doctype html>
|
||||
<html data-vue-meta-server-rendered {{ htmlAttrs.text() }}>
|
||||
<html {{ htmlAttrs.text(true) }}>
|
||||
<head {{ headAttrs.text() }}>
|
||||
{{ meta.text() }}
|
||||
{{ title.text() }}
|
||||
{{ meta.text() }}
|
||||
<link rel="stylesheet" href="/global.css">
|
||||
{{ link.text() }}
|
||||
{{ style.text() }}
|
||||
{{ webpackAssets }}
|
||||
{{ script.text() }}
|
||||
{{ noscript.text() }}
|
||||
</head>
|
||||
<body {{ bodyAttrs.text() }}>
|
||||
{{ script.text({ pbody: true }) }}
|
||||
{{ noscript.text({ pbody: true }) }}
|
||||
|
||||
<a href="/">← Examples index</a>
|
||||
{{ app }}
|
||||
|
||||
<script src="/__build__/ssr.js"></script>
|
||||
{{ script.text({ body: true }) }}
|
||||
{{ noscript.text({ body: true }) }}
|
||||
</body>
|
||||
|
||||
@@ -0,0 +1,5 @@
|
||||
import createApp from './App'
|
||||
|
||||
window.users = []
|
||||
|
||||
createApp().$mount('#app')
|
||||
@@ -1,3 +0,0 @@
|
||||
import createApp from './app'
|
||||
|
||||
createApp().$mount('#app')
|
||||
@@ -1,36 +0,0 @@
|
||||
import path from 'path'
|
||||
import fs from 'fs-extra'
|
||||
import template from 'lodash/template'
|
||||
import { createRenderer } from 'vue-server-renderer'
|
||||
import consola from 'consola'
|
||||
import createApp from './server-entry'
|
||||
|
||||
const renderer = createRenderer()
|
||||
|
||||
async function createPage() {
|
||||
const templateFile = path.resolve(__dirname, 'app.template.html')
|
||||
const templateContent = await fs.readFile(templateFile, { encoding: 'utf8' })
|
||||
|
||||
// see: https://lodash.com/docs#template
|
||||
const compiled = template(templateContent, { interpolate: /{{([\s\S]+?)}}/g })
|
||||
|
||||
const webpackAssets = '<link rel="stylesheet" href="../global.css">'
|
||||
const serverApp = await createApp()
|
||||
const appHtml = await renderer.renderToString(serverApp)
|
||||
|
||||
const pageHtml = compiled({
|
||||
app: appHtml,
|
||||
webpackAssets,
|
||||
...serverApp.$meta().inject()
|
||||
})
|
||||
|
||||
return pageHtml
|
||||
}
|
||||
|
||||
consola.info(`Creating ssr page`)
|
||||
createPage()
|
||||
.then((pageHtml) => {
|
||||
consola.info(`Done, page:`)
|
||||
consola.log(pageHtml)
|
||||
})
|
||||
.catch(e => consola.error(e))
|
||||
@@ -1,3 +0,0 @@
|
||||
import createApp from './app'
|
||||
|
||||
export default createApp
|
||||
@@ -0,0 +1,27 @@
|
||||
import path from 'path'
|
||||
import fs from 'fs-extra'
|
||||
import template from 'lodash/template'
|
||||
import { createRenderer } from 'vue-server-renderer'
|
||||
import createApp from './App'
|
||||
|
||||
const renderer = createRenderer({ runInNewContext: false })
|
||||
|
||||
const templateFile = path.resolve(__dirname, 'app.template.html')
|
||||
const templateContent = fs.readFileSync(templateFile, { encoding: 'utf8' })
|
||||
|
||||
// see: https://lodash.com/docs#template
|
||||
const compiled = template(templateContent, { interpolate: /{{([\s\S]+?)}}/g })
|
||||
|
||||
process.server = true
|
||||
|
||||
export async function renderPage () {
|
||||
const app = await createApp()
|
||||
const appHtml = await renderer.renderToString(app)
|
||||
|
||||
const pageHtml = compiled({
|
||||
app: appHtml,
|
||||
...app.$meta().inject()
|
||||
})
|
||||
|
||||
return pageHtml
|
||||
}
|
||||
@@ -15,21 +15,21 @@ const ChildComponent = {
|
||||
<h3>You're looking at the <strong>{{ page }}</strong> page</h3>
|
||||
<p>Has metaInfo been updated? {{ metaUpdated }}</p>
|
||||
</div>`,
|
||||
metaInfo() {
|
||||
metaInfo () {
|
||||
return {
|
||||
title: `${this.page} - ${this.date && this.date.toTimeString()}`,
|
||||
afterNavigation() {
|
||||
afterNavigation () {
|
||||
metaUpdated = 'yes'
|
||||
}
|
||||
}
|
||||
},
|
||||
data() {
|
||||
data () {
|
||||
return {
|
||||
date: null,
|
||||
metaUpdated
|
||||
}
|
||||
},
|
||||
mounted() {
|
||||
mounted () {
|
||||
setInterval(() => {
|
||||
this.date = new Date()
|
||||
}, 1000)
|
||||
@@ -39,10 +39,10 @@ const ChildComponent = {
|
||||
// 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) {
|
||||
function view (page) {
|
||||
return {
|
||||
name: `section-${page}`,
|
||||
render(h) {
|
||||
render (h) {
|
||||
return h(ChildComponent, {
|
||||
props: { page }
|
||||
})
|
||||
|
||||
@@ -36,33 +36,33 @@ export default new Vuex.Store({
|
||||
|
||||
// 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)
|
||||
|
||||
@@ -36,27 +36,27 @@ export default new Vuex.Store({
|
||||
|
||||
// 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)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -10,12 +10,16 @@ export default {
|
||||
devtool: 'inline-source-map',
|
||||
mode: 'development',
|
||||
entry: fs.readdirSync(__dirname)
|
||||
.filter(entry => entry !== 'ssr')
|
||||
.reduce((entries, dir) => {
|
||||
const fullDir = path.join(__dirname, dir)
|
||||
const entry = path.join(fullDir, 'app.js')
|
||||
if (fs.statSync(fullDir).isDirectory() && fs.existsSync(entry)) {
|
||||
entries[dir] = entry
|
||||
|
||||
if (dir === 'ssr') {
|
||||
entries[dir] = path.join(fullDir, 'browser.js')
|
||||
} else {
|
||||
const entry = path.join(fullDir, 'app.js')
|
||||
if (fs.statSync(fullDir).isDirectory() && fs.existsSync(entry)) {
|
||||
entries[dir] = entry
|
||||
}
|
||||
}
|
||||
return entries
|
||||
}, {}),
|
||||
@@ -27,7 +31,22 @@ export default {
|
||||
},
|
||||
module: {
|
||||
rules: [
|
||||
{ test: /\.js$/, exclude: /node_modules/, use: 'babel-loader' },
|
||||
{
|
||||
test: /\.js$/,
|
||||
exclude: /node_modules/,
|
||||
use: {
|
||||
loader: 'babel-loader',
|
||||
options: {
|
||||
presets: [
|
||||
['@babel/preset-env', {
|
||||
useBuiltIns: 'usage',
|
||||
corejs: '2',
|
||||
targets: { ie: 9, safari: '5.1' }
|
||||
}]
|
||||
]
|
||||
}
|
||||
}
|
||||
},
|
||||
{ test: /\.vue$/, use: 'vue-loader' }
|
||||
]
|
||||
},
|
||||
|
||||
Reference in New Issue
Block a user