当前位置: 首页 > news >正文

React + Router

React + Router

这个只是专门讲解 React Router 新开的例子。

教程来源:https://reactrouter.com/en/main/start/tutorial

创建新项目

yarn create vite my-react-router-app --template react-ts
cd my-react-router-app
yarn

安装 React Router 依赖:

yarn add react-router-dom localforage match-sorter sort-by
# 对于 ts 要添加 @types/sort-by, 否则报错找不到模块【不影响运行】
yarn add -D @types/sort-by

运行项目

yarn dev# VITE v4.4.11  ready in 358 ms# ➜  Local:   http://localhost:5173/
# ➜  Network: use --host to expose
# ➜  press h to show help

获取该项目所需要的文件

这里找到 css 文件,将其粘贴到 src/index.css 文件中: Copy/Paste the tutorial CSS

这里找到 js 文件,将其添加到 src/contacts.js:Copy/Paste the tutorial CSS

Js 文件主要的用途:将创建、阅读、搜索、更新和删除数据。一个典型的Web应用程序可能会与Web服务器上的API对话,将使用浏览器存储并伪造一些网络延迟来保持这一点。这些代码都与React Router无关,所以直接复制/粘贴即可。

2个文件拷贝到项目中后,你可以将无关文件删除,可以删除任何其他内容(如 App.jsassets 等)。

项目主要文件(文件目录):

src
├── contacts.js # 这里最好转换成 ts 文件,即添加上类型,方便后面
├── index.css
└── main.tsx

添加路由器

main.tsx 中创建并渲染浏览器路由器

