mirror of https://github.com/YunYouJun/valaxy
feat: hot reload for usePostList, useTag, useCategory, close #106
This commit is contained in:
parent
fabe7e2a66
commit
c90c7834fa
|
@ -9,6 +9,12 @@ top: 98
|
|||
|
||||
首先,我们来介绍一下 Valaxy 有哪些便捷的特性。
|
||||
|
||||
## 热更新
|
||||
|
||||
最值得一提的是,Valaxy 从配置到文章内容、动画到全局的标签、分类,全部都是支持热更新的!局部的!
|
||||
|
||||
譬如,你修改了 `valaxy.config.ts`/`site.config.ts` 或是 `xxx.md` 文章中的内容或 `frontmatter`(`tags`/`categories`)所有的变动会立刻显示在预览界面上,无需手动刷新。同时热更新也是局部的,它只变动有修改的地方,不会重新刷新整个页面。
|
||||
|
||||
## UnoCSS
|
||||
|
||||
> 内置的类 TailwindCSS 的工具类(基于 [UnoCSS](https://github.com/unocss/unocss))。
|
||||
|
|
|
@ -2,18 +2,18 @@
|
|||
import { computed } from 'vue'
|
||||
import { useRoute } from 'vue-router'
|
||||
|
||||
import { useFrontmatter, usePostList } from 'valaxy'
|
||||
import { useFrontmatter, useSiteStore } from 'valaxy'
|
||||
const frontmatter = useFrontmatter()
|
||||
|
||||
const route = useRoute()
|
||||
const posts = usePostList()
|
||||
const site = useSiteStore()
|
||||
|
||||
function findCurrentIndex() {
|
||||
return posts.value.findIndex(p => p.href === route.path)
|
||||
return site.postList.findIndex(p => p.href === route.path)
|
||||
}
|
||||
|
||||
const nextPost = computed(() => posts.value[findCurrentIndex() - 1])
|
||||
const prevPost = computed(() => posts.value[findCurrentIndex() + 1])
|
||||
const nextPost = computed(() => site.postList[findCurrentIndex() - 1])
|
||||
const prevPost = computed(() => site.postList[findCurrentIndex() + 1])
|
||||
</script>
|
||||
|
||||
<template>
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
<script setup lang="ts">
|
||||
import { computed } from 'vue'
|
||||
import type { Post } from 'valaxy'
|
||||
import { usePostList } from 'valaxy'
|
||||
import { useSiteStore } from 'valaxy'
|
||||
|
||||
const props = withDefaults(defineProps<{
|
||||
type?: string
|
||||
|
@ -11,8 +11,8 @@ const props = withDefaults(defineProps<{
|
|||
curPage: 1,
|
||||
})
|
||||
|
||||
const routes = usePostList({ type: props.type || '' })
|
||||
const posts = computed(() => props.posts || routes.value)
|
||||
const site = useSiteStore()
|
||||
const posts = computed(() => props.posts || site.postList)
|
||||
</script>
|
||||
|
||||
<template>
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
<script setup lang="ts">
|
||||
import { computed, ref } from 'vue'
|
||||
import { useSiteStore } from 'valaxy'
|
||||
import type { Post } from 'valaxy'
|
||||
import { usePostList } from 'valaxy'
|
||||
|
||||
const props = withDefaults(defineProps<{
|
||||
type?: string
|
||||
|
@ -11,10 +11,11 @@ const props = withDefaults(defineProps<{
|
|||
curPage: 1,
|
||||
})
|
||||
|
||||
const site = useSiteStore()
|
||||
|
||||
const pageSize = ref(7)
|
||||
|
||||
const routes = usePostList({ type: props.type || '' })
|
||||
const posts = computed(() => props.posts || routes.value)
|
||||
const posts = computed(() => props.posts || site.postList)
|
||||
const displayedPosts = computed(() => posts.value.slice((props.curPage - 1) * pageSize.value, props.curPage * pageSize.value))
|
||||
</script>
|
||||
|
||||
|
|
|
@ -1,12 +1,13 @@
|
|||
<script lang="ts" setup>
|
||||
import { useCategory, usePostList, useTag } from 'valaxy'
|
||||
import { useCategory, useSiteStore, useTag } from 'valaxy'
|
||||
import { useI18n } from 'vue-i18n'
|
||||
import { useThemeConfig } from '../composables'
|
||||
|
||||
const { t } = useI18n()
|
||||
|
||||
const site = useSiteStore()
|
||||
|
||||
const themeConfig = useThemeConfig()
|
||||
const posts = usePostList()
|
||||
const categories = useCategory()
|
||||
const tags = useTag()
|
||||
</script>
|
||||
|
@ -19,7 +20,7 @@ const tags = useTag()
|
|||
|
||||
<router-link class="site-link-item" to="/archives/" :title="t('menu.archives')">
|
||||
<div class="icon" i-ri-archive-line />
|
||||
<span class="count">{{ posts.length }}</span>
|
||||
<span class="count">{{ site.postList.length }}</span>
|
||||
</router-link>
|
||||
<router-link class="site-link-item" to="/categories/" :title="t('menu.categories')">
|
||||
<div class="icon" i-ri-folder-2-line />
|
||||
|
|
|
@ -1,13 +1,13 @@
|
|||
<script lang="ts" setup>
|
||||
import { useFrontmatter, usePostList, usePostTitle } from 'valaxy'
|
||||
import { useFrontmatter, usePostTitle, useSiteStore } from 'valaxy'
|
||||
import { useI18n } from 'vue-i18n'
|
||||
|
||||
const { t } = useI18n()
|
||||
|
||||
const frontmatter = useFrontmatter()
|
||||
const postList = usePostList()
|
||||
|
||||
const title = usePostTitle(frontmatter)
|
||||
const site = useSiteStore()
|
||||
</script>
|
||||
|
||||
<template>
|
||||
|
@ -17,7 +17,7 @@ const title = usePostTitle(frontmatter)
|
|||
</template>
|
||||
<template #main-content>
|
||||
<router-view />
|
||||
<YunPostCollapse :posts="postList" />
|
||||
<YunPostCollapse :posts="site.postList" />
|
||||
</template>
|
||||
</Layout>
|
||||
</template>
|
||||
|
|
|
@ -1,20 +1,21 @@
|
|||
<script lang="ts" setup>
|
||||
import { computed, ref } from 'vue'
|
||||
import { useCategory, useFrontmatter, useInvisibleElement, usePostList, usePostTitle } from 'valaxy'
|
||||
import { useCategory, useFrontmatter, useInvisibleElement, usePostTitle, useSiteStore } from 'valaxy'
|
||||
import { useI18n } from 'vue-i18n'
|
||||
import { useRoute, useRouter } from 'vue-router'
|
||||
|
||||
const { t } = useI18n()
|
||||
|
||||
const site = useSiteStore()
|
||||
|
||||
const frontmatter = useFrontmatter()
|
||||
const categories = useCategory()
|
||||
|
||||
const route = useRoute()
|
||||
const curCategory = computed(() => (route.query.category as string || ''))
|
||||
|
||||
const postList = usePostList()
|
||||
const posts = computed(() => {
|
||||
const list = postList.value.filter((post) => {
|
||||
const list = site.postList.filter((post) => {
|
||||
if (post.categories && curCategory.value !== 'Uncategorized') {
|
||||
if (typeof post.categories === 'string')
|
||||
return post.categories === curCategory.value
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
<script lang="ts" setup>
|
||||
import { useFrontmatter, useInvisibleElement, usePostList, usePostTitle, useTags } from 'valaxy'
|
||||
import { useFrontmatter, useInvisibleElement, usePostTitle, useSiteStore, useTags } from 'valaxy'
|
||||
import { useI18n } from 'vue-i18n'
|
||||
import { computed, ref } from 'vue'
|
||||
import { useRoute, useRouter } from 'vue-router'
|
||||
|
@ -16,11 +16,11 @@ const { tags, getTagStyle } = useTags({
|
|||
primary: themeConfig.value.colors.primary,
|
||||
})
|
||||
|
||||
const postList = usePostList()
|
||||
const curTag = computed(() => route.query.tag as string || '')
|
||||
const site = useSiteStore()
|
||||
|
||||
const posts = computed(() => {
|
||||
const list = postList.value.filter((post) => {
|
||||
const list = site.postList.filter((post) => {
|
||||
if (post.tags) {
|
||||
if (typeof post.tags === 'string')
|
||||
return post.tags === curTag.value
|
||||
|
|
|
@ -0,0 +1,10 @@
|
|||
import { acceptHMRUpdate, defineStore } from 'pinia'
|
||||
|
||||
export const useYunAppStore = defineStore('yun-app', () => {
|
||||
// global cache for yun
|
||||
|
||||
return {}
|
||||
})
|
||||
|
||||
if (import.meta.hot)
|
||||
import.meta.hot.accept(acceptHMRUpdate(useYunAppStore, import.meta.hot))
|
|
@ -1,6 +1,5 @@
|
|||
import { unref } from 'vue'
|
||||
import type { Post } from '../..'
|
||||
import { usePostList } from './post'
|
||||
import { useSiteStore } from '../stores'
|
||||
|
||||
export interface BaseCategory {
|
||||
total: number
|
||||
|
@ -26,8 +25,10 @@ export const isParentCategory = (category: any): category is ParentCategory => c
|
|||
* @returns
|
||||
*/
|
||||
export function useCategory(category?: string, posts: Post[] = []): ParentCategory {
|
||||
if (!posts.length)
|
||||
posts = unref(usePostList())
|
||||
if (!posts.length) {
|
||||
const site = useSiteStore()
|
||||
posts = site.postList
|
||||
}
|
||||
|
||||
const categoryMap: Category = {
|
||||
total: posts.length,
|
||||
|
|
|
@ -4,6 +4,7 @@ import { useRoute, useRouter } from 'vue-router'
|
|||
import { useI18n } from 'vue-i18n'
|
||||
import type { Post } from '../..'
|
||||
import { sortByDate } from '../utils'
|
||||
import { useSiteStore } from '../stores'
|
||||
|
||||
export const usePostTitle = (post: Ref<Post>) => {
|
||||
const { locale } = useI18n()
|
||||
|
@ -66,11 +67,11 @@ export function usePostList(params: {
|
|||
export function usePrevNext(path?: string) {
|
||||
const route = useRoute()
|
||||
const p = computed(() => path || route.path)
|
||||
const routes = usePostList()
|
||||
const site = useSiteStore()
|
||||
|
||||
const index = computed(() => {
|
||||
let order = -1
|
||||
routes.value.find((item, i) => {
|
||||
site.postList.find((item, i) => {
|
||||
if (item.path === p.value) {
|
||||
order = i
|
||||
return true
|
||||
|
@ -80,8 +81,8 @@ export function usePrevNext(path?: string) {
|
|||
return order
|
||||
})
|
||||
|
||||
const prev = computed(() => index.value - 1 >= 0 ? routes.value[index.value - 1] : null)
|
||||
const next = computed(() => index.value + 1 < routes.value.length ? routes.value[index.value + 1] : null)
|
||||
const prev = computed(() => index.value - 1 >= 0 ? site.postList[index.value - 1] : null)
|
||||
const next = computed(() => index.value + 1 < site.postList.length ? site.postList[index.value + 1] : null)
|
||||
|
||||
return [prev, next]
|
||||
}
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
import { TinyColor } from '@ctrl/tinycolor'
|
||||
import { computed } from 'vue'
|
||||
import type { Post } from '../..'
|
||||
import { usePostList } from './post'
|
||||
import { useSiteStore } from '../stores'
|
||||
|
||||
export type Tags = Map<string, {
|
||||
count: number
|
||||
|
@ -47,11 +47,11 @@ export function useTags(options: {
|
|||
* @returns
|
||||
*/
|
||||
export function useTag() {
|
||||
const posts = usePostList()
|
||||
const site = useSiteStore()
|
||||
|
||||
return computed(() => {
|
||||
const tagMap: Tags = new Map()
|
||||
posts.value.forEach((post: Post) => {
|
||||
site.postList.forEach((post: Post) => {
|
||||
if (post.tags) {
|
||||
let tags: string[]
|
||||
if (typeof post.tags === 'string')
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
export * from './config'
|
||||
|
||||
export * from './composables'
|
||||
export * from './stores/app'
|
||||
export * from './stores'
|
||||
|
||||
export * from './utils'
|
||||
|
||||
|
|
|
@ -71,6 +71,22 @@ function handleHMR(router: Router): void {
|
|||
// console.log(payload.pageData.headers)
|
||||
Object.assign(router.currentRoute.value.meta, payload.pageData)
|
||||
}
|
||||
else {
|
||||
// hot reload for global categories & tags
|
||||
let path = payload.path
|
||||
if (payload.path.endsWith('.md'))
|
||||
path = payload.path.slice(0, -3)
|
||||
|
||||
const routeName = path.split('/').slice(1).join('-')
|
||||
|
||||
if (!router.hasRoute(routeName))
|
||||
return
|
||||
|
||||
const route = router.getRoutes().find(r => r.name === routeName)!
|
||||
router.removeRoute(routeName)
|
||||
route.meta.frontmatter = payload.pageData.frontmatter
|
||||
router.addRoute(route)
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
|
|
@ -0,0 +1,2 @@
|
|||
export * from './app'
|
||||
export * from './site'
|
|
@ -0,0 +1,31 @@
|
|||
import { computed, ref } from 'vue'
|
||||
import { acceptHMRUpdate, defineStore } from 'pinia'
|
||||
import { usePostList } from '..'
|
||||
|
||||
/**
|
||||
* cache site global store
|
||||
* - post
|
||||
* - tag
|
||||
* - category
|
||||
*/
|
||||
export const useSiteStore = defineStore('site', () => {
|
||||
const reload = ref(1)
|
||||
const postList = computed(() => {
|
||||
if (reload.value)
|
||||
return usePostList().value
|
||||
else
|
||||
return usePostList().value
|
||||
})
|
||||
|
||||
// hot reload when save md
|
||||
import.meta.hot!.on('valaxy:pageData', () => {
|
||||
reload.value += 1
|
||||
})
|
||||
|
||||
return {
|
||||
postList,
|
||||
}
|
||||
})
|
||||
|
||||
if (import.meta.hot)
|
||||
import.meta.hot.accept(acceptHMRUpdate(useSiteStore, import.meta.hot))
|
Loading…
Reference in New Issue