当前位置: 首页> 文旅> 文化 > 餐饮店如何引流与推广_幼儿园大班主题网络图_关键词seo培训_如何利用seo赚钱

餐饮店如何引流与推广_幼儿园大班主题网络图_关键词seo培训_如何利用seo赚钱

时间:2025/7/9 21:18:40来源:https://blog.csdn.net/qq_34306228/article/details/145554727 浏览次数:0次
餐饮店如何引流与推广_幼儿园大班主题网络图_关键词seo培训_如何利用seo赚钱

React Router&SSR

  • React Router
    • create-react-router
      • tsconfig.json
      • react-router.config.ts
        • 补充一
      • package.json
        • scripts
        • dependencies
      • app
        • root.tsx
          • 介绍
          • 补充一
          • 补充二
          • 补充三
          • 补充四
          • 补充五
          • 补充六
          • 补充七
        • routes.ts
          • 补充一
          • 补充二
          • 补充三
          • 补充四
        • routes
          • contacts.tsx
            • 补充一
          • home.tsx
          • about.tsx
          • edit-contact.tsx
        • data.ts
        • layouts
          • sidebar.tsx
            • 补充一
            • 补充二
    • SSR端启动
    • V6版本的React Router

React Router

create-react-router

pnpx create-react-router@latest --template remix-run/react-router/tutorials/address-book

在这里插入图片描述

pnpm run dev

在这里插入图片描述
目录结构:
在这里插入图片描述

tsconfig.json

