feat: hot reload for usePostList, useTag, useCategory, close #106

This commit is contained in:
YunYouJun 2023-01-27 01:45:33 +08:00
parent fabe7e2a66
commit c90c7834fa
16 changed files with 105 additions and 35 deletions

View File

@ -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))。

View File

@ -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>

View File

@ -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>

View File

@ -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>

View File

@ -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 />

View File

@ -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>

View File

@ -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

View File

@ -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

View File

@ -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))

View File

@ -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,

View File

@ -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]
}

View File

@ -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')

View File

@ -1,7 +1,7 @@
export * from './config'
export * from './composables'
export * from './stores/app'
export * from './stores'
export * from './utils'

View File

@ -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)
}
})
}
}

View File

@ -0,0 +1,2 @@
export * from './app'
export * from './site'

View File

@ -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))