From 571b67bc84cc8f34f9c506c708bd0dcc51daee34 Mon Sep 17 00:00:00 2001
From: avele <34437766+avele@users.noreply.github.com>
Date: Tue, 26 Nov 2019 17:21:20 +0400
Subject: [PATCH] Client preload rewritten (#417)
---
front/client/App.vue | 25 ++++--
.../components/ClientServerPrefetch.vue | 76 +++++++++++++++++++
front/client/components/SearchBar.vue | 30 ++++----
front/client/entry.client.ts | 67 ----------------
front/client/entry.server.ts | 2 +
front/client/page/CategoryPage.vue | 10 ---
front/client/page/Index.vue | 6 +-
front/client/page/Page404.vue | 4 -
front/client/page/SearchResults.vue | 23 ++----
front/client/router/index.ts | 41 ++++++++--
front/client/store/index.ts | 2 +-
front/client/store/modules/wiki.ts | 6 +-
front/index.html | 2 +-
13 files changed, 157 insertions(+), 137 deletions(-)
create mode 100644 front/client/components/ClientServerPrefetch.vue
diff --git a/front/client/App.vue b/front/client/App.vue
index 54ad1cf..261131a 100644
--- a/front/client/App.vue
+++ b/front/client/App.vue
@@ -2,7 +2,9 @@
-
+
+
+
@@ -14,24 +16,21 @@ import Component from 'vue-class-component'
import { Watch } from 'vue-property-decorator'
import AFooter from 'client/components/AFooter.vue'
import Toolbar from 'client/components/Toolbar.vue'
+import ClientServerPrefetch from 'client/components/ClientServerPrefetch.vue'
import * as nprogress from 'nprogress'
import 'nprogress/nprogress.css'
+import { getRouteDocumentTitle } from 'client/router'
nprogress.configure({ showSpinner: false })
@Component({
components: {
Toolbar,
- AFooter
+ AFooter,
+ ClientServerPrefetch
}
})
export default class RootComponent extends Vue {
- beforeMount () {
- // This package can only be loaded after mounted (on client only) cause it uses "document"
- // it is used in MarkdownEditor.vue and to make it work faster in that component we preload it here
- import('easymde')
- }
-
get isPageLoading () {
return this.$store.state.isPageLoading
}
@@ -39,6 +38,16 @@ export default class RootComponent extends Vue {
@Watch('isPageLoading')
toogleLoading (isPageLoading: boolean) {
isPageLoading ? nprogress.start() : nprogress.done()
+
+ if (!isPageLoading) {
+ document.title = getRouteDocumentTitle(this.$route)
+ }
+ }
+
+ beforeMount () {
+ // This package can only be loaded after mounted (on client only) cause it uses "document"
+ // it is used in MarkdownEditor.vue and to make it work faster in that component we preload it here
+ import('easymde')
}
}
diff --git a/front/client/components/ClientServerPrefetch.vue b/front/client/components/ClientServerPrefetch.vue
new file mode 100644
index 0000000..e975d0a
--- /dev/null
+++ b/front/client/components/ClientServerPrefetch.vue
@@ -0,0 +1,76 @@
+
\ No newline at end of file
diff --git a/front/client/components/SearchBar.vue b/front/client/components/SearchBar.vue
index 91adaf1..03841ef 100644
--- a/front/client/components/SearchBar.vue
+++ b/front/client/components/SearchBar.vue
@@ -1,7 +1,7 @@
@@ -25,7 +25,7 @@
size="0.9rem"
class="search-bar__input__clear-btn"
:color="inputIconsColor"
- v-show="searchInput"
+ v-show="searchQuery"
@click="clear"
>$vuetify.icons.times
@@ -40,19 +40,17 @@ import Component from 'vue-class-component'
export default class SearchBar extends Vue {
isFocused = false
+ searchQuery = this.$store.state.wiki.searchQuery
- processSearchInput () {
- this.$router.push({ name: 'SearchResults', query: { query: this.searchInput } })
- }
-
- // TODO investigate is using computed necessary here
- // TODO serever rendering doesnt render value in text field
- get searchInput () {
- return this.$store.state.wiki.searchInput
- }
-
- set searchInput (value) {
- this.$store.commit('wiki/setSearchInput', value)
+ processSearchQuery () {
+ const searchResultsRouteName = 'SearchResults'
+ const isSearchCurrentRoute = this.$route.name === searchResultsRouteName
+ // same commit statement called in router 'SearchResults' route's "beforeEnter", but if its same route its not called
+ // searchQuery is watched used in "SearchResults" component
+ if (isSearchCurrentRoute) {
+ this.$store.commit('wiki/setSearchQuery', this.searchQuery)
+ }
+ this.$router.push({ name: searchResultsRouteName, query: { query: this.searchQuery } })
}
get inputIconsColor () {
@@ -60,7 +58,7 @@ export default class SearchBar extends Vue {
}
clear () {
- this.searchInput = ''
+ this.searchQuery = ''
}
focus () {
diff --git a/front/client/entry.client.ts b/front/client/entry.client.ts
index bea00d6..03d7952 100644
--- a/front/client/entry.client.ts
+++ b/front/client/entry.client.ts
@@ -1,5 +1,3 @@
-import _get from 'lodash/get'
-
import { createApp } from './app'
const { app, router, store } = createApp()
@@ -20,70 +18,5 @@ if (store.state.is404) {
}
router.onReady(() => {
- registerRouterHooks()
app.$mount('#app')
})
-
-function registerRouterHooks () {
- router.beforeEach(async (to, from, next) => {
- // This case handles navigation to anchors on same page
- if (to.path === from.path) {
- next()
- return
- }
-
- store.commit('tooglePageLoading')
- if (!to.matched.length) {
- store.commit('tooglePageLoading')
- next()
- return
- }
- try {
- const propsOption = to.matched[0].props.default
- const props = propsOption
- ? typeof propsOption === 'function'
- ? propsOption(to)
- : typeof propsOption === 'object'
- ? propsOption
- : to.params
- : {}
- const routeComponent = to.matched[0].components.default
- const matchedRootComponent = routeComponent.cid // Check if component already imported
- ? routeComponent
- : (await routeComponent()).default
- const matchedComponentsAndChildren = getComponentAndItsChildren(matchedRootComponent)
- await Promise.all(matchedComponentsAndChildren.map(component => {
- const serverPrefetch = component.options.serverPrefetch && component.options.serverPrefetch[0]
- if (typeof serverPrefetch === 'function') {
- return serverPrefetch.call({
- $store: store,
- $router: router,
- ...component.options.methods,
- ...props
- })
- }
- }))
- next()
- } finally {
- store.commit('tooglePageLoading')
- }
- })
-}
-
-function getComponentAndItsChildren (component, result?) {
- if (!result) {
- result = []
- }
- if (!component.options) {
- return result
- }
- if (!result.includes(component)) {
- result.push(component)
- }
- const children = Object.values(component.options.components)
- // Parent component is also presents in components object
- .filter(x => x !== component)
- children.forEach(x => getComponentAndItsChildren(x, result))
-
- return result
-}
diff --git a/front/client/entry.server.ts b/front/client/entry.server.ts
index 11615d5..d846959 100644
--- a/front/client/entry.server.ts
+++ b/front/client/entry.server.ts
@@ -1,5 +1,6 @@
import _get from 'lodash/get'
import { createApp } from './app'
+import { getRouteDocumentTitle } from './router'
export default async context => {
return new Promise((resolve, reject) => {
@@ -19,6 +20,7 @@ export default async context => {
context.rendered = () => {
context.state = store.state
}
+ context.title = getRouteDocumentTitle(router.currentRoute)
resolve(app)
}, reject)
})
diff --git a/front/client/page/CategoryPage.vue b/front/client/page/CategoryPage.vue
index da16b25..5b909b7 100644
--- a/front/client/page/CategoryPage.vue
+++ b/front/client/page/CategoryPage.vue
@@ -28,18 +28,8 @@ export default class CategoryPage extends Vue {
return this.$store.state.category.category
}
- beforeMount () {
- this.$watch('category', this.setDocumentTitle, { immediate: true })
- }
-
beforeDestroy () {
this.$store.commit('category/setCategory', null)
}
-
- setDocumentTitle (category) {
- document.title = category
- ? `${category.title} – Aelve Guide`
- : 'Aelve Guide'
- }
}
diff --git a/front/client/page/Index.vue b/front/client/page/Index.vue
index 78e8262..3506934 100644
--- a/front/client/page/Index.vue
+++ b/front/client/page/Index.vue
@@ -12,9 +12,5 @@ import Component from 'vue-class-component'
Categories
}
})
-export default class Index extends Vue {
- beforeMount () {
- document.title = `Aelve Guide`
- }
-}
+export default class Index extends Vue {}
diff --git a/front/client/page/Page404.vue b/front/client/page/Page404.vue
index 7ab8063..1fc3c0b 100644
--- a/front/client/page/Page404.vue
+++ b/front/client/page/Page404.vue
@@ -20,10 +20,6 @@ export default class Page404 extends Vue {
beforeDestroy () {
this.$store.commit('set404', false)
}
-
- beforeMount () {
- document.title = `Error 404 – Aelve Guide`
- }
}
diff --git a/front/client/page/SearchResults.vue b/front/client/page/SearchResults.vue
index 508c4ba..5afd698 100644
--- a/front/client/page/SearchResults.vue
+++ b/front/client/page/SearchResults.vue
@@ -76,7 +76,7 @@
diff --git a/front/client/router/index.ts b/front/client/router/index.ts
index d7595c8..6b63f66 100644
--- a/front/client/router/index.ts
+++ b/front/client/router/index.ts
@@ -1,5 +1,6 @@
import Router from 'vue-router'
import categoryPathToId from 'client/helpers/categoryPathToId'
+import _get from 'lodash/get'
function createRouter (store) {
return new Router({
@@ -32,23 +33,53 @@ function createRouter (store) {
path: '/haskell/:category',
name: 'Category',
component: () => import('../page/CategoryPage.vue'),
- props: (route) => ({ categoryId: categoryPathToId(route.params.category) })
+ props: (route) => ({ categoryId: categoryPathToId(route.params.category) }),
+ meta: {
+ documentTitle: () => {
+ const category = store.state.category.category
+ return category
+ ? `${category.title} – Aelve Guide`
+ : 'Aelve Guide'
+ }
+ }
},
{
path: '/haskell/search/results/',
name: 'SearchResults',
component: () => import('../page/SearchResults.vue'),
- props: (route) => ({ query: route.query.query })
+ beforeEnter: (to, from, next) => {
+ store.commit('wiki/setSearchQuery', to.query.query)
+ next()
+ },
+ meta: {
+ documentTitle: (route) => `${route.query.query} – Search results – Aelve Guide`,
+ }
},
{
path: '*',
name: 'Page404',
- component: () => import('../page/Page404.vue')
+ component: () => import('../page/Page404.vue'),
+ meta: {
+ documentTitle: 'Error 404 – Aelve Guide'
+ }
}
]
})
}
-export {
- createRouter
+function getRouteDocumentTitle (route) {
+ const defaultTitle = 'Aelve Guide'
+ const routeDocumentTitle = _get(route, 'meta.documentTitle')
+ if (routeDocumentTitle) {
+ return typeof routeDocumentTitle === 'function'
+ ? routeDocumentTitle(route)
+ : routeDocumentTitle
+ } else {
+ return defaultTitle
+ }
+}
+
+export {
+ createRouter,
+ getRouteDocumentTitle
}
diff --git a/front/client/store/index.ts b/front/client/store/index.ts
index e73ae98..099fdfa 100644
--- a/front/client/store/index.ts
+++ b/front/client/store/index.ts
@@ -11,7 +11,7 @@ function createStore () {
},
actions: {},
mutations: {
- tooglePageLoading (state) {
+ togglePageLoading (state) {
state.isPageLoading = !state.isPageLoading
},
set404 (state, val) {
diff --git a/front/client/store/modules/wiki.ts b/front/client/store/modules/wiki.ts
index 32368ed..51b1ef8 100644
--- a/front/client/store/modules/wiki.ts
+++ b/front/client/store/modules/wiki.ts
@@ -4,12 +4,12 @@ import { set } from '../helpers'
interface IWikiState {
searchResults: any[],
- searchInput: string
+ searchQuery: string
}
const state = (): IWikiState => ({
searchResults: [],
- searchInput: ''
+ searchQuery: ''
})
const getters: GetterTree = {}
@@ -23,7 +23,7 @@ const actions: ActionTree = {
const mutations: MutationTree = {
setSearchResults: set('searchResults'),
- setSearchInput: set('searchInput')
+ setSearchQuery: set('searchQuery')
}
const wiki: Module = {
diff --git a/front/index.html b/front/index.html
index 2c36e3d..343a188 100644
--- a/front/index.html
+++ b/front/index.html
@@ -3,7 +3,7 @@
- Aelve Guide
+ {{ title }}