perf(frontend): reduce api requests for non-logged-in enviroment (#15001)

* wip

* Update CHANGELOG.md

* wip
This commit is contained in:
syuilo 2024-11-21 07:58:34 +09:00 committed by GitHub
parent 4603ab67bb
commit f0c3a4cc0b
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
7 changed files with 77 additions and 7 deletions

View File

@ -30,6 +30,7 @@
(Based on https://activitypub.software/TransFem-org/Sharkey/-/merge_requests/663) (Based on https://activitypub.software/TransFem-org/Sharkey/-/merge_requests/663)
- Enhance: サイドバーを簡単に展開・折りたたみできるように ( #14981 ) - Enhance: サイドバーを簡単に展開・折りたたみできるように ( #14981 )
- Enhance: リノートメニューに「リノートの詳細」を追加 - Enhance: リノートメニューに「リノートの詳細」を追加
- Enhance: 非ログイン状態でMisskeyを開いた際のパフォーマンスを向上
- Fix: 通知の範囲指定の設定項目が必要ない通知設定でも範囲指定の設定がでている問題を修正 - Fix: 通知の範囲指定の設定項目が必要ない通知設定でも範囲指定の設定がでている問題を修正
- Fix: Turnstileが失敗・期限切れした際にも成功扱いとなってしまう問題を修正 - Fix: Turnstileが失敗・期限切れした際にも成功扱いとなってしまう問題を修正
(Cherry-picked from https://github.com/MisskeyIO/misskey/pull/768) (Cherry-picked from https://github.com/MisskeyIO/misskey/pull/768)

View File

@ -559,7 +559,7 @@ export class ClientServerService {
} }
}); });
//#region SSR (for crawlers) //#region SSR
// User // User
fastify.get<{ Params: { user: string; sub?: string; } }>('/@:user/:sub?', async (request, reply) => { fastify.get<{ Params: { user: string; sub?: string; } }>('/@:user/:sub?', async (request, reply) => {
const { username, host } = Acct.parse(request.params.user); const { username, host } = Acct.parse(request.params.user);
@ -584,11 +584,17 @@ export class ClientServerService {
reply.header('X-Robots-Tag', 'noimageai'); reply.header('X-Robots-Tag', 'noimageai');
reply.header('X-Robots-Tag', 'noai'); reply.header('X-Robots-Tag', 'noai');
} }
const _user = await this.userEntityService.pack(user);
return await reply.view('user', { return await reply.view('user', {
user, profile, me, user, profile, me,
avatarUrl: user.avatarUrl ?? this.userEntityService.getIdenticonUrl(user), avatarUrl: user.avatarUrl ?? this.userEntityService.getIdenticonUrl(user),
sub: request.params.sub, sub: request.params.sub,
...await this.generateCommonPugData(this.meta), ...await this.generateCommonPugData(this.meta),
clientCtx: htmlSafeJsonStringify({
user: _user,
}),
}); });
} else { } else {
// リモートユーザーなので // リモートユーザーなので
@ -641,6 +647,9 @@ export class ClientServerService {
// TODO: Let locale changeable by instance setting // TODO: Let locale changeable by instance setting
summary: getNoteSummary(_note), summary: getNoteSummary(_note),
...await this.generateCommonPugData(this.meta), ...await this.generateCommonPugData(this.meta),
clientCtx: htmlSafeJsonStringify({
note: _note,
}),
}); });
} else { } else {
return await renderBase(reply); return await renderBase(reply);
@ -729,6 +738,9 @@ export class ClientServerService {
profile, profile,
avatarUrl: _clip.user.avatarUrl, avatarUrl: _clip.user.avatarUrl,
...await this.generateCommonPugData(this.meta), ...await this.generateCommonPugData(this.meta),
clientCtx: htmlSafeJsonStringify({
clip: _clip,
}),
}); });
} else { } else {
return await renderBase(reply); return await renderBase(reply);

View File

@ -74,6 +74,9 @@ html
script(type='application/json' id='misskey_meta' data-generated-at=now) script(type='application/json' id='misskey_meta' data-generated-at=now)
!= metaJson != metaJson
script(type='application/json' id='misskey_clientCtx' data-generated-at=now)
!= clientCtx
script script
include ../boot.js include ../boot.js

View File

@ -33,25 +33,28 @@ SPDX-License-Identifier: AGPL-3.0-only
<script lang="ts" setup> <script lang="ts" setup>
import { computed, watch, provide, ref } from 'vue'; import { computed, watch, provide, ref } from 'vue';
import * as Misskey from 'misskey-js'; import * as Misskey from 'misskey-js';
import { url } from '@@/js/config.js';
import type { MenuItem } from '@/types/menu.js';
import MkNotes from '@/components/MkNotes.vue'; import MkNotes from '@/components/MkNotes.vue';
import { $i } from '@/account.js'; import { $i } from '@/account.js';
import { i18n } from '@/i18n.js'; import { i18n } from '@/i18n.js';
import * as os from '@/os.js'; import * as os from '@/os.js';
import { misskeyApi } from '@/scripts/misskey-api.js'; import { misskeyApi } from '@/scripts/misskey-api.js';
import { definePageMetadata } from '@/scripts/page-metadata.js'; import { definePageMetadata } from '@/scripts/page-metadata.js';
import { url } from '@@/js/config.js';
import MkButton from '@/components/MkButton.vue'; import MkButton from '@/components/MkButton.vue';
import { clipsCache } from '@/cache.js'; import { clipsCache } from '@/cache.js';
import { isSupportShare } from '@/scripts/navigator.js'; import { isSupportShare } from '@/scripts/navigator.js';
import { copyToClipboard } from '@/scripts/copy-to-clipboard.js'; import { copyToClipboard } from '@/scripts/copy-to-clipboard.js';
import { genEmbedCode } from '@/scripts/get-embed-code.js'; import { genEmbedCode } from '@/scripts/get-embed-code.js';
import type { MenuItem } from '@/types/menu.js'; import { getServerContext } from '@/server-context.js';
const CTX_CLIP = getServerContext('clip');
const props = defineProps<{ const props = defineProps<{
clipId: string, clipId: string,
}>(); }>();
const clip = ref<Misskey.entities.Clip | null>(null); const clip = ref<Misskey.entities.Clip | null>(CTX_CLIP);
const favorited = ref(false); const favorited = ref(false);
const pagination = { const pagination = {
endpoint: 'clips/notes' as const, endpoint: 'clips/notes' as const,
@ -64,6 +67,11 @@ const pagination = {
const isOwned = computed<boolean | null>(() => $i && clip.value && ($i.id === clip.value.userId)); const isOwned = computed<boolean | null>(() => $i && clip.value && ($i.id === clip.value.userId));
watch(() => props.clipId, async () => { watch(() => props.clipId, async () => {
if (CTX_CLIP && CTX_CLIP.id === props.clipId) {
clip.value = CTX_CLIP;
return;
}
clip.value = await misskeyApi('clips/show', { clip.value = await misskeyApi('clips/show', {
clipId: props.clipId, clipId: props.clipId,
}); });

View File

@ -62,13 +62,16 @@ import { dateString } from '@/filters/date.js';
import MkClipPreview from '@/components/MkClipPreview.vue'; import MkClipPreview from '@/components/MkClipPreview.vue';
import { defaultStore } from '@/store.js'; import { defaultStore } from '@/store.js';
import { pleaseLogin } from '@/scripts/please-login.js'; import { pleaseLogin } from '@/scripts/please-login.js';
import { getServerContext } from '@/server-context.js';
const CTX_NOTE = getServerContext('note');
const props = defineProps<{ const props = defineProps<{
noteId: string; noteId: string;
initialTab?: string; initialTab?: string;
}>(); }>();
const note = ref<null | Misskey.entities.Note>(); const note = ref<null | Misskey.entities.Note>(CTX_NOTE);
const clips = ref<Misskey.entities.Clip[]>(); const clips = ref<Misskey.entities.Clip[]>();
const showPrev = ref<'user' | 'channel' | false>(false); const showPrev = ref<'user' | 'channel' | false>(false);
const showNext = ref<'user' | 'channel' | false>(false); const showNext = ref<'user' | 'channel' | false>(false);
@ -116,6 +119,12 @@ function fetchNote() {
showPrev.value = false; showPrev.value = false;
showNext.value = false; showNext.value = false;
note.value = null; note.value = null;
if (CTX_NOTE && CTX_NOTE.id === props.noteId) {
note.value = CTX_NOTE;
return;
}
misskeyApi('notes/show', { misskeyApi('notes/show', {
noteId: props.noteId, noteId: props.noteId,
}).then(res => { }).then(res => {

View File

@ -39,6 +39,7 @@ import { definePageMetadata } from '@/scripts/page-metadata.js';
import { i18n } from '@/i18n.js'; import { i18n } from '@/i18n.js';
import { $i } from '@/account.js'; import { $i } from '@/account.js';
import MkHorizontalSwipe from '@/components/MkHorizontalSwipe.vue'; import MkHorizontalSwipe from '@/components/MkHorizontalSwipe.vue';
import { getServerContext } from '@/server-context.js';
const XHome = defineAsyncComponent(() => import('./home.vue')); const XHome = defineAsyncComponent(() => import('./home.vue'));
const XTimeline = defineAsyncComponent(() => import('./index.timeline.vue')); const XTimeline = defineAsyncComponent(() => import('./index.timeline.vue'));
@ -52,6 +53,8 @@ const XFlashs = defineAsyncComponent(() => import('./flashs.vue'));
const XGallery = defineAsyncComponent(() => import('./gallery.vue')); const XGallery = defineAsyncComponent(() => import('./gallery.vue'));
const XRaw = defineAsyncComponent(() => import('./raw.vue')); const XRaw = defineAsyncComponent(() => import('./raw.vue'));
const CTX_USER = getServerContext('user');
const props = withDefaults(defineProps<{ const props = withDefaults(defineProps<{
acct: string; acct: string;
page?: string; page?: string;
@ -61,13 +64,24 @@ const props = withDefaults(defineProps<{
const tab = ref(props.page); const tab = ref(props.page);
const user = ref<null | Misskey.entities.UserDetailed>(null); const user = ref<null | Misskey.entities.UserDetailed>(CTX_USER);
const error = ref<any>(null); const error = ref<any>(null);
function fetchUser(): void { function fetchUser(): void {
if (props.acct == null) return; if (props.acct == null) return;
const { username, host } = Misskey.acct.parse(props.acct);
if (CTX_USER && CTX_USER.username === username && CTX_USER.host === host) {
user.value = CTX_USER;
return;
}
user.value = null; user.value = null;
misskeyApi('users/show', Misskey.acct.parse(props.acct)).then(u => { misskeyApi('users/show', {
username,
host,
}).then(u => {
user.value = u; user.value = u;
}).catch(err => { }).catch(err => {
error.value = err; error.value = err;

View File

@ -0,0 +1,23 @@
/*
* SPDX-FileCopyrightText: syuilo and misskey-project
* SPDX-License-Identifier: AGPL-3.0-only
*/
import * as Misskey from 'misskey-js';
import { $i } from '@/account.js';
const providedContextEl = document.getElementById('misskey_clientCtx');
export type ServerContext = {
clip?: Misskey.entities.Clip;
note?: Misskey.entities.Note;
user?: Misskey.entities.UserLite;
} | null;
export const serverContext: ServerContext = (providedContextEl && providedContextEl.textContent) ? JSON.parse(providedContextEl.textContent) : null;
export function getServerContext<K extends keyof NonNullable<ServerContext>>(entity: K): Required<Pick<NonNullable<ServerContext>, K>> | null {
// contextは非ログイン状態の情報しかないためログイン時は利用できない
if ($i) return null;
return serverContext ? (serverContext[entity] ?? null) : null;
}