React基础笔记(二)

256 阅读8分钟

这是我参与8月更文挑战的第2天,活动详情查看:8月更文挑战

前言

本文作为本人学习总结之用,同时分享给大家,适合入门的react小白
因为个人技术有限,如果有发现错误或存在疑问之处,欢迎指出或指点!不胜感谢!

React脚手架

创建项目并启动React项目

第一步,全局安装:npm i -g create-react-app

第二步,切换到想创项目的目录,使用命令:

create-react-app hello-react

yarn create react-app hello-react

第三步,进入项目文件夹:cd hello-react

第四步,启动项目:npm start 或者yarn start

配置代理

1.第一步:创建代理配置文件

在src下创建配置文件:src/setupProxy.js

2.编写setupProxy.js配置具体代理规则:

const proxy = require('http-proxy-middleware')

module.exports = function(app) {
  app.use(
    proxy('/api1', {  //api1是需要转发的请求(所有带有/api1前缀的请求都会转发给5000)
      target: 'http://localhost:5000', //配置转发目标地址(能返回数据的服务器地址)
      changeOrigin: true, //控制服务器接收到的请求头中host字段的值
      /*
        changeOrigin设置为true时,服务器收到的请求头中的host为:localhost:5000
        changeOrigin设置为false时,服务器收到的请求头中的host为:localhost:3000
        changeOrigin默认值为false,但我们一般将changeOrigin值设为true
      */
      pathRewrite: {'^/api1': ''} //去除请求前缀,保证交给后台服务器的是正常请求地址(必须配置)
    }),
    proxy('/api2', { 
      target: 'http://localhost:5001',
      changeOrigin: true,
      pathRewrite: {'^/api2': ''}
    })
  )
}

说明:

  1. 优点:可以配置多个代理,可以灵活的控制请求是否走代理。
  2. 缺点:配置繁琐,前端请求资源时必须加前缀。

React-router

SPA

  1. React是单页Web应用(single page web application,SPA)。
  2. 整个应用只有一个完整的页面。
  3. 点击页面中的链接不会刷新页面,只会做页面的局部更新。
  4. 数据都需要通过ajax请求获取, 并在前端异步展现。

前端路由

路由的概念来源于服务端,在服务端中路由描述的是 URL 与处理函数之间的映射关系。

在 Web 前端单页应用 SPA中,路由描述的是 URL 与 UI 之间的映射关系,这种映射是单向的,即 URL 变化引起 UI 更新(无需刷新页面)。

前端路由主要有两种实现方案:hash、history

