Jelajahi Sumber

react day5:数据获取

daxia 1 tahun lalu
induk
melakukan
4eca748292

+ 288 - 0
20_React.js_VIP22/React路由.md

@@ -0,0 +1,288 @@
+# React路由
+
+> React框架配套的路由系统插件 是 React Router。目前版本为V6。
+
+## 1. 基本使用
+
+1. 创建路由实例
+
+   createBroswerRouter或者createHashRouter
+
+2. 引入RouterProvider组件
+
+3. 定义相关路由组件
+
+```jsx
+// 定义路由的模块 router/index.js
+import { createBrowserRouter } from 'react-router-dom';
+import Home from '../pages/Home';
+import About from '../pages/About';
+
+const routes = [
+  {
+    path: '/',
+    element: <Home />,
+  },
+  {
+    path: '/about',
+    element: <About />,
+  },
+];
+
+const router = createBrowserRouter(routes);
+
+export default router;
+
+```
+
+```jsx
+// 入口文件 
+import { RouterProvider } from 'react-router-dom';
+import router from './router';
+
+const root = ReactDOM.createRoot(document.getElementById('root'));
+root.render(
+  <React.StrictMode>
+    <RouterProvider router={router} />
+  </React.StrictMode>
+);
+```
+
+## 2. 嵌套路由
+
+实现分2步走:
+
+1. 在指定父路由对象上添加children属性,值为路由对象数组;
+2. 在父路由组件上通过Outlet组件来指定子路由组件的渲染位置
+
+```jsx
+// 定义嵌套子路由
+const routes = [
+  {
+    path: '/',
+    element: <Layout />,
+    children: [
+      // 这是索引路由
+      {
+        // path: '',
+        index: true,
+        element: <Home />,
+      },
+      {
+        path: 'about',
+        element: <About />,
+      },
+    ],
+  },
+];
+```
+
+```jsx
+// 父组件 -- 布局组件
+import Header from '../components/Header';
+import { Outlet } from 'react-router-dom';
+
+function Layout() {
+  return (
+    <div className="layout">
+      <header>
+        <Header />
+      </header>
+      <main style={{ padding: '20px' }}>
+        {/* 渲染子路由组件 */}
+        <Outlet />
+      </main>
+    </div>
+  );
+}
+
+export default Layout;
+```
+
+## 3. 动态路由
+
+> 当实现一个类似详情页的需求时,我们可能会需要使用到动态路由
+
+例如,在商品列表页中点击某一商品获取详情。
+
+1. 假设 商品列表 对应的 路由是 `/products`
+2. 需要单独页面显示某一商品的详情,此时 路由为这么定义:`/products/details/{product-id}` 因此这就是动态路由。
+3. 定义路由时,使用":"去定义路由中的参数,即`{path: '/products/details/:productId'}`
+4. 访问时,会自动匹配。成功后,会将路由参数与值解析出来,存储在params对象中
+5. 通过路由Hook `useParams` 来获取上面解析后的路由参数对象
+
+```jsx
+// 列表页代码
+function Products() {
+  let [products] = useState(initialProducts);
+  return (
+    <div className="products">
+      <table>
+        <thead>
+          <tr>
+            <th>#</th>
+            <th>Name</th>
+            <th>Price</th>
+            <th>Action</th>
+          </tr>
+        </thead>
+        <tbody>
+          {products.map((p, i) => (
+            <tr key={p.id}>
+              <td>{i + 1}</td>
+              <td>{p.name}</td>
+              <td>{p.price}</td>
+              <td>
+                <Link to={{ pathname: `/products/details/${p.id}` }}>详情</Link>
+              </td>
+            </tr>
+          ))}
+        </tbody>
+      </table>
+    </div>
+  );
+}
+
+export default Products;
+// 详情页代码
+import { useParams } from 'react-router-dom';
+
+function ProductDetail() {
+  const { productId } = useParams();
+  // console.log(params);
+  return (
+    <div className="detail">
+      <h3>详情页</h3>
+      <p>您正在浏览的商品ID:{productId}</p>
+    </div>
+  );
+}
+
+export default ProductDetail;
+```
+
+## 4. 查询参数
+
+> 在实际开发中,可以通过路由参数实现页面间数据传递;当然也可以通过查询参数。
+
+下面代码演示,如果通过查询参数实现页面间数据传递:
+
+```jsx
+<tbody>
+  {products.map((p, i) => (
+    <tr key={p.id}>
+      <td>{i + 1}</td>
+      <td>{p.name}</td>
+      <td>{p.price}</td>
+      <td>
+        {/* 1 路由参数 在页面间传递数据 */}
+        {/* <Link to={{ pathname: `/products/details/${p.id}` }}>详情</Link> */}
+        {/* 2 查询参数 在页面间传递数据 */}
+        {/* <Link to={`/products/detail?pid=${p.id}`}>详情</Link> */}
+        <Link to={{ pathname: '/products/detail', search: '?id=1' }}>
+          详情
+        </Link>
+      </td>
+    </tr>
+  ))}
+</tbody>
+```
+
+```jsx
+let [searchParams] = useSearchParams();
+<div className="detail">
+  {/* <NavLink to="..">Back</NavLink> */}
+  <a
+    href="#"
+    onClick={() => {
+      // go('/products');
+      // go({ pathname: '/products' });
+      go(-1);
+    }}
+  >
+    Back
+  </a>
+  <h3>详情页</h3>
+  <p>
+    您正在浏览的商品ID:{searchParams.get('pid')}
+  </p>
+</div>
+```
+
+## 5. 页面导航
+
+### 5.1 声明式导航
+
+1. Link组件
+2. NavLink组件
+
+NavLink在使用时,更容易实现选中效果。
+
+```jsx
+const handleLinkStyle = ({ isActive, isPending }) =>
+  isPending ? 'pending' : isActive ? 'actived' : 'link';
+
+function Header() {
+  return (
+    <div className="header">
+      <ul>
+        <li>
+          <NavLink className={handleLinkStyle} to="/">
+            主页
+          </NavLink>
+        </li>
+        <li>
+          <NavLink className={handleLinkStyle} to={{ pathname: '/about' }}>
+            关于
+          </NavLink>
+        </li>
+        <li>
+          <NavLink className={handleLinkStyle} to={{ pathname: '/products' }}>
+            商品
+          </NavLink>
+        </li>
+      </ul>
+    </div>
+  );
+}
+```
+
+### 5.2 编程式导航
+
+通过 `useNavigate()`Hook 获取到 `navigate`函数,接着通过调用navigate函数实现页面跳转。
+
+```jsx
+const navigate = useNavigate();
+
+// navigate(to: Path, option?)
+// navigate(delta: number)
+navigate(-1)
+navigate(1)
+navigate('/login')
+navigate({pathname: '/login'})
+navigate({pathname: '/login', search: '?id=1'})
+navigate({pathname: '/login'}, {replace: true, state: {}})
+```
+
+## 6. 数据获取
+
+> 在路由系统中,如何选择时机去获取数据?
+>
+> 我们知道在VueRouter中,已经提供了两种方式,都是用户能够接收并且体验良好的方案
+>
+> 1. 在组件渲染后,添加Loading效果后去获取数据
+> 2. 在路由开启导航时先去获取数据,这样组件渲染时就会连同数据一起渲染出来了
+
+### 6.1 Loading效果
+
+### 6.2 Data Loading
+
+1. 在定义路由的时候,给路由对象添加loader数据加载器(值为函数),它会在在导航过程中启动数据加载。
+2. 在路由组件中,通过`useLoaderData()`Hook函数来获取上面loader返回的数据
+
+## 7. 页面访问权限
+
+## 8. 其他
+
+
+

