Compare commits
2 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
a5ef1a1e70 | ||
|
|
297000f208 |
@@ -13,7 +13,6 @@
|
|||||||
|
|
||||||
<!-- Favicon -->
|
<!-- Favicon -->
|
||||||
<link rel="icon" type="image/svg+xml" href="/lite.svg" />
|
<link rel="icon" type="image/svg+xml" href="/lite.svg" />
|
||||||
<link rel="canonical" href="https://litek.typist.cc/" />
|
|
||||||
|
|
||||||
<!-- Open Graph / Facebook -->
|
<!-- Open Graph / Facebook -->
|
||||||
<meta property="og:type" content="website" />
|
<meta property="og:type" content="website" />
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
{
|
{
|
||||||
"name": "litek",
|
"name": "litek",
|
||||||
"private": true,
|
"private": true,
|
||||||
"version": "0.0.15",
|
"version": "0.0.16",
|
||||||
"type": "module",
|
"type": "module",
|
||||||
"scripts": {
|
"scripts": {
|
||||||
"dev": "vite",
|
"dev": "vite",
|
||||||
|
|||||||
96
src/hooks/use-seo.ts
Normal file
96
src/hooks/use-seo.ts
Normal file
@@ -0,0 +1,96 @@
|
|||||||
|
import { useEffect } from 'react';
|
||||||
|
import { useLocation } from 'react-router-dom';
|
||||||
|
|
||||||
|
interface UseSEOOptions {
|
||||||
|
title?: string;
|
||||||
|
description?: string;
|
||||||
|
baseUrl?: string;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* SEO Hook - 动态更新页面 SEO 元数据
|
||||||
|
*
|
||||||
|
* @param options - SEO 配置选项
|
||||||
|
* @param options.title - 页面标题(可选)
|
||||||
|
* @param options.description - 页面描述(可选)
|
||||||
|
* @param options.baseUrl - 网站基础 URL,默认为 https://litek.typist.cc
|
||||||
|
*
|
||||||
|
* @example
|
||||||
|
* ```tsx
|
||||||
|
* // 在组件中使用
|
||||||
|
* useSEO({
|
||||||
|
* title: 'UUID Generator',
|
||||||
|
* description: 'Free online UUID generator tool'
|
||||||
|
* });
|
||||||
|
* ```
|
||||||
|
*/
|
||||||
|
export const useSEO = (options: UseSEOOptions = {}) => {
|
||||||
|
const location = useLocation();
|
||||||
|
const {
|
||||||
|
title,
|
||||||
|
description,
|
||||||
|
baseUrl = 'https://litek.typist.cc'
|
||||||
|
} = options;
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
// 构建当前页面的完整 URL
|
||||||
|
const canonicalUrl = `${baseUrl}${location.pathname}`;
|
||||||
|
|
||||||
|
// 更新或创建 canonical 链接
|
||||||
|
let canonical = document.querySelector('link[rel="canonical"]') as HTMLLinkElement;
|
||||||
|
if (!canonical) {
|
||||||
|
canonical = document.createElement('link');
|
||||||
|
canonical.setAttribute('rel', 'canonical');
|
||||||
|
document.head.appendChild(canonical);
|
||||||
|
}
|
||||||
|
canonical.setAttribute('href', canonicalUrl);
|
||||||
|
|
||||||
|
// 更新页面标题
|
||||||
|
if (title) {
|
||||||
|
document.title = `${title} - Lite Kit`;
|
||||||
|
}
|
||||||
|
|
||||||
|
// 更新 meta description
|
||||||
|
if (description) {
|
||||||
|
let metaDescription = document.querySelector('meta[name="description"]') as HTMLMetaElement;
|
||||||
|
if (metaDescription) {
|
||||||
|
metaDescription.setAttribute('content', description);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// 更新 Open Graph URL
|
||||||
|
let ogUrl = document.querySelector('meta[property="og:url"]') as HTMLMetaElement;
|
||||||
|
if (ogUrl) {
|
||||||
|
ogUrl.setAttribute('content', canonicalUrl);
|
||||||
|
}
|
||||||
|
|
||||||
|
// 更新 Open Graph Title
|
||||||
|
if (title) {
|
||||||
|
let ogTitle = document.querySelector('meta[property="og:title"]') as HTMLMetaElement;
|
||||||
|
if (ogTitle) {
|
||||||
|
ogTitle.setAttribute('content', `${title} - Lite Kit`);
|
||||||
|
}
|
||||||
|
|
||||||
|
// 更新 Twitter Card Title
|
||||||
|
let twitterTitle = document.querySelector('meta[name="twitter:title"]') as HTMLMetaElement;
|
||||||
|
if (twitterTitle) {
|
||||||
|
twitterTitle.setAttribute('content', `${title} - Lite Kit`);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// 更新 Open Graph Description
|
||||||
|
if (description) {
|
||||||
|
let ogDescription = document.querySelector('meta[property="og:description"]') as HTMLMetaElement;
|
||||||
|
if (ogDescription) {
|
||||||
|
ogDescription.setAttribute('content', description);
|
||||||
|
}
|
||||||
|
|
||||||
|
// 更新 Twitter Card Description
|
||||||
|
let twitterDescription = document.querySelector('meta[name="twitter:description"]') as HTMLMetaElement;
|
||||||
|
if (twitterDescription) {
|
||||||
|
twitterDescription.setAttribute('content', description);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}, [location.pathname, title, description, baseUrl]);
|
||||||
|
};
|
||||||
|
|
||||||
@@ -4,20 +4,26 @@ import { Outlet } from "react-router-dom";
|
|||||||
import { ThemeProvider } from "@/components/theme/provider"
|
import { ThemeProvider } from "@/components/theme/provider"
|
||||||
import { SidebarProvider, SidebarTrigger } from "@/components/ui/sidebar"
|
import { SidebarProvider, SidebarTrigger } from "@/components/ui/sidebar"
|
||||||
import { AppSidebar } from "@/components/sidebar";
|
import { AppSidebar } from "@/components/sidebar";
|
||||||
|
import { useSEO } from "@/hooks/use-seo";
|
||||||
|
|
||||||
export const Layout: FC = () => (
|
export const Layout: FC = () => {
|
||||||
<ThemeProvider defaultTheme="dark" storageKey="vite-ui-theme">
|
// 使用 SEO hook 自动更新 canonical URL 和其他 SEO 元数据
|
||||||
<SidebarProvider>
|
useSEO();
|
||||||
<AppSidebar />
|
|
||||||
<div className="p-4 flex flex-col w-full h-[100vh] overflow-hidden">
|
return (
|
||||||
<nav className="flex items-center justify-between">
|
<ThemeProvider defaultTheme="dark" storageKey="vite-ui-theme">
|
||||||
<SidebarTrigger className="size-10" />
|
<SidebarProvider>
|
||||||
<div role="actions" />
|
<AppSidebar />
|
||||||
</nav>
|
<div className="p-4 flex flex-col w-full h-[100vh] overflow-hidden">
|
||||||
<main className="flex-1 overflow-auto p-4 overflow-hidden">
|
<nav className="flex items-center justify-between">
|
||||||
<Outlet />
|
<SidebarTrigger className="size-10" />
|
||||||
</main>
|
<div role="actions" />
|
||||||
</div>
|
</nav>
|
||||||
</SidebarProvider>
|
<main className="flex-1 overflow-auto p-4 overflow-hidden">
|
||||||
</ThemeProvider>
|
<Outlet />
|
||||||
);
|
</main>
|
||||||
|
</div>
|
||||||
|
</SidebarProvider>
|
||||||
|
</ThemeProvider>
|
||||||
|
);
|
||||||
|
};
|
||||||
Reference in New Issue
Block a user