IT数码 购物 网址 头条 软件 日历 阅读 图书馆
TxT小说阅读器
↓语音阅读,小说下载,古典文学↓
图片批量下载器
↓批量下载图片,美女图库↓
图片自动播放器
↓图片自动播放器↓
一键清除垃圾
↓轻轻一点,清除系统垃圾↓
开发: C++知识库 Java知识库 JavaScript Python PHP知识库 人工智能 区块链 大数据 移动开发 嵌入式 开发工具 数据结构与算法 开发测试 游戏开发 网络协议 系统运维
教程: HTML教程 CSS教程 JavaScript教程 Go语言教程 JQuery教程 VUE教程 VUE3教程 Bootstrap教程 SQL数据库教程 C语言教程 C++教程 Java教程 Python教程 Python3教程 C#教程
数码: 电脑 笔记本 显卡 显示器 固态硬盘 硬盘 耳机 手机 iphone vivo oppo 小米 华为 单反 装机 图拉丁
 
   -> JavaScript知识库 -> React RouterV6 -> 正文阅读

[JavaScript知识库]React RouterV6

1.Router(V6)简介

在这里插入图片描述在这里插入图片描述

2.对比V5

在这里插入图片描述

3.用法详解

3.1一级路由和多级路由

在这里插入图片描述
安装
在这里插入图片描述

3.2指定默认路由

在这里插入图片描述
在这里插入图片描述

3.3路由重定向

在这里插入图片描述

3.4 404

在这里插入图片描述

3.5代码

import React from 'react'
import {Navigate, Route,Routes,} from "react-router-dom"
import Film from "../views/Film"
import Cinema from "../views/Cinema"
import Center from "../views/Center"
import NotFound from "../views/NotFound"
export default function Mrouter() {
  return (
    <div>
      <Routes>
        {/* 默认路由 */}
        {/* <Route index element={<Film />}></Route> */}
        <Route path="/film" element={<Film />}></Route>
        <Route path="/cinema" element={<Cinema />}></Route>
        <Route path="/center" element={<Center />}></Route>
        {/* 匹配到/ 重定向到film页面 */}
        <Route path="/" element={<Navigate to="/film"></Navigate>}></Route>
        {/* 都没匹配上 跳转到404页面  *符号是全部的意思,所以将这个放在最后,以上路由都没匹配上才执行到这一步的路由匹配*/}
        <Route path="*" element={<NotFound/>}></Route>
      </Routes>
    </div>
  );
}

3.6路由嵌套(maizuo.com为例)

路由嵌套有两种形式
二级路由不代表路由嵌套,如果组件整个被替换,只是路径上嵌套比如/cinema/search,搜索组件完全替换了cinema组件,而film组件下的两个二级路由是Nowplaying和Comingsoon,这两个组件共用film组件的轮播图
比如说这两个组件,上面部分的轮播图是共用的,只有下面的内容是替换的
在这里插入图片描述在这里插入图片描述那么会将这两个组件放在film组件的哪个位置呢,怎么让它们在轮播图的下面而不是上面,就需要路由容器占位了
在这里插入图片描述相对路径的写法:
在这里插入图片描述

在这里插入图片描述

像这种组件完全替换,实际上是平级的组件,但是路径是二级路径的代码需要这样写:
在这里插入图片描述在这里插入图片描述在这里插入图片描述

import React from "react";
import { Navigate, Route, Routes } from "react-router-dom";
import Film from "../views/Film";
import Cinema from "../views/Cinema";
import Search from "../views/Search";
import Center from "../views/Center";
import NotFound from "../views/NotFound";
import Nowplaying from "../views/films/Nowplaying";
import Comingsoon from "../views/films/Comingsoon";
export default function Mrouter() {
  return (
    <div>
      <Routes>
        {/* 默认路由 */}
        {/* <Route index element={<Film />}></Route> */}
        <Route path="/film" element={<Film />}>
          {/* 默认路由 没有匹配子组件时,只匹配到父组件默认为Nowplaying路由 */}
          {/* <Route index element={<Nowplaying />}/> */}
          {/* 默认路由写法2 重定向路径必须写绝对路径*/}
          {/* <Route path="" element={<Navigate to="/film/nowplaying"></Navigate>} /> */}
          {/* 默认路由写法3 */}
          <Route index element={<Navigate to="/film/nowplaying"></Navigate>} />
          {/* 这样的写法是将这两个组件加载到film组件中来 */}
          <Route path="nowplaying" element={<Nowplaying />} />
          <Route path="comingsoon" element={<Comingsoon />} />
        </Route>

        <Route path="/cinema" element={<Cinema />}></Route>
        <Route path="/cinema/search" element={<Search />}></Route>
        <Route path="/center" element={<Center />}></Route>
        {/* 匹配到/ 重定向到film页面 */}
        <Route path="/" element={<Navigate to="/film"></Navigate>}></Route>
        {/* 都没匹配上 跳转到404页面  *符号是全部的意思,所以将这个放在最后,以上路由都没匹配上才执行到这一步的路由匹配*/}
        <Route path="*" element={<NotFound />}></Route>
      </Routes>
    </div>
  );
}