+ 4 - 0
20_React.js_VIP22/day-5/code/react-router-demo/public/data.json

@@ -0,0 +1,4 @@
+[
+  { "id": 1, "name": "basketball", "price": "$29.9" },
+  { "id": 2, "name": "Iphone 15", "price": "$1299.9" }
+]

+ 5 - 0
20_React.js_VIP22/day-5/code/react-router-demo/src/components/Header.jsx

@@ -23,6 +23,11 @@ function Header() {
             商品
           </NavLink>
         </li>
+        <li>
+          <NavLink className={handleLinkStyle} to={{ pathname: '/data-fetch' }}>
+            数据获取
+          </NavLink>
+        </li>
       </ul>
     </div>
   );

+ 71 - 0
20_React.js_VIP22/day-5/code/react-router-demo/src/pages/DataLoading.jsx

@@ -0,0 +1,71 @@
+import { useState, useEffect } from 'react';
+import { NavLink, useLoaderData } from 'react-router-dom';
+
+function Loading() {
+  return <div>数据加载中...</div>;
+}
+
+function DataLoading() {
+  // let [products, setProducts] = useState([]);
+  // let [loading, setLoading] = useState(false);
+
+  // useEffect(() => {
+  //   // http请求前 设置Loading状态 为 true
+  //   setLoading(true);
+  //   fetch('/data.json')
+  //     .then((res) => res.json())
+  //     .then((products) => {
+  //       // console.log(products);
+  //       // 为了能看清Loading效果,延时1s修改products数据
+  //       setTimeout(() => {
+  //         setProducts(products);
+  //       }, 1000);
+  //     })
+  //     .finally(() => {
+  //       // 请求完成后,设置Loading状态 为 false
+  //       setLoading(false);
+  //     });
+  // }, []);
+
+  let products = useLoaderData();
+
+  return (
+    <div className="products">
+      <table>
+        <thead>
+          <tr>
+            <th>#</th>
+            <th>Name</th>
+            <th>Price</th>
+            <th>Action</th>
+          </tr>
+        </thead>
+        <tbody>
+          {products.length === 0 ? (
+            <tr>
+              <td colSpan={4}>
+                <Loading />
+              </td>
+            </tr>
+          ) : (
+            products.map((p, i) => (
+              <tr key={p.id}>
+                <td>{i + 1}</td>
+                <td>{p.name}</td>
+                <td>{p.price}</td>
+                <td>
+                  {/* 1 路由参数 在页面间传递数据 */}
+                  <NavLink to={{ pathname: `/products/details/${p.id}` }}>
+                    详情
+                  </NavLink>
+                </td>
+              </tr>
+            ))
+          )}
+        </tbody>
+      </table>
+    </div>
+  );
+}
+
+export default DataLoading;

+ 12 - 0
20_React.js_VIP22/day-5/code/react-router-demo/src/router/index.js

@@ -4,6 +4,7 @@ import Home from '../pages/Home';
 import About from '../pages/About';
 import Products from '../pages/Products';
 import ProductDetail from '../pages/ProductDetail';
+import DataLoading from '../pages/DataLoading';
 
 //! 1 基本使用
 // const routes = [
@@ -45,6 +46,17 @@ const routes = [
         path: '/products/detail',
         element: <ProductDetail />,
       },
+      {
+        path: '/data-fetch',
+        loader: async (ctx) => {
+          console.log(ctx);
+          // loaders can be async functions
+          const res = await fetch('/data.json');
+          const products = await res.json();
+          return products;
+        },
+        element: <DataLoading />,
+      },
     ],
   },
 ];