我是靠谱客的博主 成就海燕,最近开发中收集的这篇文章主要介绍React复习(12),觉得挺不错的,现在分享给大家,希望可以做个参考。

概述

(1)SPA应用

单页面web应用(Single Page Web Application)
是一种网站模型,可以动态重写当前页面,而不需要重新加载整个页面
相对于传统的Web应用它能做到前后端分离,后端只负责处理数据提供接口
页面逻辑和渲染都交给前端
SPA的一个重要实现就是改变路由时页面不刷新
实现这个功能通常有两种方式:windows.history对象或者location.hash

路由分类
(1)后端路由:node服务器端路由,用来处理客户端提交的请求并返回一个响应数据
(2)前端路由:浏览器端路由,当请求的是路由path时,浏览器端前没有发送http请求
     但界面会更新显示对应的组件

(2)Hash模式

//原理:利用改变#后面的值不触发网页重载,在客户端实现路由切换
import React from "react";
const Home = () => <div>Home</div>
const About = () => <div>About</div>
const Inbox = () => <div>Inbox</div>
class App extends React.Component {
  state = {
    path: window.location.hash.substr(1)
  }
  render() {
    let Content
    switch (this.state.path) {
      case '/home':
        Content = Home
        break;
      case '/about':
        Content = About
        break;
      case '/inbox':
        Content = Inbox
        break;
      default:
        break;
    }
    return (
      <div>
        <div>Header</div>
        <div>
          <ul>
            <li><a href="#/home">Home</a></li>
            <li><a href="#/about">About</a></li>
            <li><a href="#/inbox">Inbox</a></li>
          </ul>
        </div>
        <div>
          {/*根据变量内容与组件名相匹配来实时刷新*/}
          <Content/>
        </div>
      </div>
    )
  }
  componentDidMount() {
    //监听hash值,发生变化则执行回调函数
    window.addEventListener('hashchange', () => {
      const path = window.location.hash.substr(1)
      this.setState({
        path
      })
    })
  }
}
export default App;

(3)History模式

调用pushState可以在当前历史记录里添加一条url记录,但页面不会跳转
新增后若页面再发生一次跳转,那么再发生后腿时会跳转到新增url记录上
当前url状态对象可以通过history.state来访问
若要在前进或后退时访问历史记录对象则可以监听window.onpopstate事件
replaceState是替换历史记录中的最新的一条
import React from 'react';

const Home = () => <div>Home Component</div>;
const Page1 = () => <div>Page1 Component</div>;
const Page2 = () => <div>Page2 Component</div>;
const Page3 = () => <div>Page3 Component</div>;
const Page4 = () => <div>Page4 Component</div>;
const route = {
  '/': Home,
  '/page1': Page1,
  '/page2': Page2,
  '/page3': Page3,
  '/page4': Page4,
}
class App extends React.Component {
  constructor(props) {
    super(props)
    this.state = {
      //pathname就是请求地址,例如 /page1
      path: window.location.pathname
    }
    this.initPushstate()
  }
  //定义方法,在每一次压栈时触发
  initPushstate = () => {
    //保存原生的pushState
    let oldPushState = window.history.pushState;
    //重写pushState,添加回调功能
    window.history.pushState = function (state, title, pathname) {
      // 执行原生pushState
      let result = oldPushState.apply(window.history, arguments);
      // window添加回调onpushstate 
      if (typeof window.onpushstate === 'function') {
        window.onpushstate({ state, title, pathname, type: 'pushstate' });
      }
      return result;
    }
  }
  push1 = () => {
    window.history.pushState({ page: 'page1' }, { title: 'page1' }, '/page1')
    console.log(window.history.length);
  }
  push2 = () => {
    window.history.pushState({ page: 'page2' }, { title: 'page2' }, '/page2')
    console.log(window.history.length);
  }
  push3 = () => {
    window.history.pushState({ page: 'page3' }, { title: 'page3' }, '/page3')
    console.log(window.history.length);
  }
  //把最新的历史记录第一条替换,完成重定向,不会触发onpushstate
  //例如现在在2,点击3,再点击replace,然后点击回退,这时会跳转到2
  //因为把最新的历史记录3替换掉了
  replace = () => {
    window.history.replaceState({ page: 'page4' }, { title: 'page4' }, '/page4')
    console.log(window.history.length);
  }
  forward = () => {
    //加载历史列表中的下一个 URL ,方法的效果等价于点击前进按钮或调用 history.go(1)
    window.history.forward()
    console.log(window.history.length);
  }
  back = () => {
    window.history.back()
    console.log(window.history.length);
  }
  go = () => {
    // window.history.go(1)
    window.history.go(-1)
    console.log(window.history.length)
  }
  render() {
    let path = this.state.path
    let Outlet = route[path]
    return (
      <div>
        <h2>pathname:{this.state.route}</h2>
        <button onClick={this.push1}>pushstate1</button>
        <button onClick={this.push2}>pushstate2</button>
        <button onClick={this.push3}>pushstate3</button>
        {/*重定向*/}
        <button onClick={this.replace}>replace</button>

        <button onClick={this.forward}>forward</button>
        <button onClick={this.back}>back</button>
        <button onClick={this.go}>go</button>
        <Outlet />
      </div>
    )
  }
  componentDidMount() {
    console.log(window.location);
    console.log(window.history);
    //在window上加一个onpushstate函数
    //模拟onpopstate相同的功能,在每一次压栈操作后触发回调函数
    //利用setState把数据同步回去
    window.onpushstate = (event) => {
      console.log('onpushstate');
      this.setState({
        //同步路径
        path: window.location.pathname
      })
    }
    //前进或后退是触发
    window.onpopstate = (e) => {
      console.log('onpopstate');
      this.setState({
        //回退或者前进时能及时切换页面
        path: window.location.pathname
      })
    }
  }
}
export default App

 (4)react-router