4.声明式导航和编程式导航

a标签相当于于声明式导航
location相当编程式导航
在这里插入图片描述

4.1声明式导航

使用NavLink选项卡,选中哪个哪个会自动有class=active,可以给active设置高亮颜色,但是由于很多人一起开发,别人的acive也会影响到我们的颜色,所以要取另外的名,className给NavLink传了个属性,支持回调函数的写法,回调函数接收到一个形参,解构出来isActive
在这里插入图片描述
在这里插入图片描述Tabbar.js

import React from 'react'
import {NavLink} from "react-router-dom"
export default function Tabbar() {
  return (
    <div>
      <ul>
        <li>
          {" "}
          <NavLink
            to="/film"
            className={({ isActive }) => (isActive ? "xxactive" : "")}
          >
            电影
          </NavLink>
        </li>
        <li>
          {" "}
          <NavLink
            to="/cinema"
            className={({ isActive }) => (isActive ? "xxactive" : "")}
          >
            影院
          </NavLink>
        </li>
        <li>
          <NavLink
            to="/center"
            className={({ isActive }) => (isActive ? "xxactive" : "")}
          >
            我的
          </NavLink>
        </li>
      </ul>
    </div>
  );
}

在这里插入图片描述

4.2编程式导航

更适合携带参数跳转到另一个页面
使用useNavigate跳转,还有一种方法是this.props.history,但是这种方法有的组件不一定能拿到this.props的值

4.21query(URL Search传参 /detail?id=1000)

在这里插入图片描述
在这里插入图片描述
Nowplaying.js

import React, { useEffect, useState} from "react";
import {useNavigate} from "react-router-dom"
import axios from "axios";
export default function Nowplaying() {
  const [list, setList] = useState([]);
  const navigate = useNavigate();
  useEffect(() => {
    axios(
      "https://m.maizuo.com/gateway?cityId=110100&pageNum=1&pageSize=10&type=1&k=4238375",
      {
        headers: {
          "X-Client-Info":
            '{"a":"3000","ch":"1002","v":"5.2.0","e":"16460383564094280654127105","bc":"110100"}',
          "X-Host": "mall.film-ticket.film.list",
        },
      }
    ).then((res) => {
      // console.log(res.data.data.films)
      //setState函数是异步的,这样写是同步写法,可以及时获取到List的值
      // setList((list)=>{
      //   list = res.data.data.films;
      //   console.log(list);
      //   debugger;
      //   return list;
      // });
      // 这样是异步写法,打印的list的值还为空
      setList(res.data.data.films);
      //  console.log(list);
    });
  }, []);
  function handleChangePage(id) {
    navigate(`/detail/?id=${id}`);
  }
  return (
    <div>
      <ul>
        {list.map((item) => (
          <li
            key={item.filmId}
            onClick={() => {
              handleChangePage(item.filmId)
            }}
          >
            {item.name}
          </li>
        ))}
      </ul>
    </div>
  );
}

Detail.js

import React from "react";
import { useSearchParams } from "react-router-dom";

export default function Detail() {
  const [searchParams, setsearchParams] = useSearchParams();
  // console.log(window.location.href);
  //拿到上个页面跳转过来传过来的id
  console.log(searchParams.get("id"));
  return <div>Detail
    <button onClick={()=>setsearchParams({'id':1000})}>猜你喜欢</button>
  </div>;
}

4.22路由传参 (/detail/1000) 动态路由

在这里插入图片描述Nowplaying.js