// 导入路由模块
import {createBrowserRouter,RouterProvider
} from "react-router-dom"// 配置路由
const router = createBrowserRouter([{// 根路由 root routerpath: "/",// 之后会替换成组件element: <div>Hello world!</div>}
])// 渲染:将其添加到 render 函数中作为参数传递即可
ReactDOM.createRoot(document.getElementById('root')!).render(<React.StrictMode><RouterProvider router={router}></RouterProvider></React.StrictMode>,
)

创建根路由

创建 src/routessrc/routes/root.tsx

rout.tsx 文件如下:

export default function Root() {return (<><div id="sidebar"><h1>React Router Contacts</h1><div><form id="search-form" role="search"><inputid="q"aria-label="Search contacts"placeholder="Search"type="search"name="q"/><divid="search-spinner"aria-hiddenhidden={true}/><divclassName="sr-only"aria-live="polite"></div></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><div id="detail"></div></>);
}

回到 src/main.tsx 的 router 变量处,将 <div>Hello World</div> 替换成组件。

import Root from './routes/root'// 配置路由
const router = createBrowserRouter([{path: "/",element: <Root></Root>}
])

好了,基本结构搭建完毕!运行项目看看吧。

yarn dev

处理错误页面

以前在写 React 的时候,因为是单页面应用,所以地址栏的变化不会影响程序的影响。

但是导入了 React Router 组件后,地址栏会被它所监听到,从而出现 React Router 默认的错误屏幕。

创建 src/error-page.tsx 文件。

import { useRouteError } from "react-router-dom";export default function ErrorPage() {const error = useRouteError();console.error(error);return (<div id="error-page"><h1>Oops!</h1><p>Sorry, an unexpected error has occurred.</p><p><i>{error.statusText || error.message}</i></p></div>);
}

回到 src/main.tsx 文件。

将根路由上的 <ErrorPage> 设置为 errorElement

import ErrorPage from './error-page'// 配置路由
const router = createBrowserRouter([{path: "/",element: <Root></Root>,errorElement: <ErrorPage></ErrorPage>}
])

然后我们随机访问一个页面,http://localhost:5173/contacts/1 会显示我们创建的错误页面(ErrorPage 组件)。

useRouteError 提供了抛出的错误。当用户导航到不存在的路由时,您将得到一个错误响应,并显示“Not Found” statusText

创建联系人路由

有了根路由,那么其他页面呢?

创建一个 src/routes/contact.tsx文件

import { Form } from "react-router-dom";export default function Contact() {const contact = {first: "Your",last: "Name",avatar: "https://placekitten.com/g/200/200",twitter: "your_handle",notes: "Some notes",favorite: true,};return (<div id="contact"><div><imgkey={contact.avatar}src={contact.avatar || null}/></div><div><h1>{contact.first || contact.last ? (<>{contact.first} {contact.last}</>) : (<i>No Name</i>)}{" "}<Favorite contact={contact} /></h1>{contact.twitter && (<p><atarget="_blank"href={`https://twitter.com/${contact.twitter}`}>{contact.twitter}</a></p>)}{contact.notes && <p>{contact.notes}</p>}<div><Form action="edit"><button type="submit">Edit</button></Form><Formmethod="post"action="destroy"onSubmit={(event) => {if (!confirm("Please confirm you want to delete this record.")) {event.preventDefault();}}}><button type="submit">Delete</button></Form></div></div></div>);
}function Favorite({ contact }) {// yes, this is a `let` for laterlet favorite = contact.favorite;return (<Form method="post"><buttonname="favorite"value={favorite ? "false" : "true"}aria-label={favorite? "Remove from favorites": "Add to favorites"}>{favorite ? "★" : "☆"}</button></Form>);
}

然后我们到 src/main.tsx 进行添加路由信息,还是配置那里。

// 配置路由
const router = createBrowserRouter([//....{path: "contacts/:contactId",element: <Contact></Contact>,errorElement: <ErrorPage></ErrorPage>}
])

会发现,它不在我们的根布局中(右侧没有 组件,即侧边导航栏没有看到)

如果希望contact组件在 <Root> 布局中呈现。

我们通过使联系路由成为根路由的子路由来实现这一点。

回到我们的 src/main,tsx 的配置路由信息:

// 将添加的 contacts 路由信息移动到 Root 的 children 属性上
// 配置路由
const router = createBrowserRouter([{//....children: [{path: "contacts/:contactId",element: <Contact></Contact>}]},
])

再次看到根布局,但右侧是一个空白页面。我们需要告诉根路由我们希望它在哪里渲染其子路由。

回到我们的 组件中(src/routes/rout.tsx

Render an Outlet 👉渲染一个 <Outlet>

// 导入
import { Outlet } from "react-router-dom"// 在哪里进行渲染
<div id="detail"><Outlet></Outlet>
</div>

客户端路由 CSR

Client Side Routing 。

在路由跳转之间,我们发现,浏览器是请求整个网页文档。而不是使用 React Router。

客户端路由允许我们的应用更新URL,而无需从服务器请求另一个文档。【立即呈现新的UI】

使用 组件

import { Link } from "react-router-dom"// 将 a 标签替换成 <Link to={}> </Link>
<nav><ul><li><Link to={`/contacts/1`}>Your Name</Link></li><li><Link to={`/contacts/2`}>Your Friend</Link></li></ul>
</nav>

加载数据

前面我们只是将数据写死,该节内容就是如何通过 React Router 调用 API 去获取数据。

注意:这是一个 Demo,真实的情况请采用 axios 去获取后端接口。

到我们的根路由组件()

import { getContacts } from "../contacts"export async function loader() {const contacts = await getContacts();return { contacts };
}

再来到 src/main.tsx 文件,进行配置 loader

import Root, { loader as rootLoader } from './routes/root'// 配置路由
const router = createBrowserRouter([{// ...loader: rootLoader,// ...},
])

然后回到我们的根路由组件()

import { Outlet, Link, useLoaderData } from "react-router-dom"// 其他代码...export default function Root() {const { contacts }: any = useLoaderData();return (<>{* 其他代码... *}<nav>{/* 判断是否存在联系人 */}{contacts.length ? (<ul>{contacts.map((contact: any) => (<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>}</Link></li>))}</ul>) : (<p><i>No contacts</i></p>)}</nav>{* 其他代码... *}</>)
}

好了,看看页面吧~

创建联系人

在页面中我们可以看到没有联系人列表。

这个时候我们需要完成新建功能,看到 New 按钮了吗?

点击一下会发生什么?呜?出错了?找不到 localhost 的网页

这里正常情况会报 405 错误,找不到对应的请求方式。

这里可以采取两种方式: 给 vite 增加 post 提交功能,或者采用 CSR 客户端路由方式【本节重点】。

回到我们的根路由组件()

import { Outlet, Link, useLoaderData, Form } from "react-router-dom"
import { getContacts, createContact } from "../contacts";// 新增联系人
export async function action() {const contact = await createContact();return { contact };
}// ...return (// ...<Form method="post"><button type="submit">New</button></Form>
)

回到 src/main.tsx 配置 action

import Root, { loader as rootLoader, action as rootAction } from './routes/root'// 配置路由
const router = createBrowserRouter([{path: "/",element: <Root></Root>,errorElement: <ErrorPage></ErrorPage>,loader: rootLoader,action: rootAction,//...
])

但是思考一下。

当我们点击 New 后,页面如何更新的? action 在哪里?重新获取数据的代码在哪里? useStateonSubmituseEffect 在哪里?!

<Form> 阻止浏览器向服务器发送请求,而是将其发送到路由 action 。在Web语义中,POST通常意味着某些数据正在更改。按照惯例,React Router将此作为提示,在操作完成后自动重新验证页面上的数据。这意味着你所有的 useLoaderData 钩子都会更新,UI会自动与你的数据保持同步!

所以,原理在于 React Router 根据 POST (意味着要更新数据了),去自动帮助我们更新源代码。

获取 Loader 中 URL 参数

当点击联系人列表的某个名称时,会跳转到某个页面,只是 id 变成我们模拟出来的了,而不是写死的。

但是这里有个问题,右侧还是 组件,数据没有进行更新。

在 也需要 loader ,这里与根路由一样,不再阐述,只会给关键代码。

// src/routes/contact.tsx
import { Form, useLoaderData } from "react-router-dom"
import { getContact } from "../contacts"export async function loader({ params }: any) {const contact = await getContact(params.contactId)return { contact }
}export default function Contact() {const { contact }: any = useLoaderData(); // ...
}
// src/main.tsx
import Contact, { loader as contactLoader } from './routes/contact'// 配置路由
const router = createBrowserRouter([{//....children: [{path: "contacts/:contactId",element: <Contact></Contact>,loader: contactLoader}]},
])

更新数据

一个新文件 src/routes/edit.tsx

import { Form, useLoaderData } from "react-router-dom";// 自己补充 loader ,参考 contact.tsxexport default function EditContact() {const { contact } = useLoaderData();return (<Form method="post" id="contact-form"><p><span>Name</span><inputplaceholder="First"aria-label="First name"type="text"name="first"defaultValue={contact.first}/><inputplaceholder="Last"aria-label="Last name"type="text"name="last"defaultValue={contact.last}/></p><label><span>Twitter</span><inputtype="text"name="twitter"placeholder="@jack"defaultValue={contact.twitter}/></label><label><span>Avatar URL</span><inputplaceholder="https://example.com/avatar.jpg"aria-label="Avatar URL"type="text"name="avatar"defaultValue={contact.avatar}/></label><label><span>Notes</span><textareaname="notes"defaultValue={contact.notes}rows={6}/></label><p><button type="submit">Save</button><button type="button">Cancel</button></p></Form>);
}
// src/main.tsx
import EditContact, { loader as editLoader } from './routes/edit'
// 配置路由
const router = createBrowserRouter([{//...{path: "contacts/:contactId/edit",element: <EditContact></EditContact>,loader: editLoader}]},
])

除了添加 loader 外,还应该添加 action

// src/routes/edit.tsx
import { Form, useLoaderData, redirect } from "react-router-dom";
import { getContact, updateContact } from "../contacts"export async function action({ request, params }: any) {const formData = await request.formData()const updates = Object.fromEntries(formData)await updateContact(params.contactId, updates)return redirect(`/contacts/${params.contactId}`)
}// src/main.tsx
// 配置路由
const router = createBrowserRouter([{//...children: [{//..},{path: "contacts/:contactId/edit",element: <EditContact></EditContact>,loader: editLoader,action: editAction}]},
])

Loaders and actions 都能够返回 Response (它们都能接收 request 参数)。

redirect 重定向到一个地址。

如果没有客户端路由,如果服务器在POST请求后重定向,则新页面将获取最新数据并呈现。正如我们之前了解到的,React Router模拟了这个模型,并在操作之后自动重新验证页面上的数据。这就是为什么侧边栏会在我们保存表单时自动更新。如果没有客户端路由,额外的重新验证代码就不存在,所以它也不需要在客户端路由中存在!

小插曲 ,更新 “New” 按钮的 action ,

在 组件中,经过前面这么多步骤,自己也能够实现

高亮显示当前联系人

随着左侧的导航栏联系人越来越多,我们分不清右侧人物是左侧哪个标签。

可以采用 <NavLink>

// src/routes/root.tsx
import {Outlet,NavLink,useLoaderData,Form,redirect,
} from "react-router-dom";return (// ...<NavLinkto={`contacts/${contact.id}`}className={({ isActive, isPending }) =>isActive? "active": isPending? "pending": ""}>{/* other code */}</NavLink>
)

加上过渡动画

使用 Global Pending UI

使用到的 API : useNavigation。通过它,我们可以知道该页面是否处于加载中。

在短延迟后添加一个漂亮的淡入淡出(以避免快速加载的UI闪烁)。你可以做任何你想做的事情,比如显示一个加载条在顶部。

在跳转过程时,会发现第二次明显加快不少,这是有缓存。

// src/routes/root.tsx
import {// existing codeuseNavigation,
} from "react-router-dom";export default function Root() {const { contacts } = useLoaderData();const navigation = useNavigation();return (<><div id="sidebar">{/* existing code */}</div><divid="detail"className={navigation.state === "loading" ? "loading" : ""}><Outlet /></div></>);
}

删除记录

这里与 edit.tsx 类似,同样创建文件,添加 action

// src/routes/destroy.tsx 【自己创建】
import { redirect } from "react-router-dom";
import { deleteContact } from "../contacts";export async function action({ params }: any) {await deleteContact(params.contactId);return redirect("/");
}// src/main.tsx
/* existing code */
import { action as destroyAction } from "./routes/destroy";const router = createBrowserRouter([{path: "/",/* existing root route props */children: [/* existing routes */{path: "contacts/:contactId/destroy",action: destroyAction,},],},
]);/* existing code */

索引路由

Index Routes

在浏览首页时,右侧总是空白,我们可以设置一个组件显示在那里。

// src/routes/index.tsx
export default function Index() {return (<p id="zero-state">This is a demo for React Router.<br />Check out{" "}<a href="https://reactrouter.com">the docs at reactrouter.com</a>.</p>);
}// src/main.tsx
// existing code
import Index from "./routes/index";const router = createBrowserRouter([{path: "/",element: <Root />,errorElement: <ErrorPage />,loader: rootLoader,action: rootAction,children: [{ index: true, element: <Index /> },/* existing routes */],},
]);

浏览器后退按钮

我们需要在按钮上添加一个click处理程序,以及React Router中的 useNavigate

// src/routes/edit.tsx
import {Form,useLoaderData,redirect,useNavigate,
} from "react-router-dom";export default function EditContact() {const { contact } = useLoaderData();const navigate = useNavigate();return (<Form method="post" id="contact-form">{/* existing code */}<p><button type="submit">Save</button><buttontype="button"onClick={() => {navigate(-1);}}>Cancel</button></p></Form>);
}

URL 搜查参数 和 GET 提交

在前面我们,我们分别针对增删改,都做了处理。

  • 按下 New 创建联系人
  • 点击 左侧导航栏,查看联系人信息
  • 点击 Edit 按钮可以修改
  • 点击 Delete 按钮可以进行删除
  • 点击 编辑页面的取消按钮,可以回退回上一个页面
  • 在首页添加了索引路由
  • 左侧导航栏使用 NavLink 显示当前

不仅仅只有上面,还保存到 indexDb 中哦,这个演示已经做了很多了,

现在来做搜索。

现在将 form 改成 React Router 做 CSR 【客户端路由】

// src/routes/root.jsx
<Form id="search-form" role="search"></Form>

因为这是 GET 请求,它不会经过 action,将过滤代码写在 loader 中。

// src/routes/root.jsx
// 获取联系人列表 
export async function loader({ request }: any) {// 过滤const url = new URL(request.url);const q = url.searchParams.get("q");const contacts = await getContacts(q);return { contacts };
}

现在回到页面,进行简单的输入一些字符。就能看到查询的结构了。

URL地址栏与搜索框一致

前面只是简单的进行搜索。

问题:

  • 当重新刷新该页面时,搜索框中不包含值了
  • 当搜素完后,回退上一页,搜索框中还是以前的值

第一个问题比较好解决,给搜索框一个默认值。

// src/routes/root.jsx
export async function loader({ request }: any) {//...return { contacts, q };
}const { contacts, q }: any = useLoaderData();
<input...defaultValue={q}
/>

解决第二个问题,搜索框会残留之前的搜素记录。

当数据发生变化时,对搜素框的值进行修改,这里可以用到 useEffect(callback, [q])

// src/routes/root.jsx
const [query, setQuery] = useState(q);// 当查询 q 变化时,更新 query
useEffect(() => {setQuery(q);
}, [q])// 将查询值作为搜素框的值
<input value={query} />// 在搜索框输入时,记得监听,更新 query 值
<input onChange={e => {setQuery(e.target.value)
}}

自动提交 useSubmit

前面的搜素,只有我们按下回车键才会进行表单提交,才会过滤后的结果。

使用 React Router 的 API:useSubmit

// src/routes/root.jsx
import { useSubmit } from "react-router-dom"// 获取 submit
const submit = useSubmit();// 提交表单组件
onChange={e => {// currentTarget 与 targetsubmit(e.currentTarget.form);
}}

这里有个小小知识点:

  • currentTarget 当前绑定该事件的元素
  • target 触发该事件的元素
  • 当绑定事件元素和触发事件元素一致时,它们相同(包括 this)

优化体验

因为搜素是在网络请求,可能网络不太好或者请求数据量大。使用 useNavigation 获取加载状态。

// src/routes/root.jsx
// 是否在搜素
const searching = navigation.location && new URLSearchParams(navigation.location.search).has("q");// 修改搜索框样式
<input className={searching ? "loading" : ""} />// 修改搜索框图标
<divid="search-spinner"aria-hiddenhidden={!searching}
/>

管理历史堆栈

优化自动提交,useSubmit()

submit 有其他配置,如 replace。

设置为true,替换浏览器历史记录堆栈中的当前条目,而不是创建一个新条目(即保持在“同一页面”)。默认为false。

submit(e.currentTarget.value, {replace: true
})

我们只想替换搜索结果,而不是开始搜索之前的页面。

const isFirstSearch = q == null;
submit(e.currentTarget.value, {replace: !isFirstSearch;
})

在没有导航下更改数据

在前面都是通过修改地址栏,通过导航的方式切换页面,使页面上的数据发生改变。

在该节内容中,我们将学习到如何通过 useFetcher API 去更改数据。

我们有 useFetcher 钩子。它允许我们与 loader 和 action 进行通信,而不会导致导航。

比如该项目中联系人页面上的★按钮对此很有意义。我们不是在创建或删除新记录,我们不想更改页面,我们只是想更改我们正在查看的页面上的数据。

// src/routes/contact.tsx
import { useFetcher } from "react-router-dom"// 使用
const fetcher = useFetcher();
return (<fetcher.Form method="post"></fetcher.Form>
)

对于 post 提交我们还要 action 来处理。

export async function action({ request, params }: any) {const formData = await request.formData();return updateContact(params.contactId, {favorite: formData.get("favorite") === "true"})
}

将 action 加入到 src/main.tsx 中,这一步跳过,较简单。

优化点击

在点击收藏按钮时,明显感到迟钝。

在 Favorite 加上下面代码:

if (fetcher.formData) {favorite = fetcher.formData.get("favorite") === "true"
}

加上后,会立即更新状态。

优化找不到联系人

当访问一个未知的联系人时,报错:

Cannot read properties of null (reading 'avatar')

在获取联系人(loader)中去判断。

// src/routes/contact.tsx
export async function loader({ params }) {const contact = await getContact(params.contactId);if (!contact) {throw new Response("", {status: 404,statusText: "Not Found",});}return { contact };
}

将错误页面单独出来

将报错信息,都放在根路由上。(即:左侧有侧边导航栏)

// 配置路由
const router = createBrowserRouter([{// 根路由path: "/",element: <Root></Root>,errorElement: <ErrorPage></ErrorPage>,loader: rootLoader,action: rootAction,children: [{// 单独拉出一个路由,去处理错误页面errorElement: <ErrorPage></ErrorPage>,children: [// 子路由]}]},
])

JSX 风格【拓展】

可以不使用 createBrowserRouter 配置路由。

可以采取 jsx 的风格去编写路由。

import {createRoutesFromElements,createBrowserRouter,Route,
} from "react-router-dom";const router = createBrowserRouter(createRoutesFromElements(<Routepath="/"element={<Root />}loader={rootLoader}action={rootAction}errorElement={<ErrorPage />}><Route errorElement={<ErrorPage />}><Route index element={<Index />} /><Routepath="contacts/:contactId"element={<Contact />}loader={contactLoader}action={contactAction}/><Routepath="contacts/:contactId/edit"element={<EditContact />}loader={contactLoader}action={editAction}/><Routepath="contacts/:contactId/destroy"action={destroyAction}/></Route></Route>)
);

更多 API 访问 React Router 官方: React-Router v6.16.0

相关文章:

React + Router

React Router 这个只是专门讲解 React Router 新开的例子。 教程来源&#xff1a;https://reactrouter.com/en/main/start/tutorial 创建新项目 yarn create vite my-react-router-app --template react-ts cd my-react-router-app yarn安装 React Router 依赖: yarn add…...

微信小程序设置动态变量设值

微信小程序设置动态变量设值 微信小程序如何动态变量设值&#xff1f; 示例代码如下&#xff1a; setValFunc() {const key this.data.currentPickerid; // 业务需求动态键值key&#xff0c;或者是上一界面获取的动态key值const value 变量值;this.setData({[${key}]: valu…...

闪站侠洗衣洗鞋多门店多用户管理系统,洗鞋店干洗店小程序开发;

闪站侠洗护软件是多分店多用户管理系统&#xff0c;一个分店可以同时关联多个用户。闪站侠洗护管理软件通过互联网为洗衣店/洗鞋店干洗店提供加盟或直营连锁管理&#xff1b; 实现会员洗衣的门店收衣->上门收衣->开单拍照->清洗护理/工厂洗涤->微&#xff5c;信/短…...

JDBC增删改查示例

数据库表 CREATE TABLE customers ( id int NOT NULL AUTO_INCREMENT, name varchar(15) DEFAULT NULL, email varchar(20) DEFAULT NULL, birth date DEFAULT NULL, photo mediumblob, PRIMARY KEY (id) ) ENGINEInnoDB AUTO_INCREMENT39 DEFAULT CHARSETgb2312;…...

emqx broker安装

emqx broker安装 Emq x百万级开源 MQTT 消息服务器 是基于 Erlang/OTP 语言平台开发 一款完全开源&#xff0c;高可用低时延的百万级分布式物联网 MQTT 5.0 消息服务器 官方地址: https://www.emqx.com/zh Centos7 安装 #下载Centos7 amd64位版本 wget https://www.emqx.c…...

如何选择国产压力测试工具?

随着互联网的飞速发展&#xff0c;软件应用的性能和稳定性变得愈发重要。无论是在线购物网站、社交媒体平台还是移动应用程序&#xff0c;用户都期望能够快速、流畅地访问和使用它们。为了确保应用程序在高负载下仍能够正常运行&#xff0c;压力测试工具变得至关重要。在国内&a…...

基于AT89C51流水花样灯proteus仿真设计

一、仿真原理图&#xff1a; 二、仿真效果图&#xff1a; 三、仿真工程&#xff1a; c51单片机流水灯花样灯proteus仿真设计资源-CSDN文库...

android U广播详解(二)

android U广播详解&#xff08;一&#xff09; 基础代码介绍 广播相关 // 用作单个进程批量分发receivers&#xff0c;已被丢弃 frameworks/base/services/core/java/com/android/server/am/BroadcastReceiverBatch.java // 主要逻辑所在类&#xff0c;包括入队、分发、结束…...

导航守卫的使用记录和beforeEach( )死循环的问题

前置导航守卫beforeEach的使用 import Vue from vue import VueRouter from vue-router // 进度条 import NProgress from nprogress import nprogress/nprogress.cssVue.use(VueRouter)// 路由表 const routes [{path: "/",redirect: "/home",},{path: …...

SpringMVC源码分析(三)HandlerExceptionResolver启动和异常处理源码分析

问题&#xff1a;异常处理器在SpringMVC中是如何进行初始化以及使用的&#xff1f; Spring MVC提供处理异常的方式主要分为两种&#xff1a; 1、实现HandlerExceptionResolver方式&#xff08;HandlerExceptionResolver是一个接口&#xff0c;在SpringMVC有一些默认的实现也可以…...

系统架构与Tomcat的安装和配置

2023.10.16 今天是学习javaweb的第一天&#xff0c;主要学习了系统架构的相关知识和原理&#xff0c;下载了web服务器软件&#xff1a;Tomcat&#xff0c;并对其进行了配置。 系统架构 包括&#xff1a;C/S架构 和 B/S架构。 C/S架构&#xff1a; Client / Server&#xff0…...

【Shell脚本】根据起止日期获取Alert日志内容

【Shell脚本】根据起止日期获取Alert日志内容 根据输入的起止日期字符串&#xff0c;检索Oracle告警日志&#xff0c;打印中间的日志行内容。 #!/bin/bash # $1 START_TIME_STR, e.g. "Oct 17 07:" # $2 END_TIME_STR, e.g. "Oct 17 08:" source /home/o…...

Library projects cannot set applicationId. applicationId is set to

Library projects cannot set applicationId. applicationId is set to com.xxx.library_cache in default config. 删掉即可...

【兔子王赠书第2期】《案例学Python(基础篇)》

文章目录 前言推荐图书本书特色本书目录本书样章本书读者对象粉丝福利丨评论免费赠书尾声 前言 随着人工智能和大数据的蓬勃发展&#xff0c;Python将会得到越来越多开发者的喜爱和应用。身边有很多朋友都开始使用Python语言进行开发。正是因为Python是一门如此受欢迎的编程语…...

用户行为数据案例

一、环境要求 HadoopHiveSparkHBase 开发环境。 二、数据描述 本数据集包含了2017-09-11至2017-12-03之间有行为的约5458位随机用户的所有行为&#xff08;行为包括点击、购买、加购、喜欢&#xff09;。数据集的每一行表示一条用户行为&#xff0c;由用户ID、商品ID、商品类…...

selenium教程 —— css定位

说明&#xff1a;本篇博客基于selenium 4.1.0 selenium-css定位 element_css driver.find_element(By.CSS_SELECTOR, css表达式) 复制代码 css定位说明 selenium中的css定位&#xff0c;实际是通过css选择器来定位到具体元素&#xff0c;css选择器来自于css语法 css定位优点…...

Leetcode 1834. Single-Threaded CPU (堆好题)

Single-Threaded CPU Medium You are given n​​​​​​ tasks labeled from 0 to n - 1 represented by a 2D integer array tasks, where tasks[i] [enqueueTimei, processingTimei] means that the i​​​​​​th​​​​ task will be available to process at enque…...

21-数据结构-内部排序-交换排序

简介&#xff1a;主要根据两个数据进行比较从而交换彼此位置&#xff0c;以此类推&#xff0c;交换完全部。主要有冒泡和快速排序两种。 目录 一、冒泡排序 1.1简介&#xff1a; 1.2代码&#xff1a; 二、快速排序 1.1简介&#xff1a; 1.2代码&#xff1a; 一、冒泡排序…...

5-k8s-探针介绍

文章目录 一、探针介绍二、探针类型三、探针定义方式四、探针实例五、启动探针测试六、存活探针测试七、就绪探针测试 一、探针介绍 概念 在 Kubernetes 中 Pod 是最小的计算单元&#xff0c;而一个 Pod 又由多个容器组成&#xff0c;相当于每个容器就是一个应用&#xff0c;应…...

【网络安全 --- MySQL数据库】网络安全MySQL数据库应该掌握的知识,还不收藏开始学习。

四&#xff0c;MySQL 4.1 mysql安装 #centos7默认安装的是MariaDB-5.5.68或者65&#xff0c; #查看版本的指令&#xff1a;[rootweb01 bbs]# rpm -qa| grep mariadb #安装mariadb的最新版&#xff0c;只是更新了软件版本&#xff0c;不会删除之前原有的数据。 #修改yum源的配…...

【MyBatis系列】- 什么是MyBatis

【MyBatis系列】- 什么是MyBatis 文章目录 【MyBatis系列】- 什么是MyBatis一、学习MyBatis知识必备1.1 学习环境准备1.2 学习前掌握知识二、什么是MyBatis三、持久层是什么3.1 为什么需要持久化服务3.2 持久层四、Mybatis的作用五、MyBatis的优点六、参考文档一、学习MyBatis知…...

【Linux】Ubuntu美化bash【教程】

【Linux】Ubuntu美化bash【教程】 文章目录 【Linux】Ubuntu美化bash【教程】1. 查看当前环境中是否有bash2. 安装Synth-Shell3. 配置Synth-Shell4. 取消greeterReference 1. 查看当前环境中是否有bash 查看当前使用的bash echo $SHELL如下所示 sjhsjhR9000X:~$ echo $SHELL…...

微信小程序仿苹果负一屏由弱到强的高斯模糊

进入下面小程序可以体验效果&#xff0c;然后进入更多。查看模糊效果 一、创建小程序组件 二、代码 wxml: <view class"topBar-15"></view> <view class"topBar-14"></view> <view class"topBar-13"></view&…...

js中的new方法

new方法的作用&#xff1a;创建一个实例对象&#xff0c;并继承原对象的属性和方法&#xff1b; new对象内部操作&#xff1a; 1&#xff0c;创建一个新对象&#xff0c;将新对象的proto属性指向原对象的prototype属性&#xff1b; 2&#xff0c;构造函数执行环境中的this指向…...

机器学习-无监督算法之降维

降维&#xff1a;将训练数据中的样本从高维空间转换到低维空间&#xff0c;降维是对原始数据线性变换实现的。为什么要降维&#xff1f;高维计算难&#xff0c;泛化能力差&#xff0c;防止维数灾难优点&#xff1a;减少冗余特征&#xff0c;方便数据可视化&#xff0c;减少内存…...

ubuntu20.04下Kafka安装部署及基础使用

Ubuntu安装kafka基础使用 kafka 安装环境基础安装下载kafka解压文件修改配置文件启动kafka创建主题查看主题发送消息接收消息 工具测试kafka Assistant 工具连接测试基础连接连接成功查看topic查看消息查看分区查看消费组 Idea 工具测试基础信息配置信息当前消费组发送消息消费…...

汉得欧洲x甄知科技 | 携手共拓全球化布局,助力出海中企数智化发展

HAND Europe 荣幸获得华为云颁发的 GrowCloud 合作伙伴奖项&#xff0c;进一步巩固了其在企业数字化领域的重要地位。于 2023 年 10 月 5 日&#xff0c;HAND Europe 参加了华为云荷比卢峰会&#xff0c;并因其在全球拓展方面的杰出贡献而荣获 GrowCloud 合作伙伴奖项的认可。 …...

【Javascript保姆级教程】显示类型转换和隐式类型转换

文章目录 前言一、显式类型转换1.1 字符串转换1.2 数字转换1.3 布尔值转换 二、隐式类型转换2.1 数字与字符串相加2.2 布尔值与数字相乘 总结 前言 JavaScript是一种灵活的动态类型语言&#xff0c;这意味着变量的数据类型可以在运行时自动转换&#xff0c;或者通过显式类型转…...

C++算法前缀和的应用:分割数组的最大值的原理、源码及测试用例

分割数组的最大值 相关知识点 C算法&#xff1a;前缀和、前缀乘积、前缀异或的原理、源码及测试用例&#xff1a;付视频课程 二分 过些天整理基础知识 题目 给定一个非负整数数组 nums 和一个整数 m &#xff0c;你需要将这个数组分成 m 个非空的连续子数组。 设计一个算法…...

gitlab自编译 源码下载

网上都是怎么用 gitlab&#xff0c;但是实际开发中有需要针对 gitlab 进行二次编译自定义实现功能的想法。 搜索了网上的资料以及在官网的查找&#xff0c;查到了如下 gitlab 使用 ruby 开发。 gitlab 下载包 gitlab/gitlab-ce - Packages packages.gitlab.com gitlab/gitl…...