利用react-router实现的案例

 

先用BrowserRouter把app组件包裹上
link创建链接,route使链接和组件间建立关联,OutLet是路由出口
获取路由详情
在invoices的Link标签中的to方法请求链接,index.js里的Route标签生效,渲染出Invoices组件
渲染位置就在App.js的Outlet中

//index.js配置路由
import React from 'react';
import ReactDOM from 'react-dom';
import App from './App';
import { BrowserRouter, Route, Routes } from 'react-router-dom'
import Expenses from './components/expenses'
import Invoices from './components/invoices'
import NoMatch from './components/noMatch'
import Invoice from './components/invoice'

ReactDOM.render(
  <BrowserRouter>
    <Routes>
      {/* 路由嵌套,在APP组件中渲染两个子组件的出口*/}
      <Route path='/' element={<App></App>}>
        <Route path='expenses' element={<Expenses></Expenses>}></Route>
        <Route path='invoices' element={<Invoices></Invoices>}>
          {/* 没有ID时展示默认信息 */}
          <Route index element={
            <main style={{ padding: '5rem' }}>
              <p>请选择发票选项</p>
            </main>
          } />
          {/* 根据ID显示单个订单信息 */}
          {/*invoiceId是占位符*/}
          <Route path=":invoiceId" element={<Invoice />} />
        </Route>
        {/* 当上面所有的路由不匹配时*会拦截到 */}
        <Route path='*' element={<NoMatch></NoMatch>}></Route>
      </Route>
    </Routes>
  </BrowserRouter>
  , document.getElementById('root'));

//App.js
import {Link, Outlet} from 'react-router-dom'
function App(){
  return(
    <div>
      <h1>账本</h1>
      <nav style={{borderBottom:'solid 1px',paddingBottom:'1rem'}}>
        <Link to='/invoices'>发票</Link>
        {' || '}
        <Link to='/expenses'>花销</Link>
      </nav>
      {/* 路由出口 */}
      <Outlet></Outlet>
    </div>
  )
}
export default App

//data.js模拟数据
let invoices = [
  {
    number: 1,
    name: 'phone1',
    amount: '&80',
    date: '06/07/2001'
  },
  {
    number: 2,
    name: 'phone2',
    amount: '&90',
    date: '06/09/2001'
  },
  {
    number: 3,
    name: 'iphone3',
    amount: '&70',
    date: '06/11/2001'
  },
  {
    number: 4,
    name: 'iphone4',
    amount: '&70',
    date: '06/11/2001'
  },
  {
    number: 5,
    name: 'apple5',
    amount: '&70',
    date: '06/11/2001'
  },
  {
    number: 6,
    name: 'apple6',
    amount: '&70',
    date: '06/11/2001'
  },
]
export function getInvoices(){
  return invoices
}
export function getInvoice(number) {
  return invoices.find(
    invoice => invoice.number === number
  );
}