import React, { useEffect, useState} from "react";
import {useNavigate} from "react-router-dom"
import axios from "axios";
export default function Nowplaying() {
  const [list, setList] = useState([]);
  const navigate = useNavigate();
  useEffect(() => {
    axios(
      "https://m.maizuo.com/gateway?cityId=110100&pageNum=1&pageSize=10&type=1&k=4238375",
      {
        headers: {
          "X-Client-Info":
            '{"a":"3000","ch":"1002","v":"5.2.0","e":"16460383564094280654127105","bc":"110100"}',
          "X-Host": "mall.film-ticket.film.list",
        },
      }
    ).then((res) => {
      // console.log(res.data.data.films)
      //setState函数是异步的,这样写是同步写法,可以及时获取到List的值
      // setList((list)=>{
      //   list = res.data.data.films;
      //   console.log(list);
      //   debugger;
      //   return list;
      // });
      // 这样是异步写法,打印的list的值还为空
      setList(res.data.data.films);
      //  console.log(list);
    });
  }, []);
  function handleChangePage(id) {
    //query传参
    // navigate(`/detail/?id=${id}`);
    //路由传参
    navigate(`/detail/${id}`)

  }
  return (
    <div>
      <ul>
        {list.map((item) => (
          <li
            key={item.filmId}
            onClick={() => {
              handleChangePage(item.filmId)
            }}
          >
            {item.name}
          </li>
        ))}
      </ul>
    </div>
  );
}

Detail.js

import React from "react";
import { useSearchParams, useParams, useNavigate } from "react-router-dom";

export default function Detail() {
  const navigate =useNavigate()
  // console.log(window.location.href);
  // query传参
  // const [searchParams, setsearchParams] = useSearchParams();
  //拿到上个页面跳转过来传过来的id
  //console.log(searchParams.get("id"));

  // 路由传参拿到上个页面跳转过来传来的id的方法
  const params=useParams()
  // 看看params里是什么
  // console.log(params);
  console.log(params.myid);
  return (
    <div>
      Detail
      {/* query传参 */}
      {/* <button onClick={() => setsearchParams({ id: 1000 })}>猜你喜欢</button> */}
      {/* 路由传参的跳转到另一个路由地址就得用navigate了 */}
      <button onClick={() => navigate("/detail/1000")}>猜你喜欢</button>
    </div>
  );
}

5.路由拦截

在这里插入图片描述

自己封装的Redirect组件
在这里插入图片描述
Login.js

import React from 'react'
import { useNavigate } from 'react-router-dom';
import Redirect from '../components/Redirect'

export default function Login() {
  const navigate=useNavigate()
  return (
    <div>
      <input type="text"></input>
      
      <button type="button" onClick={()=>{localStorage.setItem("token","ss");
        navigate("/center");
    }}>登录</button>
      
    </div>
  )
}

index.js

import React from "react";
import { Navigate, Route, Routes } from "react-router-dom";
import Redirect from "../components/Redirect"
import Film from "../views/Film";
import Cinema from "../views/Cinema";
import Search from "../views/Search";
import Center from "../views/Center";
import NotFound from "../views/NotFound";
import Detail from "../views/Detail"
import Login from "../views/Login"
import Nowplaying from "../views/films/Nowplaying";
import Comingsoon from "../views/films/Comingsoon";
export default function Mrouter() {
  return (
    <div>
      <Routes>
        {/* 默认路由 */}
        {/* <Route index element={<Film />}></Route> */}
        <Route path="/film" element={<Film />}>
          {/* 默认路由 没有匹配子组件时,只匹配到父组件默认为Nowplaying路由 */}
          {/* <Route index element={<Nowplaying />}/> */}
          {/* 默认路由写法2 重定向路径必须写绝对路径*/}
          {/* <Route path="" element={<Navigate to="/film/nowplaying"></Navigate>} /> */}
          {/* 默认路由写法3 */}
          <Route index element={<Navigate to="/film/nowplaying"></Navigate>} />
          {/* 这样的写法是将这两个组件加载到film组件中来 */}
          <Route path="nowplaying" element={<Nowplaying />} />
          <Route path="comingsoon" element={<Comingsoon />} />
        </Route>

        <Route path="/cinema" element={<Cinema />}></Route>
        <Route path="/cinema/search" element={<Search />}></Route>
        {/* 判断有无token值,有的话isAuth则为真显示Center组件,否则重定向到登录页面,
        但是这样写如果没有token,在登录页面输入登录之后,并不能跳转到center组件来,因为element里的是组件只渲染了一次,
        之前的render写法里面是回调函数,匹配一次就会执行一次,所以不能这么写 */}
        {/* <Route
          path="/center"
          element={isAuth() ? <Center /> : <Redirect to="/login"></Redirect>}
        ></Route> */}
        <Route
          path="/center"
          element={<AuthComponent>
            <Center></Center>
          </AuthComponent>}></Route>
        <Route path="/login" element={<Login />}></Route>
        {/* <Route path="/detail" element={<Detail />}></Route> */}
        {/* 路由传参 */}
        <Route path="/detail/:myid" element={<Detail />}></Route>
        {/* 匹配到/ 重定向到film页面 */}
        <Route path="/" element={<Navigate to="/film"></Navigate>}></Route>
        {/* 都没匹配上 跳转到404页面  *符号是全部的意思,所以将这个放在最后,以上路由都没匹配上才执行到这一步的路由匹配*/}
        <Route path="*" element={<NotFound />}></Route>
      </Routes>
    </div>
  );
}
// function isAuth(){
//   return localStorage.getItem("token")
// }
// 一旦匹配到center路径了,就要渲染AuthComponent组件,判断里面有token,就把内部插槽组件return出去,为假就重定向
// 如果没有token值,当登录页面重新设置token后匹配center路径,组件又会重新渲染,判断token值,有就渲染
//路由拦截组件的封装
// props解构赋值出children
function AuthComponent({children}){
  const isLogin=localStorage.getItem("token")
  return isLogin?children:<Redirect to="/login"/>
}