hash 实现

  • hash 是 URL 中 hash (#) 及后面的那部分,常用作锚点在页面内进行导航,改变 URL 中的 hash 部分不会引起页面刷新
  • 通过 hashchange 事件监听 URL 的变化,改变 URL 的方式只有这几种:通过浏览器前进后退改变 URL、通过<a>标签改变 URL、通过window.location改变URL,这几种情况改变 URL 都会触发 hashchange 事件

hash优缺点:

优点:优点: 实现简单,兼容性好(兼容到 ie8) 绝大多数前端框架均提供了给予 hash 的路由实现 不需要服务器端进行任何设置和开发 除了资源加载和 ajax 请求以外,不会发起其他请求

缺点: 对于部分需要重定向的操作,后端无法获取 hash 部分内容,导致后台无 法取得 url 中的数据,典型的例子就是微信公众号的 oauth 验证 服务器端无法准确跟踪前端路由信息 对于需要锚点功能的需求会与目前路由机制冲突

history 实现

  • history 提供了 pushState 和 replaceState 两个方法,这两个方法改变 URL 的 path 部分不会引起页面刷新

  • history 提供类似 hashchange 事件的 popstate 事件,但 popstate 事件有些不同:通过浏览器前进后退改变 URL 时会触发 popstate 事件,通过pushState/replaceState或<a>标签改变 URL 不会触发 popstate 事件。好在我们可以拦截 pushState/replaceState的调用和<a>标签的点击事件来检测 URL 变化,所以监听 URL 变化可以实现,只是没有 hashchange 那么方便

history 优缺点

优点 :对于重定向过程中不会丢失 url 中的参数。后端可以拿到这部分数据。绝大多数前段框架均提供了 history 模式的路由实现。后端可以准确跟踪路由信息 可以使用 history.state 来获取当前 url 对应的状态信息

缺点:兼容性不如 hash 路由(只兼容到 IE10) 需要后端支持,每次返回 html 文档

简单的说两者之前的区别

hashhistory
只修改#后面内容可以设置同源下任意的URL
新值不能与旧值相同,一样的不会触发动作将记录添加到栈中新旧值可以相同,pushSate该添加的会添加到栈中
对服务器无需改动刷新时,若服务器没有响应数据或资源,会404。需要对服务器做一些改造,对不同的路由进行相应的设置。
即不会发送请求会向服务器发送请求,避免404服务器应该做处理。当匹配不到资源时,应返回同一个html页面。

react-router-dom

  1. react的一个插件库。
  2. 专门用来实现一个SPA应用。
  3. 基于react的项目基本都会用到此库。

安装:yarn add react-router-dom 

内置组件--Link

1.导航区为Link标签 
 <Link className="list-group-item" to="/about">About</Link>
2.展示区写Route标签进行路径的匹配
  <Route path="/about" component={About}/>
3.<App>的最外侧包裹了一个<BrowserRouter>或<HashRouter>
//index.js
  ReactDOM.render(
    <BrowserRouter>
        <App/>
    </BrowserRouter>,
    document.getElementById('root')
)

内置组件--NavLink

跟link一样的写法

NavLink可以实现路由链接的高亮,通过activeClassName指定样式名

<NavLink activeClassName="atguigu" className="list-group-item" to="/about">About</NavLink>

//封装组件   
//props中有children  children正好是NavLink的属性名显示文字内容
<NavLink activeClassName="atguigu" className="list-group-item"  {...this.props}/>
//调用时
<MyNavLink to="/about">About</MyNavLink>

内置组件--switch

1.通常情况下,path和component是一一对应的关系。

2.Switch可以提高路由匹配效率(单一匹配)。 找到之后不再继续找寻

未使用Switch包裹注册路由: 未匹配时,展示全部路由

image.png

具体编码
{/* 注册路由 */}
用Switch包裹之后仅出现Home组件
<Switch>
  <Route path="/about" component={About}/>
  <Route path="/home" component={Home}/>
  <Route path="/home" component={Test}/>
</Switch>

内置组件Redirect

一般写在所有路由注册的最下方,当所有路由都无法匹配时,跳转到Redirect指定的路由(重定向)

具体编码
<Switch>
  <Route path="/about" component={About}/>
  <Route path="/home" component={Home}/>
  <Redirect to="/about"/>
</Switch>

解决多级路径刷新页面样式丢失的问题

  1. public/index.html 中 引入样式时不写 ./ 写 / (常用)
  2. public/index.html 中 引入样式时不写 ./ 写 %PUBLIC_URL% (常用)
  3. 使用HashRouter (不建议使用)
<link rel="icon" href="%PUBLIC_URL%/favicon.ico" />
<link rel="stylesheet" href="/css/bootstrap.css">
    
  ReactDOM.render(
    <HashRouter>
      <App/>
    </HashRouter>,
    document.getElementById('root')
  )

路由的严格匹配与模糊匹配

  1. 默认使用的是模糊匹配(简单记:【输入的路径】必须包含要【匹配的路径】,且顺序要一致)
  2. 开启严格匹配:

<Route exact={true} path="/about" component={About}/>或者简写

<Route exact path="/about" component={About}/>

  1. 严格匹配不要随便开启,需要再开,有些时候开启会导致无法继续匹配二级路由

嵌套路由

  1. 注册子路由时要写上父路由的path值
  2. 路由的匹配是按照注册路由的顺序进行的
//子路由的写法
<Switch>
  <Route path="/home/news" component={News}/>
  <Route path="/home/message" component={Message}/>
  <Redirect to="/home/news"/>
</Switch>

路由组件传递参数

  1. params参数

    路由链接(携带参数):<Link to='/demo/test/tom/18'}>详情</Link>

    注册路由(声明接收):<Route path="/demo/test/:name/:age" component={Test}/>

    接收参数:this.props.match.params

  2. search参数

    路由链接(携带参数):<Link to='/demo/test?name=tom&age=18'}>详情</Link>

    注册路由(无需声明,正常注册即可):<Route path="/demo/test" component={Test}/>

    接收参数:this.props.location.search

    备注:获取到的search是urlencoded编码字符串,需要借助querystring解析

  3. state参数

     路由链接(携带参数):<Link to={{pathname:'/demo/test',state:{name:'tom',age:18}}}>详情</Link>

     注册路由(无需声明,正常注册即可):<Route path="/demo/test" component={Test}/>

     接收参数:this.props.location.state

     备注:刷新也可以保留住参数