//expenses.js
export default function Expenses(){
  return(
    <main style={{padding:'1rem 0'}}>
      <h2>花销</h2>
    </main>
  )
}

//invoice.js
import { useParams } from "react-router-dom";
import { getInvoice } from "../data";
export default function Invoice() {
  //params就是一个对象,对象的键名就是index.js中path后的字符串,也就是invoiceId
  //键值就是invoices.js中to标签${}里对应的值,点击时能获取到
  let params = useParams();
  let invoice = getInvoice(parseInt(params.invoiceId, 10));
  return (
    <main style={{ padding: "1rem" }}>
      <h2>总额: {invoice.amount}</h2>
      <p>
        {invoice.name}: {invoice.number}
      </p>
      <p>日期: {invoice.date}</p>
    </main>
  );
}

//invoices.js
import { getInvoices } from "../data"
import { Outlet, useSearchParams } from "react-router-dom"
import { NavLink } from "react-router-dom"
export default function Invoices() {
  let invoices = getInvoices()
  // useSearchParams返回值是一个数组,从中结构出这两个方法
  let [searchParams, setSearchParams] = useSearchParams()
  return (
    <div style={{ display: 'flex' }}>
      <nav
        style={{
          borderRight: 'solid 1px',
          padding: '1rem'
        }}
      >
        {/* 搜索 */}
        <input
          //searchParams是搜索参数,从地址栏拿到值,设置为输入框默认值
          //刷新页面时保证搜索框值还在
          value={searchParams.get('filter') || ""}
          onChange={
            (e) => {
              let value = e.target.value
              if (value) {
                //url设置搜索参数
                setSearchParams({ filter: value })
              } else {
                setSearchParams({})
              }
            }
          }
        />
        {/* 发票列表 */}
        {
          invoices
            .filter(invoice => {
              //过滤参数
              let filter = searchParams.get('filter')
              //若没有filter则不过滤了,数据全都返回
              if (!filter) return true
              //全转成大写
              let name = invoice.name.toLocaleLowerCase()
              //startsWith 以...开头,返回布尔值
              //若为true则留在invoices数组中
              return name.startsWith(filter.toLocaleLowerCase())

            })
            .map(invoice => (
              // NavLink是导航链接,能动态改变样式
              // 点击NavLink是给style传参数
              // style中的函数接收一个对象
              // 其中包含isActive属性,是布尔类型,代表是否点击标签
              // style返回值是对象
              // 动态样式就是利用返回对象实现的
              // <NavLink style={() => ({
              // })}
              // >
              // </NavLink>
              // 从对象里结构出来isActive
              <NavLink style={({ isActive }) => ({
                display: 'block',
                margin: '1rem 0',
                color: isActive ? "red" : ""
              })}
                //动态地址
                to={`/invoices/${invoice.number}`}
                key={invoice.number}
              >
                {invoice.name}
              </NavLink>
            ))
        }
      </nav>
      <Outlet></Outlet>
    </div>
  )
}

//noMatch.jsx
function NoMatch(){
  return(
    <main style={{padding:'1rem'}}>
      <p>404</p>
    </main>
  )
}
export default NoMatch

(5)自定义路由组件

改进前查询出新的列表后点击选项又会跳回该页面
这是因为点击选项时路由地址有变回去了
选项的路由地址没有拼接上搜索条件
所以我们给原有的<NavLink/>加强,重新定义一个组件

QueryNavLink.js
import { NavLink, useLocation } from "react-router-dom"
//把to解构出来,其他属性压回到props里
export function QueryLNavLink({to,...props}){
  //useLocation返回的是url信息
  let location = useLocation()
  console.log(location);
  //只改变to,其他属性正常传入
  return <NavLink to={to+location.search} {...props}></NavLink>
}

再把invoices.js的NavLink标签换成QueryNavLink就行了

最后

以上就是成就海燕为你收集整理的React复习(12)的全部内容,希望文章能够帮你解决React复习(12)所遇到的程序开发问题。

如果觉得靠谱客网站的内容还不错,欢迎将靠谱客网站推荐给程序员好友。

本图文内容来源于网友提供,作为学习参考使用,或来自网络收集整理,版权属于原作者所有。
点赞(44)

评论列表共有 0 条评论

立即
投稿
返回
顶部