6.路由模式

BrowserRouter更像平时我们访问的路径,但是需要后端同步配置一下,不然会返回404
在这里插入图片描述HashRouter会让路径多一个#符号
在这里插入图片描述在这里插入图片描述

7.withRouter/类组件(自己封装)跳转方法

被Route包裹的组件会被赋予this.props.history,match等方法,但是被它包裹的组件的子组件没有这个方法,用withRouter组件可以实现,但是V6中没有这个方法了,需要自己封装
函数组件中如果要跳转网页,其实也用不到,可以用navigate(任何组件都可用),但是类组件没有hooks,需要封装withRouter方法

函数组件:将每个li变成一个新的组件FilmItem
Nowplaying.js

import React, { useEffect, useState} from "react";
import axios from "axios";
import FilmItem from "./FilmItem";
export default function Nowplaying() {
  const [list, setList] = useState([]);
  useEffect(() => {
    axios(
      "https://m.maizuo.com/gateway?cityId=110100&pageNum=1&pageSize=10&type=1&k=4238375",
      {
        headers: {
          "X-Client-Info":
            '{"a":"3000","ch":"1002","v":"5.2.0","e":"16460383564094280654127105","bc":"110100"}',
          "X-Host": "mall.film-ticket.film.list",
        },
      }
    ).then((res) => {
      // console.log(res.data.data.films)
      //setState函数是异步的,这样写是同步写法,可以及时获取到List的值
      // setList((list)=>{
      //   list = res.data.data.films;
      //   console.log(list);
      //   debugger;
      //   return list;
      // });
      // 这样是异步写法,打印的list的值还为空
      setList(res.data.data.films);
      //  console.log(list);
    });
  }, []);
 
  return (
    <div>
      <ul>
        {list.map((item) => (
          // 将item传给FilmItem组件
         <FilmItem key={item.filmId} {...item}></FilmItem>
        ))}
      </ul>
    </div>
  );
}

FilmItem.js

import React from "react";
import { useNavigate } from "react-router-dom";
export default function FilmItem(item) {
  const navigate = useNavigate();
  function handleChangePage(id) {
    //query传参
    // navigate(`/detail/?id=${id}`);
    //路由传参
    navigate(`/detail/${id}`);
  }
  return (
    <div>
      <li
        onClick={() => {
          handleChangePage(item.filmId);
        }}
      >
        {item.name}
      </li>
    </div>
  );
}

类组件封装withrouter
在这里插入图片描述
withRouter.js

import React from 'react'
import { useNavigate,useParams,useLocation } from 'react-router-dom'
export default function withRouter(Component) {
  // 高阶函数高阶组件  接收一个组件,return出去一个组件,这里return了一个函数组件function(props){}
  // Nowplaying所有属性传到这了
  return function (props){
    const push = useNavigate();
    const params = useParams();
    const location = useLocation()
    // history里传个对象{push:push,params:params}
    return <Component {...props} history={{ push, params,location }} />;
  }
}
//withRouter(FilmItem)

FilmItem.js

// import React from "react";
// import { useNavigate } from "react-router-dom";
// export default function FilmItem(item) {
//   const navigate = useNavigate();
//   function handleChangePage(id) {
//     //query传参
//     // navigate(`/detail/?id=${id}`);
//     //路由传参
//     navigate(`/detail/${id}`);
//   }
//   return (
//     <div>
//       <li
//         onClick={() => {
//           handleChangePage(item.filmId);
//         }}
//       >
//         {item.name}
//       </li>
//     </div>
//   );
// }
// 类组件
import React, { Component } from "react";
import withRouter from "../../components/withRouter";
 class FilmItem extends Component {
  handleChangePage(id){
    // console.log(id);
    this.props.history.push(`/detail/${id}`);
    //  this.props.history.push跳转页面
    //  this.props.history.match获取参数
    //  this.props.history.location获取当前路由
    console.log(this.props.history);
  }
  render() {
    console.log(this.props);
    return (
      <div>
        <li
          onClick={() => {
            this.handleChangePage(this.props.filmId);
          }}
        >
          {this.props.name}
        </li>
      </div>
    );
  }
  
}
export default withRouter(FilmItem)

8.路由懒加载

由于路由过多,导致首页加载慢,渲染慢,我们使用路由懒加载后是按需加载且只加载一次
在这里插入图片描述可以看到再次进入cinema不再加载,只加载了第一次
在这里插入图片描述写成路由懒加载的写法后:
router文件夹下的index.js

import React from "react";
import { Navigate, Route, Routes } from "react-router-dom";
import Redirect from "../components/Redirect";
// import Film from "../views/Film";
// import Cinema from "../views/Cinema";
// import Search from "../views/Search";
// import Center from "../views/Center";
// import NotFound from "../views/NotFound";
// import Detail from "../views/Detail";
// import Login from "../views/Login";
// import Nowplaying from "../views/films/Nowplaying";
// import Comingsoon from "../views/films/Comingsoon";
export default function Mrouter() {
  return (
    <div>
      <Routes>
        {/* 默认路由 */}
        {/* <Route index element={<Film />}></Route> */}
        <Route path="/film" element={LazyLoad("Film")}>
          {/* 默认路由 没有匹配子组件时,只匹配到父组件默认为Nowplaying路由 */}
          {/* <Route index element={<Nowplaying />}/> */}
          {/* 默认路由写法2 重定向路径必须写绝对路径*/}
          {/* <Route path="" element={<Navigate to="/film/nowplaying"></Navigate>} /> */}
          {/* 默认路由写法3 */}
          <Route index element={<Navigate to="/film/nowplaying"></Navigate>} />
          {/* 这样的写法是将这两个组件加载到film组件中来 */}
          {/* 路由懒加载写法:Nowplaying是films文件夹下的 */}
          {/* <Route path="nowplaying" element={<Nowplaying />} /> */}
          <Route path="nowplaying" element={LazyLoad("films/Nowplaying")} />
          <Route path="comingsoon" element={LazyLoad("films/Comingsoon")} />
        </Route>
        {/* 传进去的值是字符串,函数需要字符串,而不是变量,且找不到这个变量,如果有a=1,这里传变量a,函数就会找到1 */}
        <Route path="/cinema" element={LazyLoad("Cinema")}></Route>
        <Route path="/cinema/search" element={LazyLoad("Search")}></Route>
        {/* 判断有无token值,有的话isAuth则为真显示Center组件,否则重定向到登录页面,
        但是这样写如果没有token,在登录页面输入登录之后,并不能跳转到center组件来,因为element里的是组件只渲染了一次,
        之前的render写法里面是回调函数,匹配一次就会执行一次,所以不能这么写 */}
        {/* <Route
          path="/center"
          element={isAuth() ? <Center /> : <Redirect to="/login"></Redirect>}
        ></Route> */}
        <Route
          path="/center"
          element={<AuthComponent>{LazyLoad("Center")}</AuthComponent>}
        ></Route>
        <Route path="/login" element={LazyLoad("Login")}></Route>
        {/* <Route path="/detail" element={<Detail />}></Route> */}
        {/* 路由传参 */}
        <Route path="/detail/:myid" element={LazyLoad("Detail")}></Route>
        {/* 匹配到/ 重定向到film页面 */}
        <Route path="/" element={<Navigate to="/film"></Navigate>}></Route>
        {/* 都没匹配上 跳转到404页面  *符号是全部的意思,所以将这个放在最后,以上路由都没匹配上才执行到这一步的路由匹配*/}
        <Route path="*" element={LazyLoad("NotFound")}></Route>
      </Routes>
    </div>
  );
}
// function isAuth(){
//   return localStorage.getItem("token")
// }
// 一旦匹配到center路径了,就要渲染AuthComponent组件,判断里面有token,就把内部插槽组件return出去,为假就重定向
// 如果没有token值,当登录页面重新设置token后匹配center路径,组件又会重新渲染,判断token值,有就渲染
//路由拦截组件的封装
// props解构赋值出children
function AuthComponent({ children }) {
  const isLogin = localStorage.getItem("token");
  return isLogin ? children : <Redirect to="/login" />;
}
// 路由懒加载的封装
const LazyLoad = (path) => {
  const Comp = React.lazy(() => import(`../views/${path}`));
  return (
    <React.Suspense fallback={<>加载中...</>}>
      <Comp />
    </React.Suspense>
  );
};