具体编码:
messageArr.map((msgObj)=>{
  return (
    <li key={msgObj.id}>
      {/* 向路由组件传递params参数 */}
      <Link to={`/home/message/detail/${msgObj.id}/${msgObj.title}`}>{msgObj.title}</Link>

      {/* 向路由组件传递search参数 */}
      <Link to={`/home/message/detail/?id=${msgObj.id}&title=${msgObj.title}`}>{msgObj.title}</Link>

      {/* 向路由组件传递state参数 */}
      <Link to={{pathname:'/home/message/detail',state:{id:msgObj.id,title:msgObj.title}}}>{msgObj.title}</Link>
    </li>
  )
})

{/* 声明接收params参数 */}
<Route path="/home/message/detail/:id/:title" component={Detail}/>

{/* search参数无需声明接收,正常注册路由即可 */}
<Route path="/home/message/detail" component={Detail}/>

{/* state参数无需声明接收,正常注册路由即可 */}
<Route path="/home/message/detail" component={Detail}/>

子组件
import qs from 'querystring'
export default class Detail extends Component {
    render() {
        // 接收params参数
        const {id,title} = this.props.match.params 

        // 接收search参数
        const {search} = this.props.location
        const {id,title} = qs.parse(search.slice(1))

        // 接收state参数
        const {id,title} = this.props.location.state || {}

        const findResult = DetailData.find((detailObj)=>{
            return detailObj.id === id
        }) || {}
        return (
            <ul>
                <li>ID:{id}</li>
                <li>TITLE:{title}</li>
                <li>CONTENT:{findResult.content}</li>
            </ul>
        )
    }
}

编程式路由导航

  • push是往 history 里面增加一层堆栈,可以返回上一层
  • replace跳转不会形成history,不可返回到上一层。
//借助this.prosp.history对象上的API对操作路由跳转、前进、后退
  this.prosp.history.push()  //push查看
        //push跳转+携带params参数
        this.props.history.push(`/home/message/detail/${id}/${title}`)
        //push跳转+携带search参数
        this.props.history.push(`/home/message/detail?id=${id}&title=${title}`)
        //push跳转+携带state参数
        this.props.history.push(`/home/message/detail`,{id,title})

  this.prosp.history.replace()  //replace查看
        //replace跳转+携带params参数
        this.props.history.replace(`/home/message/detail/${id}/${title}`)
        //replace跳转+携带search参数
        this.props.history.replace(`/home/message/detail?id=${id}&title=${title}`)
        //replace跳转+携带state参数
        this.props.history.replace(`/home/message/detail`,{id,title})

  this.prosp.history.goBack()  //回退
  this.prosp.history.goForward() //前进
  this.prosp.history.go(n)  //负为退 正为进

BrowserRouter与HashRouter的区别

  1. 底层原理不一样:
    • BrowserRouter使用的是H5的history API,不兼容IE9及以下版本。
    • HashRouter使用的是URL的哈希值。
  2. path表现形式不一样
    • BrowserRouter的路径中没有# 例如:localhost:3000/demo/test
    • HashRouter的路径包含# 例如:localhost:3000/#/demo/test
  3. 刷新后对路由state参数的影响
    • BrowserRouter没有任何影响,因为state保存在history对象中。
    • HashRouter刷新后会导致路由state参数的丢失!!!
  4. 备注:HashRouter可以用于解决一些路径错误相关的问题。