Unverified Commit c6df1940 authored by Andreas Sonnleitner's avatar Andreas Sonnleitner Committed by GitHub

fix: migrate from reactivity transform and some fixes (#125)

parent 657c0b48
...@@ -5,7 +5,7 @@ const props = defineProps<{ ...@@ -5,7 +5,7 @@ const props = defineProps<{
maxPage: number maxPage: number
}>() }>()
const hasMore = $computed(() => props.page < props.maxPage) const hasMore = computed(() => props.page < props.maxPage)
</script> </script>
<template> <template>
......
import { WritableComputedOptions } from 'vue'
import { Item, User } from '~/types' import { Item, User } from '~/types'
export interface StoreState { export interface StoreState {
items: Record<number, Item> items: Record<number, Item>
comments: Record<number, Item[]> comments: Record<number, Item[]>
users: Record<number, User> users: Record<string, User>
feeds: Record<string, Record<number, number[]>> feeds: Record<string, Record<number, number[]>>
} }
...@@ -28,56 +29,56 @@ export function getFeed (state:StoreState, { feed, page }: FeedQuery) { ...@@ -28,56 +29,56 @@ export function getFeed (state:StoreState, { feed, page }: FeedQuery) {
} }
export function fetchFeed (query: FeedQuery) { export function fetchFeed (query: FeedQuery) {
const state = $(useStore()) const state = useStore()
const { feed, page } = query const { feed, page } = query
return reactiveLoad<Item[]>( return reactiveLoad<Item[]>(
() => getFeed(state, query), () => getFeed(state.value, query),
(items) => { (items) => {
const ids = items.map(item => item.id) const ids = items.map(item => item.id)
state.feeds[feed][page] = ids state.value.feeds[feed][page] = ids
items items
.filter(Boolean) .filter(Boolean)
.forEach((item) => { .forEach((item) => {
if (state.items[item.id]) { if (state.value.items[item.id]) {
Object.assign(state.items[item.id], item) Object.assign(state.value.items[item.id], item)
} else { } else {
state.items[item.id] = item state.value.items[item.id] = item
} }
}) })
}, },
() => $fetch('/api/hn/feeds', { params: { feed, page } }), () => $fetch('/api/hn/feeds', { params: { feed, page } }),
(state.feeds[feed][page] || []).map(id => state.items[id]) (state.value.feeds[feed][page] || []).map(id => state.value.items[id])
) )
} }
export function fetchItem (id: string) { export function fetchItem (id: number) {
const state = $(useStore()) const state = useStore()
return reactiveLoad<Item>( return reactiveLoad<Item>(
() => state.items[id], () => state.value.items[id],
(item) => { state.items[id] = item }, (item) => { state.value.items[id] = item },
() => $fetch('/api/hn/item', { params: { id } }) () => $fetch('/api/hn/item', { params: { id } })
) )
} }
export function fetchComments (id: string) { export function fetchComments (id: number) {
const state = $(useStore()) const state = useStore()
return reactiveLoad<Item[]>( return reactiveLoad<Item[]>(
() => state.comments[id], () => state.value.comments[id],
(comments) => { state.comments[id] = comments }, (comments) => { state.value.comments[id] = comments },
() => $fetch('/api/hn/item', { params: { id } }).then(i => i.comments) () => $fetch('/api/hn/item', { params: { id } }).then(i => i.comments!)
) )
} }
export function fetchUser (id: string) { export function fetchUser (id: string) {
const state = $(useStore()) const state = useStore()
return reactiveLoad<User>( return reactiveLoad<User>(
() => state.users[id], () => state.value.users[id],
(user) => { state.users[id] = user }, (user) => { state.value.users[id] = user },
() => $fetch('/api/hn/user', { params: { id } }) () => $fetch('/api/hn/user', { params: { id } })
) )
} }
...@@ -96,7 +97,7 @@ export async function reactiveLoad<T> ( ...@@ -96,7 +97,7 @@ export async function reactiveLoad<T> (
const data = computed({ const data = computed({
get, get,
set set
}) } as WritableComputedOptions<T | undefined>)
const loading = ref(false) const loading = ref(false)
if (data.value == null) { if (data.value == null) {
......
...@@ -3,8 +3,5 @@ export default defineNuxtConfig({ ...@@ -3,8 +3,5 @@ export default defineNuxtConfig({
plugins: { plugins: {
'postcss-nested': {} 'postcss-nested': {}
} }
},
experimental: {
reactivityTransform: true
} }
}) })
{ {
"name": "nuxt-hn", "name": "nuxt-hn",
"private": true, "private": true,
"packageManager": "pnpm@7.26.2", "packageManager": "pnpm@7.29.1",
"description": "Nuxt Hacker News", "description": "Nuxt Hacker News",
"author": "Evan You <yyx990803@gmail.com>", "author": "Evan You <yyx990803@gmail.com>",
"contributors": [ "contributors": [
...@@ -29,9 +29,10 @@ ...@@ -29,9 +29,10 @@
}, },
"devDependencies": { "devDependencies": {
"@nuxtjs/eslint-config-typescript": "^12.0.0", "@nuxtjs/eslint-config-typescript": "^12.0.0",
"eslint": "^8.33.0", "@types/node": "^18.14.6",
"nuxt": "^3.1.1", "eslint": "^8.35.0",
"postcss-nested": "^6.0.0", "nuxt": "^3.2.3",
"postcss-nested": "^6.0.1",
"typescript": "^4.9.5" "typescript": "^4.9.5"
} }
} }
...@@ -7,45 +7,45 @@ definePageMeta({ ...@@ -7,45 +7,45 @@ definePageMeta({
const route = useRoute() const route = useRoute()
const router = useRouter() const router = useRouter()
const page = $computed(() => +route.params.page || 1) const page = computed(() => +route.params.page || 1)
const feed = $computed(() => route.params.feed as string) const feed = computed(() => route.params.feed as keyof typeof feedsInfo)
const isValidFeed = $computed(() => !!feedsInfo[feed]) const isValidFeed = computed(() => !!feedsInfo[feed.value])
// const transition = $ref('slide-right') // const transition = ref('slide-right')
const pageNo = $computed(() => Number(page) || 1) const pageNo = computed(() => Number(page.value) || 1)
const displayedPage = ref(pageNo) const displayedPage = ref(pageNo.value)
useHead({ useHead({
title: feedsInfo[feed]?.title title: feedsInfo[feed.value]?.title
}) })
const state = $(useStore()) const state = useStore()
if (isValidFeed) { if (isValidFeed.value) {
await fetchFeed({ page: pageNo, feed }) await fetchFeed({ page: pageNo.value, feed: feed.value })
} }
const items = $computed(() => getFeed(state, { page: pageNo, feed }) || []) const items = computed(() => getFeed(state.value, { page: pageNo.value, feed: feed.value }) || [])
const loading = $computed(() => items.length === 0) const loading = computed(() => items.value.length === 0)
const maxPage = $computed(() => { const maxPage = computed(() => {
return +(feedsInfo[feed]?.pages) || 0 return +(feedsInfo[feed.value]?.pages) || 0
}) })
function pageChanged (to: number, from = -1) { function pageChanged (to: number, _from = -1) {
if (!isValidFeed) { return } if (!isValidFeed.value) { return }
if (to <= 0 || to > maxPage) { if (to <= 0 || to > maxPage.value) {
router.replace(`/${feed}/1`) router.replace(`/${feed.value}/1`)
return return
} }
// Prefetch next page // Prefetch next page
fetchFeed({ fetchFeed({
feed, feed: feed.value,
page: page + 1 page: page.value + 1
}).catch(() => {}) }).catch(() => {})
// transition = from === -1 // transition.value = from === -1
// ? null // ? null
// : to > from // : to > from
// ? 'slide-left' // ? 'slide-left'
...@@ -54,8 +54,8 @@ function pageChanged (to: number, from = -1) { ...@@ -54,8 +54,8 @@ function pageChanged (to: number, from = -1) {
displayedPage.value = to displayedPage.value = to
} }
onMounted(() => pageChanged(page)) onMounted(() => pageChanged(page.value))
watch(() => page, (to, old) => pageChanged(to, old)) watch(page, (to, old) => pageChanged(to, old))
</script> </script>
<template> <template>
......
...@@ -2,14 +2,14 @@ ...@@ -2,14 +2,14 @@
import { host, timeAgo, isAbsolute } from '~/composables/utils' import { host, timeAgo, isAbsolute } from '~/composables/utils'
const route = useRoute() const route = useRoute()
const id = $computed(() => route.params.id as string) const id = computed(() => +route.params.id)
const [resultItem, resultComments] = await Promise.all([fetchItem(id), fetchComments(id)]) const [resultItem, resultComments] = await Promise.all([fetchItem(id.value), fetchComments(id.value)])
const { data: item } = $(resultItem) const { data: item } = toRefs(resultItem)
const { data: comments, loading: commentsLoading } = $(resultComments) const { data: comments, loading: commentsLoading } = toRefs(resultComments)
useHead({ useHead({
title: item?.title title: item.value?.title
}) })
</script> </script>
......
...@@ -2,16 +2,16 @@ ...@@ -2,16 +2,16 @@
import { timeAgo } from '~/composables/utils' import { timeAgo } from '~/composables/utils'
const route = useRoute() const route = useRoute()
const id = $computed(() => route.params.id as string) const id = computed(() => route.params.id as string)
const result = await fetchUser(id) const result = await fetchUser(id.value)
const { data: user, loading } = $(result) const { data: user, loading } = toRefs(result)
useHead({ useHead({
title: loading title: loading.value
? 'Loading' ? 'Loading'
: user : user.value
? user.id ? user.value.id
: 'User not found' : 'User not found'
}) })
</script> </script>
......
This source diff could not be displayed because it is too large. You can view the blob instead.
import { createError } from 'h3'
import { $fetch } from 'ofetch' import { $fetch } from 'ofetch'
import { getQuery, parseURL } from 'ufo'
import { feedsInfo, validFeeds } from '~/composables/api' import { feedsInfo, validFeeds } from '~/composables/api'
import { baseURL } from '~/server/constants' import { baseURL } from '~/server/constants'
import { configureSWRHeaders } from '~/server/swr'
const feedUrls: Record<keyof typeof feedsInfo, string> = { const feedUrls: Record<keyof typeof feedsInfo, string> = {
ask: 'askstories', ask: 'askstories',
...@@ -15,7 +12,7 @@ const feedUrls: Record<keyof typeof feedsInfo, string> = { ...@@ -15,7 +12,7 @@ const feedUrls: Record<keyof typeof feedsInfo, string> = {
news: 'topstories' news: 'topstories'
} }
async function fetchFeed (feed: string, page = '1') { async function fetchFeed (feed: keyof typeof feedsInfo, page = '1') {
const { fetchItem } = await import('./item') const { fetchItem } = await import('./item')
const entries = Object.values( const entries = Object.values(
await $fetch(`${baseURL}/${feedUrls[feed]}.json`) await $fetch(`${baseURL}/${feedUrls[feed]}.json`)
...@@ -23,10 +20,9 @@ async function fetchFeed (feed: string, page = '1') { ...@@ -23,10 +20,9 @@ async function fetchFeed (feed: string, page = '1') {
return Promise.all(entries.map(id => fetchItem(id))) return Promise.all(entries.map(id => fetchItem(id)))
} }
export default defineEventHandler(({ req, res }) => { export default defineEventHandler((event) => {
configureSWRHeaders(res) configureSWRHeaders(event)
const { search } = parseURL(req.url) const { page = '1', feed = 'news' } = getQuery(event) as { page: string, feed: keyof typeof feedsInfo }
const { page = '1', feed = 'news' } = getQuery(search) as { page: string, feed: string }
if (!validFeeds.includes(feed) || String(Number(page)) !== page) { if (!validFeeds.includes(feed) || String(Number(page)) !== page) {
throw createError({ throw createError({
......
import { createError } from 'h3'
import { $fetch } from 'ofetch' import { $fetch } from 'ofetch'
import { parseURL, getQuery } from 'ufo'
import { baseURL } from '~/server/constants' import { baseURL } from '~/server/constants'
import { Item } from '~/types' import { Item } from '~/types'
import { configureSWRHeaders } from '~/server/swr'
export async function fetchItem ( export async function fetchItem (
id: string, id: string,
...@@ -31,10 +28,9 @@ export async function fetchItem ( ...@@ -31,10 +28,9 @@ export async function fetchItem (
} }
} }
export default defineEventHandler(({ req, res }) => { export default defineEventHandler((event) => {
configureSWRHeaders(res) configureSWRHeaders(event)
const { search } = parseURL(req.url) const { id } = getQuery(event) as { id?: string }
const { id } = getQuery(search) as { id: string }
if (!id) { if (!id) {
throw createError({ throw createError({
......
import { createError } from 'h3'
import { $fetch } from 'ofetch' import { $fetch } from 'ofetch'
import { parseURL, getQuery } from 'ufo'
import { User } from '~/types' import { User } from '~/types'
import { baseURL } from '~/server/constants' import { baseURL } from '~/server/constants'
import { configureSWRHeaders } from '~/server/swr'
async function fetchUser (id: string): Promise<User> { async function fetchUser (id: string): Promise<User> {
const user = await $fetch(`${baseURL}/user/${id}.json`) const user = await $fetch(`${baseURL}/user/${id}.json`)
...@@ -15,10 +12,9 @@ async function fetchUser (id: string): Promise<User> { ...@@ -15,10 +12,9 @@ async function fetchUser (id: string): Promise<User> {
} }
} }
export default defineEventHandler(({ req, res }) => { export default defineEventHandler((event) => {
configureSWRHeaders(res) configureSWRHeaders(event)
const { search } = parseURL(req.url) const { id } = getQuery(event) as { id?: string }
const { id } = getQuery(search) as { id: string }
if (!id) { if (!id) {
throw createError({ throw createError({
......
...@@ -2,6 +2,6 @@ export default defineEventHandler((event) => { ...@@ -2,6 +2,6 @@ export default defineEventHandler((event) => {
const query = getQuery(event) const query = getQuery(event)
if (typeof query.csr !== 'undefined') { if (typeof query.csr !== 'undefined') {
event.req.headers['x-nuxt-no-ssr'] = 'true' event.node.req.headers['x-nuxt-no-ssr'] = 'true'
} }
}) })
import type { H3Response } from 'h3'
export function configureSWRHeaders (res: H3Response) {
res.setHeader('Cache-Control', 's-maxage=10, stale-while-revalidate')
}
import { H3Event } from 'h3'
export function configureSWRHeaders (event: H3Event) {
setHeader(event, 'Cache-Control', 's-maxage=10, stale-while-revalidate')
}
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment