第1章 React应用(基于React脚手架)
1.1 使用create-react-app创建react应用
1.1.1 React脚手架
- 脚手架: 用来帮助程序员快速创建一个基于xxx库的模板项目
(1)包含了所有需要的配置(语法检查、jsx编译、devServer…)
(2)下载所有相关依赖
(3)可以直接运行简单效果 - react提供了一个用于创建react项目的脚手架库: create-react-app
- 项目的整体技术架构为: react + webpack + es6 + eslint
- 特点: 模块化, 组件化, 工程化
1.1.2 创建项目并启动
- 全局安装
npm i create-react-app -g
- 使用命令创建项目:
create-react-app hello-react
- 启动项目:
npm start
1.1.3 脚手架目录结构
public ---- 静态资源文件夹
favicon.icon ------ 网站页签图标
index.html -------- 主页面
logo192.png ------- logo图
logo512.png ------- logo图
manifest.json ----- 应用加壳的配置文件
robots.txt -------- 爬虫协议文件
src ---- 源码文件夹
App.css -------- App组件的样式
App.js --------- App组件
App.test.js ---- 用于给App做测试
index.css ------ 样式
index.js ------- 入口文件
logo.svg ------- logo图
reportWebVitals.js
--- 页面性能分析文件(需要web-vitals库的支持)
setupTests.js
---- 组件单元测试的文件(需要jest-dom库的支持)
1.2 配置代理
1.2.1 方法一
在package.json中追加如下配置
"proxy":"http://localhost:5000"
说明:
- 优点:配置简单,前端请求资源时可以不加任何前缀。
- 缺点:不能配置多个代理。
- 工作方式:上述方式配置代理,当请求了3000不存在的资源时,那么该请求会转发给5000 (优先匹配前端资源)
1.2.2 方法二
-
第一步:创建代理配置文件, 在src下创建配置文件:src/setupProxy.js
-
编写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': ''} }) ) }
-
优缺点:
- 优点:可以配置多个代理,可以灵活的控制请求是否走代理。
- 缺点:配置繁琐,前端请求资源时必须加前缀
第2章 React ajax
2.1 使用
2.1.1 说明
- React本身只关注于界面, 并不包含发送ajax请求的代码
- 前端应用需要通过ajax请求与后台进行交互(json数据)
- 3.react应用中需要集成第三方ajax库(或自己封装)
2.1.2 常用库
- jQuery:比较重
- axios: 基于XmlHttpRequest对象的ajax,promise风格;可以在浏览器端和node服务端(封装的http模块)
- fetch: 原生发送请求
2.1.3 axios使用
- axios.get()
- axios.post
2.1.4 Fetch的使用
- 见MDN上的用法
2.3 消息订阅-发布机制
- 通过以上学习,目前组件之间的通信只能将状态提取到公共共有组件身上,然后利用props以及传递函数的方式实现交互。
- 兄弟组件之间可以利用消息订阅-发布机制来实现通信
2.3.1 PubSubJS
- 工具库:
PubSubJS
- 下载:
npm intall pubsub-js --save
- 使用:
import PubSub from 'pubsub-js'
//引入- 在
componentDidMount
里订阅消息PubSub.subscribe('delete', function(data){ })
; //订阅 PubSub.publish('delete', data)
//发布消息- 在订阅的组件销毁的时候需要取消订阅 ,
PubSub.unsubscrible(this.xxx)
- 通过以上就可以实现消息订阅与发布了
第3章 React路由
3.1 相关理解
3.1.1 SPA的理解
- 用三大框架开发的项目都是单页面应用,也就是整个应用只有一个完整的页面
- 点击页面的链家不会刷新页面,只会做页面的局部刷新
- 数据通过ajax请求获取,并且异步显示
3.1.2 路由的理解
- 什么是路由
- 一个路由就是一种映射关系(key: value)
- key为路径,value可能是
function
或者component
- 路由分类
- 前端路由:
- 浏览器端路由,value是component,用于展示页面内容。
- 注册路由:
- 工作过程:当浏览器的path变为/test时, 当前路由组件就会变为Test组件
- 后端路由:
- 理解: value是function, 用来处理客户端提交的请求。
- 注册路由: router.get(path, function(req, res))
- 工作过程:当node接收到一个请求时, 根据请求路径找到匹配的路由, 调用路由中的函数来处理请求, 返回响应数据
- 前端路由:
3.1.3 react-router-dom的理解
- react的插件库,专门用于实现SPA应用
- 基于react的项目基本上都会用到这个库
- 1.下载react-router-dom:
npm install react-router-dom --save
3.2 路由组件与一般组件
1.写法不同:
一般组件:<Demo/>
路由组件:<Route path="/demo" component={Demo}/>
2.存放位置不同:
一般组件:components
路由组件:pages
3.接收到的props不同:
一般组件:写组件标签时传递了什么,就能收到什么
路由组件:接收到三个固定的属性:history相关:go,goback,goForward,push,replace,location.pathname,state,search;match:params: {},path: "/about",url: "/about"
3.3 路由的使用以及相关API
3.3.1 Link
、BrowserRouter
、HashRouter
- 所有的路由都要受到router的管理,router分为两类:BrowserRouter(history跳转)和 HashRouter(hash跳转)
- a标签是怎么写的,Link就是怎么写
index.js
//引入react核心库
import React from 'react'
//引入ReactDOM
import ReactDOM from 'react-dom'
//
import {BrowserRouter} from 'react-router-dom'
//引入App
import App from './App'
ReactDOM.render(
<BrowserRouter>
<App/>
</BrowserRouter>,
document.getElementById('root')
)
App.jsx
import React, { Component } from 'react'
import {Link,Route} from 'react-router-dom'
import Home from './components/Home'
import About from './components/About'
export default class App extends Component {
render() {
return (
<div>
<div className="row">
<div className="col-xs-offset-2 col-xs-8">
<div className="page-header"><h2>React Router Demo</h2></div>
</div>
</div>
<div className="row">
<div className="col-xs-2 col-xs-offset-2">
<div className="list-group">
{/* 原生html中,靠<a>跳转不同的页面 */}
{/* <a className="list-group-item" href="./about.html">About</a>
<a className="list-group-item active" href="./home.html">Home</a> */}
{/* 在React中靠路由链接实现切换组件--编写路由链接 */}
<Link className="list-group-item" to="/about">About</Link>
<Link className="list-group-item" to="/home">Home</Link>
</div>
</div>
<div className="col-xs-6">
<div className="panel">
<div className="panel-body">
{/* 注册路由 */}
<Route path="/about" component={About}/>
<Route path="/home" component={Home}/>
</div>
</div>
</div>
</div>
</div>
)
}
}
3.3.2 NavLink
- NavLink可以实现路由链接的高亮,通过activeClassName指定样式名.点击谁,就会给谁追加上
active
类名 - 如何封装avLink
- 封装
<NavLink activeClassName="atguigu" className="list-group-item" {...this.props}/>
- 使用
<MyNavLink to="/home">Home</MyNavLink>
- 封装
- 标签体内容也可以在props的children里取到,所以可以用
...props
获取到传递的数据,
3.3.3 Switch的使用
- 因为路由匹配是匹配了还会继续往下匹配,所以就可以用到
switch
;通常情况下,path和component是一一对应的关系;Switch可以提高路由匹配效率(单一匹配)。<Switch> <Route path="/about" component={About}/> <Route path="/home" component={Home}/> <Route path="/home" component={Test}/> </Switch>
3.3.4 解决多级路径刷新页面样式丢失的问题
- public/index.html 中 引入样式时不写
./
写/
(常用) - public/index.html 中 引入样式时不写 ./ 写
%PUBLIC_URL%
(常用) - 使用
HashRouter
3.3.5 路由的严格匹配与模糊匹配
- 默认使用的是模糊匹配(简单记:【输入的路径】必须包含要【匹配的路径】,且顺序要一致)
- 开启严格匹配:
<Route exact={true} path="/about" component={About}/>
- 严格匹配不要随便开启,需要再开,有些时候开启会导致无法继续匹配二级路由
3.3.6 Redirect的使用
- 一般写在所有路由注册的最下方,当所有路由都无法匹配时,跳转到Redirect指定的路由
- 具体编码:
<Switch>
<Route path="/about" component={About}/>
<Route path="/home" component={Home}/>
<Redirect to="/about"/>
</Switch>
3.4 嵌套路由导航
-
在嵌套的组件里去写,但是一定要带上当前父组件的路由;注册子路由时要写上父路由的path值
-
/home/news
: 路由的匹配是按照注册路由的顺序进行的。 首先会一级一级的匹配,先匹配到home,那么会出现home组件;然后再进行匹配子级路由;前提是不能开启exact -
编码如下:
<div> <ul className="nav nav-tabs"> <li> <MyNavLink to="/home/news">News</MyNavLink> </li> <li> <MyNavLink to="/home/message">Message</MyNavLink> </li> </ul> {/* 注册路由 */} <Switch> <Route path="/home/news" component={News}/> <Route path="/home/message" component={Message}/> <Redirect to="/home/news"/> </Switch> </div>
3. 5 向路由组件传递参数
- params参数
1. 路由链接(携带参数):<Link to='/demo/test/tom/18'}>详情</Link>
2. 注册路由(声明接收):<Route path="/demo/test/:name/:age" component={Test}/>
3. 接收参数:this.props.match.params
- search参数
1. 路由链接(携带参数):<Link to='/demo/test?name=tom&age=18'}>详情</Link>
2. 注册路由(无需声明,正常注册即可):<Route path="/demo/test" component={Test}/>
3. 接收参数:this.props.location.search
;备注:获取到的search是urlencoded编码字符串,需要借助querystring解析(react脚手架创建项目的时候就下载了) - state参数
- 路由链接(携带参数):
<Link to={{pathname:'/demo/test',state:{name:'tom',age:18}}}>详情</Link>
- 注册路由(无需声明,正常注册即可):
<Route path="/demo/test" component={Test}/>
- 接收参数:
this.props.location.state
,备注:刷新也可以保留住参数(但是清除缓存就没有了)
- 路由链接(携带参数):
3.6 编程式路由导航
场景: 图片如何跳转路由?或者是一个组件展示3s以后跳到其他地方
- 借助this.prosp.history对象上的API对操作路由跳转、前进、后退
1. this.prosp.history.push()
2. this.prosp.history.replace()
3. this.prosp.history.goBack()
4. this.prosp.history.goForward()
5. this.prosp.history.go() - 编码:
replaceShow = (id,title)=>{ //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}) } pushShow = (id,title)=>{ //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}) }
3.7 withRouter的使用
- withRouter可以加工一般组件,让一般组件具备路由组件所特有的API
- withRouter的返回值是一个新组件
- 使用
import {withRouter} from 'react-router-dom'
export default withRouter(Header)
3.8 BrowserRouter与HashRouter的区别
- 底层原理不一样:
BrowserRouter使用的是H5的history API,不兼容IE9及以下版本。
HashRouter使用的是URL的哈希值。 - path表现形式不一样
BrowserRouter的路径中没有#,例如:localhost:3000/demo/test
HashRouter的路径包含#,例如:localhost:3000/#/demo/test - 刷新后对路由state参数的影响
(1).BrowserRouter没有任何影响,因为state保存在history对象中。
(2).HashRouter刷新后会导致路由state参数的丢失,无法记录state!!! - 备注:HashRouter可以用于解决一些路径错误相关的问题。