9.useRoutes钩子配置路由

路由改成钩子配置路由
在这里插入图片描述router下的index.js

import React from "react";
import { useRoutes, Navigate } from "react-router-dom";
import Redirect from "../components/Redirect";
export default function Mrouter() {
  const element = useRoutes([
    {
      path: "/film",
      element: LazyLoad("Film"),
      // 子路径
      children: [
        // 重定向
        {
          path: "",
          element: <Redirect to="/film/nowplaying"></Redirect>,
        },
        {
          path: "nowplaying",
          // 文件夹films下的Nowplaying  路径
          element: LazyLoad("films/Nowplaying"),
        },
        {
          path: "comingsoon",
          element: LazyLoad("films/Comingsoon"),
        },
      ],
    },
    {
      path: "/cinema",
      element: LazyLoad("Cinema"),
    },
    {
      path: "/cinema/search",
      element: LazyLoad("Search"),
    },
    {
      path: "/login",
      element: LazyLoad("Login"),
    },
    {
      path: "/center",
      element: <AuthComponent>{LazyLoad("Center")}</AuthComponent>,
    },
    {
      path: "/detail/:myid",
      element: LazyLoad("Detail"),
    },
    {
      path: "/",
      element: <Navigate to="/film"></Navigate>,
    },
    {
      path: "*",
      element: LazyLoad("NotFound"),
    },
  ]);
  return element;
}
function AuthComponent({ children }) {
  const isLogin = localStorage.getItem("token");
  return isLogin ? children : <Redirect to="/login" />;
}
// 路由懒加载的封装
const LazyLoad = (path) => {
  const Comp = React.lazy(() => import(`../views/${path}`));
  return (
    <React.Suspense fallback={<>加载中...</>}>
      <Comp />
    </React.Suspense>
  );
};

  JavaScript知识库 最新文章
ES6的相关知识点
react 函数式组件 & react其他一些总结
Vue基础超详细
前端JS也可以连点成线(Vue中运用 AntVG6)
Vue事件处理的基本使用
Vue后台项目的记录 (一)
前后端分离vue跨域,devServer配置proxy代理
TypeScript
初识vuex
vue项目安装包指令收集
上一篇文章      下一篇文章      查看所有文章
加:2022-04-06 16:08:57  更:2022-04-06 16:13:19 
 
开发: C++知识库 Java知识库 JavaScript Python PHP知识库 人工智能 区块链 大数据 移动开发 嵌入式 开发工具 数据结构与算法 开发测试 游戏开发 网络协议 系统运维
教程: HTML教程 CSS教程 JavaScript教程 Go语言教程 JQuery教程 VUE教程 VUE3教程 Bootstrap教程 SQL数据库教程 C语言教程 C++教程 Java教程 Python教程 Python3教程 C#教程
数码: 电脑 笔记本 显卡 显示器 固态硬盘 硬盘 耳机 手机 iphone vivo oppo 小米 华为 单反 装机 图拉丁

360图书馆 购物 三丰科技 阅读网 日历 万年历 2024年11日历 -2024/11/24 2:26:40-

图片自动播放器
↓图片自动播放器↓
TxT小说阅读器
↓语音阅读,小说下载,古典文学↓
一键清除垃圾
↓轻轻一点,清除系统垃圾↓
图片批量下载器
↓批量下载图片,美女图库↓
  网站联系: qq:121756557 email:121756557@qq.com  IT数码