Hello React day5


Welcome

1.PubSubJs(消息的发布与订阅)

安装: npm install pubsub-js
使用: import PubSub from 'pubsub-js'
//注意这里的callback接收的第一个参数为订阅的消息名!!!
订阅: let token = PubSub.subscribe('MY TOPIC', callback);
//第一个参数为消息名,后面参数为传递的值
发布: PubSub.publish('MY TOPIC', 'hello world!');
//取消类似定时器的取消
取消: PubSub.unsubscribe(token);

2.fetch的基本用法(优化写法)

    try {
            let result = await fetch(`https://api.github.com/search/users?q=${value}`)  
            //返回的是一个Promise 通过await拿到结果是一个response对象
            //数据在这个对象的response.json()方法中,但是这个也是一个promise
            //再次使用await,这样得到的就是那个数据对象
            let data = await result.json()   
            PubSub.publish('changeState', { mainValue: data.items, loading: false })
        } catch (error) {
            PubSub.publish('changeState', { loading: false, err: error.message })
        }
  • fetch原生函数(存在兼容性问题)与XmlHttpRequest属于同级
  • axios,jq中的$ajax都是基于xmlHttpRequest封装的

3.对象解构赋值

let obj = {a:{b:123}
cosnt {a} = obj  //普通解构
cosnt {a:{b}} = obj // 深度解构
cosnt {a:{b:value}} = obj  //深度解构加重命名

4.react-router-dom的基本使用

import { Link,Route,Routes } from "react-router-dom"
1.明确好界面中的导航区、展示区
2.导航区的a标签改为Link标签
<Link className="xxxx" to='/about'>yyyy</Link>
3.展示区写Routes和Route标签进行路由的匹配
//新版本写法
<Routes>
    <Route path='/about' element={<About />} />
    <Route path='/home' element={<Home />} />
</Routes>

//app.js 
import {BrowserRouter } from "react-router-dom"
4.app的最外层包裹一个 <BrowserRouter> 或者<HashBrowser>

5.路由组件与一般组件

​ 1.写法不同

​ 一般组件:

​ 路由组件:<*Route* path=’/about’ component={Demo} />

​ 2.存放位置不同

​ 一般组件:存放在components文件夹下

​ 路由组件:存放在pages文件夹下

​ 3.接收到的props不同

​ 一般组件:写标签时传入了什么,就接受到了什么

​ 路由组件:接收到三个固定的属性

history:
        go: ƒ go(n)
        goBack: ƒ goBack()
        goForward: ƒ goForward()
        push: ƒ push(path, state)
        replace: ƒ replace(path, state)
location:
        pathname: "/about"
        search: ""
        state: undefined
match:
        params: {}
        path: "/about"
        url: "/about"

6.组件实例中的children属性

<MyNavLink to='/about' children="About" />
<MyNavLink to='/home' children="Home" />
//这两种方式是等价的   组件标签中的内容,其实就是作为组件实例上的children属性进行传递的的
//在组件内部的props可以接收到
<MyNavLink to='/about' >About</MyNavLink>
<MyNavLink to='/home' >Home</MyNavLink>

7.Switch的使用

//像这种同一个路径匹配了多个组件的时候,对应的组件都会展示
<Route path='/about' component={About} />
<Route path='/home' component={Home} />               
<Route path='/home' component={Dmeo} /> 
//import {Switch} from 'react-router-dom'
//加了Switch组件以后,就会提高性能,只要匹配到对应的路径就不会在匹配其它的了
<Switch>
          <Route path='/about' component={About} />
          <Route path='/home' component={Home} />               
          <Route path='/home' component={Dmeo} />     
</Switch>  

8.解决样式丢失的方法

  • 在用react脚手架运行项目的时候,本地开启的服务器会把public作为根目录
  • 如果请求路径拿不到资源时,会默认把index.html页面展示出来
  • 造成路径丢失的原因
http://localhost:3000/home   
//在这种路径下会以这种路径去正确的请求css样式表
Request URL: http://localhost:3000/css/bootstrap.css

http://localhost:3000/home/aa/bb
//在这种路径下会以这种路径去错误的请求css样式表(会返回给你一个index.html,因为匹配不到结果)
Request URL: http://localhost:3000/home/aa/css/bootstrap.css
  • 三种解决办法
<link rel="stylesheet" href="./css/bootstrap.css">
//第一种  把点去掉
<link rel="stylesheet" href="/css/bootstrap.css">
//第二种  %PUBLIC_URL% 
<link rel="stylesheet" href=" %PUBLIC_URL%/css/bootstrap.css">
//第三种 采用<HashRouter>模式而不是<BrowserRouter>模式
http://localhost:3000/#/home/asasas   //会自动的忽略掉#后面的路径,不加在请求的url地址中
Request URL: http://localhost:3000/css/bootstrap.css

9.严格匹配和模糊匹配

  • react-router-dom的默认匹配模式就是模糊匹配
//默认的模糊匹配这样子也不会报错,能正确展示
<MyNavLink to='/home/a/b' >Home</MyNavLink>
<Switch>
      <Route path='/home' component={Home} />                    
</Switch> 
  • 给展示区域的路由组件上加 exact属性 (exact={ true} / exact) //这样就只能进行严格匹配,能不用就不用

🍋 10. 重定向路由

在我们写好了这些之后,我们会发现,我们需要点击任意一个按钮,才会去匹配一个组件,这并不是我们想要的,我们想要页面一加载上来,默认的就能匹配到一个组件。

这个时候我们就需要时候 Redirecrt 进行默认匹配了。

<Redirect to="/home" />

当我们加上这条语句时,页面找不到指定路径时,就会重定向到 /home 页面下因此当我们请求3000端口时,就会重定向到 /home 这样就能够实现我们想要的效果了

image-20210904013342960

🍓11. 嵌套路由

嵌套路由也就是我们前面有提及的二级路由,但是嵌套路由包括了二级、三级…还有很多级路由,当我们需要在一个路由组件中添加两个组件,一个是头部,一个是内容区

我们将我们的嵌套内容写在相应的组件里面,这个是在 Home 组件的 return 内容

<div>
    <h2>Home组件内容</h2>
    <div>
        <ul className="nav nav-tabs">
            <li>
                <MyNavLink className="list-group-item" to="/home/news">News</MyNavLink>
            </li>
            <li>
                <MyNavLink className="list-group-item " to="/home/message">Message</MyNavLink>
            </li>
        </ul>
        {/* 注册路由 */}
        <Switch>
            <Route path="/home/news" component={News} />
            <Route path="/home/message" component={Message} />
        </Switch>
    </div>
</div>

在这里我们需要使用嵌套路由的方式,才能完成匹配

首先我们得 React 中路由得注册是有顺序得,我们在匹配得时候,因为 Home 组件是先注册得,因此在匹配的时候先去找 home 路由,由于是模糊匹配,会成功的匹配

在 Home 组件里面去匹配相应的路由,从而找到 /home/news 进行匹配,因此找到 News 组件,进行匹配渲染

如果开启精确匹配的话,第一步的 /home/news 匹配 /home 就会卡住不动,这个时候就不会显示有用的东西了!

🍟 12. 传递 params 参数

react-router-params

首先我们需要实现的效果是,点击消息列表,展示出消息的详细内容

这个案例实现的方法有三种,第一种就是传递 params 参数,由于我们所显示的数据都是从数据集中取出来的,因此我们需要有数据的传输给 Detail 组件

我们首先需要将详细内容的数据列表,保存在 DetailData 中,将消息列表保存在 Message 的 state 中。

我们可以通过将数据拼接在路由地址末尾来实现数据的传递

 <Link to={`/home/message/detail/${msgObj.id}/${msgObj.title}`}>{msgObj.title}</Link>

如上,我们将消息列表的 id 和 title 写在了路由地址后面

这里我们需要注意的是:需要采用模板字符串以及 $ 符的方式来进行数据的获取

在注册路由时,我们可以通过 :数据名 来接收数据

<Route path="/home/message/detail/:id/:title" component={Detail} />

如上,使用了 :id/:title 成功的接收了由 Link 传递过来的 id 和 title 数据

这样我们既成功的实现了路由的跳转,又将需要获取的数据传递给了 Detail 组件

我们在 Detail 组件中打印 this.props 来查看当前接收的数据情况

image-20210906153042353

我们可以发现,我们传递的数据被接收到了对象的 match 属性下的 params 中

因此我们可以在 Detail 组件中获取到又 Message 组件中传递来的 params 数据

并通过 params 数据中的 id 值,在详细内容的数据集中查找出指定 id 的详细内容

const { id, title } = this.props.match.params
const findResult = DetailData.find((detailObj) => {
    return detailObj.id === id
})

最后渲染数据即可

🍀 13. 传递 search 参数

我们还可以采用传递 search 参数的方法来实现

首先我们先确定数据传输的方式

我们先在 Link 中采用 ? 符号的方式来表示后面的为可用数据

<Link to={`/home/message/detail/?id=${msgObj.id}&title=${msgObj.title}`}>{msgObj.title}</Link>

采用 search 传递的方式,无需在 Route 中再次声明,可以在 Detail 组件中直接获取到

image-20210906155217647

我们可以发现,我们的数据保存在了 location 对象下的 search 中,是一种字符串的形式保存的,我们可以引用一个库来进行转化 querystring

import qs from 'querystring'

这个库是 React 中自带有的,它有两个方法,一个是 parse 一个是 stringify

我们可以采用 parse 方法,将字符串转化为键值对形式的对象

const { search } = this.props.location
const { id, title } = qs.parse(search.slice(1))

这样我们就能成功的获取数据,并进行渲染

tips:无需声明接收

🌷 14. 传递 state 参数

采用传递 state 参数的方法,是我觉得最完美的一种方法,因为它不会将数据携带到地址栏上,采用内部的状态来维护

<Link to={{ pathname: '/home/message/detail', state: { id: msgObj.id, title: msgObj.title } }}>{msgObj.title}</Link>

首先,我们需要在 Link 中注册跳转时,传递一个路由对象,包括一个 跳转地址名,一个 state 数据,这样我们就可以在 Detail 组件中获取到这个传递的 state 数据

注意:采用这种方式传递,无需声明接收

我们可以在 Detail 组件中的 location 对象下的 state 中取出我们所传递的数据

const { id, title } = this.props.location.state

image-20210906160940033

直接使用即可~

解决清除缓存造成报错的问题,我们可以在获取不到数据的时候用空对象来替代,例如,

const { id, title } = this.props.location.state || {}

当获取不到 state 时,则用空对象代替

这里的 state 和状态里的 state 有所不同

15.withRouter

  • 它不是一个组件是一个函数

  • 是用来给一般组件使用路由组件的api的、

    //导入
    import { withRouter } from 'react-router-dom';
    //类式组件
    class Header extends Component {...}
    //导出
    export default withRouter(Header)
    //这样在这个组件内部就有了路由组件的相关api了,不影响自己props组件实例参数的接收
    

16.BrowserRouter 和 HashRouter 的区别

  • 底层的实现原理不一样

    对于 BrowserRouter 来说它使用的是 React 为它封装的 history API ,这里的 history 和浏览器中的 history 有所不同噢!通过操作这些 API 来实现路由的保存等操作,但是这些 API 是 H5 中提出的,因此不兼容 IE9 以下版本。
    
    对于 HashRouter 而言,它实现的原理是通过 URL 的哈希值,但是这句话我不是很理解,用一个简单的解释就是
    
    我们可以理解为是锚点跳转,因为锚点跳转会保存历史记录,从而让 HashRouter 有了相关的前进后退操作,HashRouter 不会将 `#` 符号后面的内容请求。兼容性更好!
    
  • 地址栏的表现形式不一样

    ​ HashRouter 的路径中包含 # ,例如 localhost:3000/#/demo/test

  • 刷新后state参数改变

​ 在BrowserRouter 中,state 保存在history 对象中,刷新不会丢失

​ HashRouter 则刷新会丢失 state


文章作者: 洪大俊
版权声明: 本博客所有文章除特別声明外,均采用 CC BY 4.0 许可协议。转载请注明来源 洪大俊 !
评论
  目录