{"include": ["**/*", //全局路径"**/.server/**/*", //SSR"**/.client/**/*", //CSR".react-router/types/**/*"],"compilerOptions": {"lib": ["DOM", "DOM.Iterable", "ES2022"],"types": ["node", "vite/client"],"target": "ES2022","module": "ES2022","moduleResolution": "bundler","jsx": "react-jsx","rootDirs": [".", "./.react-router/types"],"baseUrl": ".","esModuleInterop": true,"verbatimModuleSyntax": true,"noEmit": true,"resolveJsonModule": true,"skipLibCheck": true,"strict": true}
}

react-router.config.ts

xxx.config.xxx 这种文件是可以从根路径去解析的

import { type Config } from "@react-router/dev/config";export default {ssr: false, //ssr设置为false
} satisfies Config; //Config定义的类型
补充一

添加预加载,这样对后面的静态页面 about 就不会出现先加载后再展示页面了
pre-rendering
借助的是浏览器中的预渲染
通过预加载的方式找到/app/routes/about.tsx这个路径,这个资源因此也可以前置加载了
在这里插入图片描述

import { type Config } from "@react-router/dev/config";export default {ssr: false, //ssr设置为falseprerender: ['/about'] //预加载
} satisfies Config; //Config定义的类型

请添加图片描述

package.json

scripts
  1. dev:执行的是 react-router-dev,“dev”: “react-router dev”,
  2. build:传递的是环境的变量,可以看作是服务端渲染时的场景,“build”: “cross-env NODE_ENV=production react-router build”,

pnpm run build
会先进行打包构建,得到client,如果react-router.config.ts中的ssr为true,则有client和service两个包
这是ssr为false时:
在这里插入图片描述
这是ssr为true时:
在这里插入图片描述
servser端中的,index.js中是一些路由的配置

state状态,router路由 -> next 在ssr中是有一个同构的概念, 客户端渲染的部分,服务端渲染的部分会有同步的依赖,这部分相同的依赖就有可能是 状态,路由

  1. start:这时是 serve,cross-env NODE_ENV=production react-router-serve ./build/server/index.js
dependencies
  1. @react-router/node
  2. @react-router/servce:react-router-serve的依赖

app

整体的根路径就是:root.tsx

root.tsx
介绍

由三部分构成:

  1. APP 渲染页面的结构,form表单
  2. Layout 布局,整体页面,html是在这里绘制的
  3. ErrorBoundary 目前还没用上
import type { Route } from "./+types/root";

这里 Route的根路径,算是最新版本的特性,指代当前 router的类型,
这里的 ./+types/root 使用的是 tsconfig.json中的 rootDirs[1]的值,完整是 .react-router/types/app/+types 这个路径
在这里插入图片描述
用的就是这个文件
在这里插入图片描述

export default function App() {......
}

是前端应用中常见的绘制的部分

import appStylesHref from "./app.css?url";

这个也就是 app/app.css的部分,算是对应的同一型的,默认型的样式的格式

import {Form,Scripts,ScrollRestoration,isRouteErrorResponse,
} from "react-router";
import type { Route } from "./+types/root";import appStylesHref from "./app.css?url";export default function App() {return (<><div id="sidebar"><h1>React Router Contacts</h1><div><Form id="search-form" role="search"><inputaria-label="Search contacts"id="q"name="q"placeholder="Search"type="search"/><div aria-hidden hidden={true} id="search-spinner" /></Form><Form method="post"><button type="submit">New</button></Form></div><nav><ul><li><a href={`/contacts/1`}>Your Name</a></li><li><a href={`/contacts/2`}>Your Friend</a></li></ul></nav></div></>);
}// The Layout component is a special export for the root route.
// It acts as your document's "app shell" for all route components, HydrateFallback, and ErrorBoundary
// For more information, see https://reactrouter.com/explanation/special-files#layout-export
export function Layout({ children }: { children: React.ReactNode }) {return (<html lang="en"><head><meta charSet="utf-8" /><meta name="viewport" content="width=device-width, initial-scale=1" /><link rel="stylesheet" href={appStylesHref} /></head><body>{children}<ScrollRestoration /><Scripts /></body></html>);
}// The top most error boundary for the app, rendered when your app throws an error
// For more information, see https://reactrouter.com/start/framework/route-module#errorboundary
export function ErrorBoundary({ error }: Route.ErrorBoundaryProps) {let message = "Oops!";let details = "An unexpected error occurred.";let stack: string | undefined;if (isRouteErrorResponse(error)) {message = error.status === 404 ? "404" : "Error";details =error.status === 404? "The requested page could not be found.": error.statusText || details;} else if (import.meta.env.DEV && error && error instanceof Error) {details = error.message;stack = error.stack;}return (<main id="error-page"><h1>{message}</h1><p>{details}</p>{stack && (<pre><code>{stack}</code></pre>)}</main>);
}
补充一

后面contacts.ts补充后,这里开始增加详情页

  1. 增加Outlet
import {Form,//详情页Outlet,Scripts,ScrollRestoration,isRouteErrorResponse,
} from "react-router";
  1. Outlet增加到页面
return (<><div id="sidebar">....</div><div id="detail"><Outlet /></div></>);

完成上面这两部后,页面上就成这样了:
在这里插入图片描述

补充二

目前从 contacts/1 切换到 contacts/2 是重定向的动作,通过刷新页面的方式,利用history导航进行页面跳转,希望不通过刷新页面的方式去做,这样要怎么做?
使用Link

  1. 引入Link
import {Form,Outlet,//增加LinkLink,Scripts,ScrollRestoration,isRouteErrorResponse,
} from "react-router";
  1. 写入Link
<nav><ul><li>{/* 将这里原本的a标签替换成Link */}<Link to={`/contacts/1`}>Your Name</Link></li><li><Link to={`/contacts/2`}>Your Friend</Link></li></ul>
</nav>
补充三
  1. 引入数据
// 通过mock接口请求,返回假数据
import { getContacts } from "./data";
  1. 导出一个方法,客户端请求加载
export async function clientLoader() {// 将此方法的loader作为参数透传出去const contacts = await getContacts();return { contacts };
}
  1. 样式中修改
// 传递不同的加载器,针对不同加载器来加载不同数据
//type ComponentProps = T.CreateComponentProps<Info>,ComponentProps这个类型是客户端下发下来的,Info是路由的节点状态
export default function App({ loaderData }:Route.ComponentProps) {   //这里的contacts是自动解析出来的const { contacts } = loaderData;...<nav>{contacts.length ? (<ul>{contacts.map((contact) => (<li key={contact.id}><Link to={`contacts/${contact.id}`}>{contact.first || contact.last ? (<>{contact.first} {contact.last}</>) : (<i>No Name</i>)}{contact.favorite ? (<span></span>) : null}</Link></li>))}</ul>) : (<p><i>No contacts</i></p>)}</nav>
........        
}

在这里插入图片描述

补充四

这里做的是客户端渲染的事情,客户端渲染会出现一个问题,会有一个白屏渲染的过程,是客户端需要加载资源生成的,优化刷新白屏跳转要怎么做?
请添加图片描述
优化办法:
HydrateFallback:当页面初始化的时候,渲染之前能够做一个fallback

//router中默认能够解析到的
export function HydrateFallback() {return (<div id="loading-splash"><div id="loading-splash-spinner" /><p>Loading, please wait...</p></div>);
}

请添加图片描述

补充五

添加 about 导航

export default function App({ loaderData }:Route.ComponentProps) { 
...<div id="sidebar"><h1><Link to="about">React Router Contacts</Link></h1>...</div>
}
补充六

将sidebar内容提出来,App精简成这样,并且删除clientLoader方法:

export default function App() { return <Outlet />
}

所有代码:

import {Outlet,Scripts,ScrollRestoration,isRouteErrorResponse,
} from "react-router";
import type { Route } from "./+types/root";import { getContacts } from "./data";import appStylesHref from "./app.css?url";//router中默认能够解析到的
export function HydrateFallback() {return (<div id="loading-splash"><div id="loading-splash-spinner" /><p>Loading, please wait...</p></div>);
}// 传递不同的加载器,针对不同加载器来加载不同数据
//type ComponentProps = T.CreateComponentProps<Info>,ComponentProps这个类型是客户端下发下来的,Info是路由的节点状态
export default function App() { return <Outlet />
}// The Layout component is a special export for the root route.
// It acts as your document's "app shell" for all route components, HydrateFallback, and ErrorBoundary
// For more information, see https://reactrouter.com/explanation/special-files#layout-export
export function Layout({ children }: { children: React.ReactNode }) {return (<html lang="en"><head><meta charSet="utf-8" /><meta name="viewport" content="width=device-width, initial-scale=1" /><link rel="stylesheet" href={appStylesHref} /></head><body>{children}<ScrollRestoration /><Scripts /></body></html>);
}// The top most error boundary for the app, rendered when your app throws an error
// For more information, see https://reactrouter.com/start/framework/route-module#errorboundary
export function ErrorBoundary({ error }: Route.ErrorBoundaryProps) {let message = "Oops!";let details = "An unexpected error occurred.";let stack: string | undefined;if (isRouteErrorResponse(error)) {message = error.status === 404 ? "404" : "Error";details =error.status === 404? "The requested page could not be found.": error.statusText || details;} else if (import.meta.env.DEV && error && error instanceof Error) {details = error.message;stack = error.stack;}return (<main id="error-page"><h1>{message}</h1><p>{details}</p>{stack && (<pre><code>{stack}</code></pre>)}</main>);
}
补充七
  1. 引入模拟数据接口
import { createEmptyContact } from "./data";
  1. 导出数据
export async function action() {const contact = await createEmptyContact()return {contact }
}
routes.ts

安装 @react-router/dev 和 @types/react-router 两个插件

import type { RouteConfig } from "@react-router/dev/routes";//后补充的代码
import {route} from '@react-router/dev/routes' //export default [] satisfies RouteConfig;
// 针对 contacts/1,contacts/2 这种格式,匹配到 routes/contacts.tsx这个文件
export default [route('contacts/:contactId','routes/contacts.tsx')
] satisfies RouteConfig;
补充一

引入home.tsx

  1. 引入index
import {index,route} from '@react-router/dev/routes' 
  1. 导出home.tsx
export default [index('routes/home.tsx'),route('contacts/:contactId','routes/contacts.tsx')
] satisfies RouteConfig;
补充二

添加about路由

import type { RouteConfig } from "@react-router/dev/routes";import {index,route} from '@react-router/dev/routes' export default [index('routes/home.tsx'),route('contacts/:contactId', 'routes/contacts.tsx'),//about路由添加route('about','routes/about.tsx')
] satisfies RouteConfig;
补充三

额外增加新定义的想表达的页面
layout页面=左侧sidebar+右侧home/contacts/about
about页面=并列的关系,需要layout布局改变

  1. 引入layout
import {index,layout,route} from '@react-router/dev/routes' 
  1. 修改路由
export default [layout('layouts/sidebar.tsx', [index('routes/home.tsx'),route('contacts/:contactId', 'routes/contacts.tsx'),]),route('about','routes/about.tsx')
] satisfies RouteConfig;
补充四
export default [layout('layouts/sidebar.tsx', [index('routes/home.tsx'),route('contacts/:contactId', 'routes/contacts.tsx'),]),route('about', 'routes/about.tsx'),route('contacts/:contactId/edit','routes/edit-contact.tsx')
] satisfies RouteConfig;

增加 edit 路由

routes
contacts.tsx

创建 routes/contacts.tsx 文件
从router 6开始,就开始刻意将路由视图绑定,由于 路由在客户端和服务端渲染都是能够共用的,除此之外,jsx的部分大多数也是能够共用的,因此认定 客户端渲染 和 服务端渲染,只是一种渲染模式,渲染内容是完全一致的。

import { Form } from "react-router";import type { ContactRecord } from "../data";export default function Contact() {//初始的值,针对于输入框内补充对应用户的基本的信息const contact = {first: "Your",last: "Name",avatar: "https://placecats.com/200/200",twitter: "your_handle",notes: "Some notes",favorite: true,};return (//   布局的返回,使用jsx的方式,有数据则返回,没有数据则做一个兜底<div id="contact"><div><imgalt={`${contact.first} ${contact.last} avatar`}key={contact.avatar}src={contact.avatar}/></div><div><h1>{contact.first || contact.last ? (<>{contact.first} {contact.last}</>) : (<i>No Name</i>)}<Favorite contact={contact} /></h1>{contact.twitter ? (<p><ahref={`https://twitter.com/${contact.twitter}`}>{contact.twitter}</a></p>) : null}{contact.notes ? <p>{contact.notes}</p> : null}<div><Form action="edit"><button type="submit">Edit</button></Form>{/*  form表单,是否要删除的操作 */}<Formaction="destroy"method="post"onSubmit={(event) => {const response = confirm("Please confirm you want to delete this record.");if (!response) {event.preventDefault();}}}><button type="submit">Delete</button></Form></div></div></div>);
}function Favorite({contact,
}: {contact: Pick<ContactRecord, "favorite">;
}) {const favorite = contact.favorite; //favorite的传参return (// 是否喜爱的一个操作<Form method="post"><buttonaria-label={favorite? "Remove from favorites": "Add to favorites"}name="favorite"value={favorite ? "false" : "true"}>{favorite ? "★" : "☆"}</button></Form>);
}

这样子以后,路径 contacts/1 就不会报404了
请添加图片描述

补充一
  1. 请求数据
import { getContact, type ContactRecord } from "../data";// params 就是 id的入参
export async function loader({params}):Route.LoaderArgs {const contact = await getContact(params.contactId)// 兜底,找不到名字的话就返回404if (!contact) {throw new Response('Could not find contact',{status:404})}return { contact };
}
  1. 获取数据,显示数据
export default function Contact({ loaderData }:Route.ComponentProps) {//初始的值,针对于输入框内补充对应用户的基本的信息/* const contact = {first: "Your",last: "Name",avatar: "https://placecats.com/200/200",twitter: "your_handle",notes: "Some notes",favorite: true,}; */const {contact}=loaderDatareturn (...)
}

请添加图片描述

home.tsx

创建 routes/home.tsx 文件

export default function Home() {return (<p id="index-page">This is a demo for React Router.<br />Check out{" "}<a href="https://reactrouter.com">the docs at reactrouter.com</a>.</p>);
}

在这里插入图片描述

about.tsx

创建 routes/about.tsx 文件

做静态页面,没有包含动态数据展示和请求处理

import { Link } from "react-router";export default function About() {return (<div id="about"><Link to="/">← Go to demo</Link><h1>About React Router Contacts</h1><div><p>This is a demo application showing off some of thepowerful features of React Router, includingdynamic routing, nested routes, loaders, actions,and more.</p><h2>Features</h2><p>Explore the demo to see how React Router handles:</p><ul><li>Data loading and mutations with loaders andactions</li><li>Nested routing with parent/child relationships</li><li>URL-based routing with dynamic segments</li><li>Pending and optimistic UI</li></ul><h2>Learn More</h2><p>Check out the official documentation at{" "}<a href="https://reactrouter.com">reactrouter.com</a>{" "}to learn more about building great webapplications with React Router.</p></div></div>);
}

在这里插入图片描述

edit-contact.tsx

在routes目录中新增edit-contact.tsx文件,也要注册到routes中

import { Form,redirect } from "react-router";
import type { Route } from "./+types/edit-contact";import { getContact,updateContact } from "../data";export async function action({params,request,
}: Route.ActionArgs) {const formData = await request.formData();// React内置的绑定const updates = Object.fromEntries(formData);await updateContact(params.contactId, updates);// 重定向到当前的数据return redirect(`/contacts/${params.contactId}`);
}export async function loader({ params }: Route.LoaderArgs) {const contact = await getContact(params.contactId);if (!contact) {throw new Response("Not Found", { status: 404 });}return { contact };
}export default function EditContact({loaderData,
}: Route.ComponentProps) {const { contact } = loaderData;return (<Form key={contact.id} id="contact-form" method="post"><p><span>Name</span><inputaria-label="First name"defaultValue={contact.first}name="first"placeholder="First"type="text"/><inputaria-label="Last name"defaultValue={contact.last}name="last"placeholder="Last"type="text"/></p><label><span>Twitter</span><inputdefaultValue={contact.twitter}name="twitter"placeholder="@jack"type="text"/></label><label><span>Avatar URL</span><inputaria-label="Avatar URL"defaultValue={contact.avatar}name="avatar"placeholder="https://example.com/avatar.jpg"type="text"/></label><label><span>Notes</span><textareadefaultValue={contact.notes}name="notes"rows={6}/></label><p><button type="submit">Save</button><button type="button">Cancel</button></p></Form>);
}

请添加图片描述
增加action方法后:
请添加图片描述

data.ts

默认的数据,后面还有许多测试数据

layouts
sidebar.tsx

创建 app/layouts目录
创建 layouts/sidebar.tsx文件

import { Outlet } from "react-router";export default function SidebarLayout() {return <Outlet />;
}
补充一

将 root.tsx 文件的 App 内容复制到这里
全部内容:

import { Outlet } from "react-router";
import { Link,Form } from "react-router";import { getContacts } from "../data";
import type { Route } from "../+types/root";export async function clientLoader() {// 将此方法的loader作为参数透传出去const contacts = await getContacts();return { contacts };
}
export default function SidebarLayout({ loaderData }:Route.ComponentProps) {const { contacts } = loaderData;return (<><div id="sidebar"><h1>React Router Contacts</h1><div><Form id="search-form" role="search"><inputaria-label="Search contacts"id="q"name="q"placeholder="Search"type="search"/><div aria-hidden hidden={true} id="search-spinner" /></Form><Form method="post"><button type="submit">New</button></Form></div><nav>{contacts.length ? (<ul>{contacts.map((contact) => (<li key={contact.id}><Link to={`contacts/${contact.id}`}>{contact.first || contact.last ? (<>{contact.first} {contact.last}</>) : (<i>No Name</i>)}{contact.favorite ? (<span></span>) : null}</Link></li>))}</ul>) : (<p><i>No contacts</i></p>)}</nav></div><div id="detail"><Outlet /></div></>)
}

about 页面就不在 sidebar 里了
在这里插入图片描述

补充二

数据添加,结合roots补充七

import { createEmptyContact } from "../data";export async function action() {const contact = await createEmptyContact()return {contact }
}

请添加图片描述

SSR端启动

  1. 开放 ssr:
    react-router.config.ts:
import { type Config } from "@react-router/dev/config";export default {ssr: true, //ssr设置为falseprerender: ['/about'] //预加载
} satisfies Config; //Config定义的类型
  1. 打包构建

pnpm run build

在这里插入图片描述

  1. 启动

pnpm run start

在这里插入图片描述

  1. 打开链接
    在这里插入图片描述

V6版本的React Router

最新版本的router,如果没有SSR的场景,只是为了渲染,只是使用路由基本的显示声明的话,其实完全没必要升级的

除了 hashRouter,memoryRouter外,还需要熟悉的几个hooks:
useNavigate:重定向
useLocation:location.href 获取可以通过这个方法获取
useMatch:像contacts/:id 可以通过useMatch的方式去获取
React Router for API

在V7之前,VueRouter 和 React Router 是很相像的
这里看 react-router-demo的这个例子:

pnpm i
pnpm run start
App.test.tsx报错则删除这个文件

  1. index.tsx文件的路由这里使用的是hashRouter,你可以使用createBrowserRouter等
    在这里插入图片描述

  2. 路由使用声明式的方式定义的
    在这里插入图片描述

  3. memoryRouter是从内存中读取的,不会关联到这边url上

  4. browserRouter是通过一个非hash值,是比较符合直觉的
    在这里插入图片描述
    这里使用browserRouter
    在hello中,使用的是match能够获取数据
    在这里插入图片描述

![在这里插入图片描述](https://i-blog.csdnimg.cn/direct/452344a9383a4e60b415c505547b0b17.png

  1. 要使用memoryRouter的话,需要进行一些配置
    createMemoryRouter
    在这里插入图片描述
    createMemoryRouter github
    在这里插入图片描述
    hashRouter也是调用的createRouter,唯一的区别是,它传递window参数的时候,使用window中的hash值传递。createBrowserRouter也是一样,调用的createRouter函数
关键字:餐饮店如何引流与推广_幼儿园大班主题网络图_关键词seo培训_如何利用seo赚钱

版权声明:

本网仅为发布的内容提供存储空间,不对发表、转载的内容提供任何形式的保证。凡本网注明“来源:XXX网络”的作品,均转载自其它媒体,著作权归作者所有,商业转载请联系作者获得授权,非商业转载请注明出处。

我们尊重并感谢每一位作者,均已注明文章来源和作者。如因作品内容、版权或其它问题,请及时与我们联系,联系邮箱:809451989@qq.com,投稿邮箱:809451989@qq.com

责任编辑: