docs: add SEO optimization guide for SeeForMe#11
Conversation
…gnore Co-authored-by: leekHotline <117092932+leekHotline@users.noreply.github.com>
There was a problem hiding this comment.
Pull request overview
This PR adds comprehensive SEO optimization documentation for the SeeForMe mobile app, addressing the lack of search engine visibility strategy. The guide covers both app store optimization (ASO) and web landing page SEO approaches.
Changes:
- Removed
/docsfrom root.gitignoreto enable documentation tracking - Added detailed Chinese-language SEO guide at
docs/seo-guide.mdcovering ASO strategy, web SEO implementation, technical requirements, and phased execution roadmap
Reviewed changes
Copilot reviewed 1 out of 2 changed files in this pull request and generated 8 comments.
| File | Description |
|---|---|
.gitignore |
Removed /docs exclusion to allow documentation directory to be tracked in version control |
docs/seo-guide.md |
Comprehensive SEO guide covering app store optimization, web landing pages, meta tags, structured data, robots.txt/sitemap configuration, content strategy, and monitoring tools |
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
| /sitemap.xml ← 站点地图 | ||
| /robots.txt ← 爬虫规则 | ||
| ``` | ||
|
|
There was a problem hiding this comment.
The guide suggests creating several web landing pages (/about, /how-it-works, /volunteer, /blog, etc.) but doesn't explain that these would need to be implemented as new route files in the frontend/app directory following Expo Router's file-based routing convention.
For example:
/aboutwould needapp/about.tsxorapp/(public)/about.tsx/how-it-workswould needapp/how-it-works.tsx/blogwould needapp/blog/_layout.tsxandapp/blog/[slug].tsxfor dynamic posts
Consider adding a brief note about the implementation approach, or referencing Expo Router documentation for creating these routes.
| > 在当前技术栈中,这些 URL 需要在 Expo Router 中对应到 `frontend/app` 目录下的路由文件。例如: | |
| > | |
| > ```tsx | |
| > frontend/ | |
| > app/ | |
| > index.tsx // 对应 / | |
| > about.tsx // 对应 /about | |
| > how-it-works.tsx // 对应 /how-it-works | |
| > volunteer.tsx // 对应 /volunteer | |
| > blog/ | |
| > _layout.tsx // 博客列表/布局 | |
| > [slug].tsx // 对应 /blog/任意文章 | |
| > ``` | |
| > | |
| > 更多文件路由示例可参考 Expo Router 文档:<https://docs.expo.dev/routing/introduction/> |
|
|
||
| ### 2.5 评分与评论 | ||
|
|
||
| - 在用户完成一次成功的志愿服务后弹出评分请求(`expo-store-review`) |
There was a problem hiding this comment.
The guide references expo-store-review package for rating prompts, but this package is not installed in frontend/package.json. Before this feature can be implemented, the package needs to be added as a dependency. Consider adding it with: pnpm add expo-store-review
| - 在用户完成一次成功的志愿服务后弹出评分请求(`expo-store-review`) | |
| - 在用户完成一次成功的志愿服务后弹出系统评分请求(可使用 Expo 提供的评分 API 实现) |
|
|
||
| ```tsx | ||
| // frontend/app/_layout.tsx (新增 web 专属 head) | ||
| import { Head } from "expo-router/head"; |
There was a problem hiding this comment.
The guide shows importing Head from "expo-router/head", but in expo-router v6 (which is installed), the Head component should be imported from "expo-router" directly, not from a /head subpath. The correct import should be: import { Head } from "expo-router";
Additionally, note that Head is primarily meant for setting document metadata on web, and the code example correctly wraps it in a Platform.OS === "web" check, which is good practice.
| import { Head } from "expo-router/head"; | |
| import { Head } from "expo-router"; |
|
|
||
| ### 3.3 在 Expo Web 中添加元数据 | ||
|
|
||
| Expo Router 支持通过 `expo-router/head` 在每个路由文件里设置 `<head>` 内容: |
There was a problem hiding this comment.
The guide also references "expo-router/head" here. As mentioned earlier, the correct import path for expo-router v6 is import { Head } from "expo-router"; not from a /head subpath.
| Expo Router 支持通过 `expo-router/head` 在每个路由文件里设置 `<head>` 内容: | |
| Expo Router 支持通过 `Head` 组件(从 `expo-router` 导入)在每个路由文件里设置 `<head>` 内容: |
| import { Head } from "expo-router/head"; | ||
| import { Platform } from "react-native"; | ||
|
|
||
| // 在 RootLayout 返回值中加入(只在 web 渲染): | ||
| {Platform.OS === "web" && ( | ||
| <Head> | ||
| <meta name="description" | ||
| content="SeeForMe 是一款公益 App,通过实时视频让志愿者帮助视障人士感知世界。" /> | ||
| <meta property="og:image" content="https://seeforme.app/og-cover.png" /> | ||
| <link rel="canonical" href="https://seeforme.app" /> | ||
| </Head> | ||
| )} |
There was a problem hiding this comment.
The code snippet shows a JSX fragment without proper context of where it should be placed in the component. The comment says "在 RootLayout 返回值中加入" (add to RootLayout return value), but the example doesn't show the complete component structure.
For clarity, the complete pattern should show that the <Head> component needs to be added as a child within the existing component tree. Based on the current _layout.tsx structure, it could be added before or after the <GestureWrapper> component, or inside it depending on the desired scope.
| import { Head } from "expo-router/head"; | |
| import { Platform } from "react-native"; | |
| // 在 RootLayout 返回值中加入(只在 web 渲染): | |
| {Platform.OS === "web" && ( | |
| <Head> | |
| <meta name="description" | |
| content="SeeForMe 是一款公益 App,通过实时视频让志愿者帮助视障人士感知世界。" /> | |
| <meta property="og:image" content="https://seeforme.app/og-cover.png" /> | |
| <link rel="canonical" href="https://seeforme.app" /> | |
| </Head> | |
| )} | |
| import { Stack } from "expo-router"; | |
| import { Head } from "expo-router/head"; | |
| import { Platform } from "react-native"; | |
| export default function RootLayout() { | |
| return ( | |
| <> | |
| {/* 在 RootLayout 返回值中加入(只在 web 渲染) */} | |
| {Platform.OS === "web" && ( | |
| <Head> | |
| <meta | |
| name="description" | |
| content="SeeForMe 是一款公益 App,通过实时视频让志愿者帮助视障人士感知世界。" | |
| /> | |
| <meta property="og:image" content="https://seeforme.app/og-cover.png" /> | |
| <link rel="canonical" href="https://seeforme.app" /> | |
| </Head> | |
| )} | |
| {/* 其余路由与页面内容 */} | |
| <Stack /> | |
| </> | |
| ); | |
| } |
| }, | ||
| "aggregateRating": { | ||
| "@type": "AggregateRating", | ||
| "ratingValue": "4.8", | ||
| "reviewCount": "120" |
There was a problem hiding this comment.
The JSON-LD structured data includes hardcoded rating values ("ratingValue": "4.8", "reviewCount": "120") that don't reflect actual app ratings. These should either be:
- Removed until the app has real ratings to report, or
- Clearly marked as placeholder values that need to be updated with actual data
Using fake ratings in structured data could be considered misleading and may violate search engine guidelines. It's better to omit the aggregateRating field entirely until there are real reviews.
| }, | |
| "aggregateRating": { | |
| "@type": "AggregateRating", | |
| "ratingValue": "4.8", | |
| "reviewCount": "120" |
| <meta property="og:title" content="SeeForMe — 当你需要时,有人替你看见"> | ||
| <meta property="og:description" | ||
| content="连接视障者与志愿者,实时视频描述世界。免费公益 App。"> | ||
| <meta property="og:image" content="https://seeforme.app/og-cover.png"> |
There was a problem hiding this comment.
The guide uses placeholder domain "https://seeforme.app" throughout all examples (og:image, og:url, canonical links, sitemap, etc.). Before implementing these SEO recommendations, the actual production domain needs to be determined and all instances should be updated accordingly. Consider adding a note at the beginning of the document stating that "seeforme.app" is a placeholder and should be replaced with the actual domain.
| | 项目 | 要求 | 工具 | | ||
| |------|------|------| | ||
| | HTTPS | 全站启用 | Cloudflare / 域名服务商 | | ||
| | Core Web Vitals | LCP < 2.5s,FID < 100ms,CLS < 0.1 | Google Search Console | |
There was a problem hiding this comment.
The Core Web Vitals thresholds mention "FID < 100ms" (First Input Delay), but as of mid-2024, Google has replaced FID with INP (Interaction to Next Paint) as a Core Web Vital. The good threshold for INP is < 200ms. Consider updating this to reflect the current Web Vitals: LCP < 2.5s, INP < 200ms, CLS < 0.1
| | Core Web Vitals | LCP < 2.5s,FID < 100ms,CLS < 0.1 | Google Search Console | | |
| | Core Web Vitals | LCP < 2.5s,INP < 200ms,CLS < 0.1 | Google Search Console | |
There was a problem hiding this comment.
Pull request overview
Copilot reviewed 1 out of 2 changed files in this pull request and generated no new comments.
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
The product lacked any SEO strategy documentation, leaving open questions on how to get search engines to index the app and how to optimize store presence.
Changes
/docsfrom.gitignore— was blocking the entire docs directory from being trackeddocs/seo-guide.md— concise, actionable Chinese-language SEO guide covering:expo-store-review)/,/about,/how-it-works,/volunteer,/blog), full HTML meta tag examples (OG, Twitter Card, canonical)<Head>meta tags viaexpo-router/headscoped toPlatform.OS === "web"MobileApplicationschema for Google rich resultsrobots.txt/sitemap.xml: ready-to-use templates blocking/api/from crawlersalt)Original prompt
✨ Let Copilot coding agent set things up for you — coding agent works faster and does higher quality work when set up for your repo.