文章目录
一、 初始化React脚手架
1、基础相关
react提供的脚手架库:create-react-app
项目整体技术架构:react + webpack + es6 + eslint
脚手架开发项目特点:模块化, 组件化, 工程化
2、创建项目
(1)全局安装:
npm i -g create-react-app
(2)切换到需要创建项目的目录后,创建项目文件夹:
create-react-app hello-react
(3)进入项目文件夹:
cd hello-react
(4)启动项目:
npm start
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库的支持)
4、流程相关
1、react通过index.js(入口文件),引入react及react-dom/client库,引入页面组件APP.js,通过ReactDOM.render渲染组件到页面(根组件root,即index.html文件中页面元素)
2、App.js文件中,通过es6中的export default默认导出函数类组件APP
细节:注意类名用className,而不是class
3、APP外侧包裹React.StrictMode,可以帮忙检查代码不合理的地方
4、index.html文件中,%PUBLIC_URL%
代表public这个文件夹的路径
二、 规范相关
1、命名注意事项
(1)、文件夹名字用大写开头
(2)、区分是否是组件js文件还是函数方法js文件:
a. 组件用jsx结尾
b. 组件js文件用大写开头,函数方法js文件用小写开头
2、样式跟其它组件冲突的解决办法
1、css的情况下:样式名称+module命名,接收时import hello from ‘./index.module.css’,使用{hello.title}接收时import
2、使用less,用嵌套即可
.box{
.title{
}
}
3、生成代码快捷片段
安装ES7+ React/Redux/React-Native snippets插件后
rcc:生成类式组件 --react class component
rfc:生成函数式组件 --react function component
三、 input相关
1、判断input是否是回车按键
event.keyCode === 13
2、input框类型为checkbox时,获取是否选中值
event.target.checked
3、input框类型为text时,获取输入值
event.target.value
4、input框鼠标移入移出事件
鼠标移入:onMouseEnter
鼠标移出:onMouseLeave
四、 react父子组件传值
1、父传子
父组件在绑定的子组件上直接传属性值,注意尽量用{…obj}的形式
子组件用解构赋值取props中对应的值即可
2、子传父
父组件在绑定的子组件绑定的属性上传的为函数
子组件通过this.props.addTodo(todoObj)
给父组件传值,addTodo为父组件中在子组件上绑定的属性名
五、 TodoList案例小总结
1、拆分组件、实现静态组件,注意:className,style的写法
2、动态初始化列表,如何确定将数据放在哪个组件的state中?
--某个组件使用:放在自身的state中
--某些组件使用:放在他们共同的父组件state中(官方称此操作为:状态提升)
3、关于父子组件通信:
1. 【父组件】给【子组件】传递数据:通过props传递
2. 【子组件】给【父组件】传递数据:通过props传递,要求父组件提前给子组件传递一个函数
4、注意 defaultChecked 和 checked 的区别,类似的还有 defaultValue 和 value
5、状态在哪里,操作状态的方法就在哪里
六、 reduce方法复习
array.reduce(function(accumulator, currentValue, currentIndex, array), initialValue)
八、 react脚手架配置代理
1、配置单个代理
在package.json中追加如下配置
"proxy":"http://localhost:5000"
说明:
- 优点:配置简单,前端请求资源时可以不加任何前缀。
- 缺点:不能配置多个代理。
- 工作方式:上述方式配置代理,当请求了3000不存在的资源时,那么该请求会转发给5000 (优先匹配前端资源)
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': ''} }) ) }
说明:
- 优点:可以配置多个代理,可以灵活的控制请求是否走代理。
- 缺点:配置繁琐,前端请求资源时必须加前缀。
3、react配置完setupProxy.js然后运行项目无法访问
可能会因为版本出现此问题,解决办法为从http-proxy-middleware
单个引入createProxyMiddleware
const { createProxyMiddleware } = require("http-proxy-middleware");
// const proxy = require("http-proxy-middleware");
module.exports = function (app) {
app.use(
createProxyMiddleware("/api1", {
target: "http://192.168.1.13:5000",
changeOrigin: true,
pathRewrite: { "^/api1": "" },
})
);
};
九、 消息订阅-发布机制
工具库:PubSubJS
作用:可用于组件间传值
1、下载
npm install pubsub-js --save
2、使用
- import PubSub from ‘pubsub-js’ //引入
- PubSub.subscribe(‘delete’, function(data){ }); //订阅
- PubSub.publish(‘delete’, data) //发布消息
- PubSub.unsubscribe(token) //组件销毁时取消订阅消息
例如:兄弟组件传值(Search组件向List组件传值)
注意:一般在组件销毁时需要取消订阅
十、 Fetch
注意:fetch方法返回的为promise对象,且需要用try…catch处理异常情况
特点:etch: 原生函数,不再使用XmlHttpRequest对象提交ajax请求;但兼容性较差,老版浏览器可能不支持
try {
const response = await fetch(`/api1/search/users2?q=${keyWord}`)
const data = await response.json()
PubSub.publish('atguigu', {isLoading: false, users: data.items})
} catch (error) {
PubSub.publish('atguigu', {isLoading: false, err: error.message})
}
相关文档:
fetch官方文档
传统Ajax已死,Fetch永生
十一、 React路由
1、SPA的理解
单页Web应用(single page web application,SPA)。
整个应用只有一个完整的页面。
点击页面中的链接不会刷新页面,只会做页面的局部更新。
数据都需要通过ajax请求获取, 并在前端异步展现。
2、路由的基本使用
2.1、下载react-router-dom:
npm install --save react-router-dom
2.2、组件引入
import { Link, Route } from 'react-router-dom'
2.3、使用
1)、在index.html中,引入BrowserRouter,且App外层包裹路由
import {BrowserRouter} from 'react-router-dom'
// import {HashRouter} from 'react-router-dom'
const root = ReactDOM.createRoot(document.getElementById('root'))
root.render(
<BrowserRouter>
<App/>
</BrowserRouter>
)
2)、编写路由链接 – 在React中靠路由链接实现切换组件
<Link className="list-group-item" to='/about'>About</Link>
3)、注册路由
<Route path='/about' component={About}/>
3、路由的相关API
3.1、Link
作用: 导航不需要高亮显示
属性:
to:跳转路径
标签体为特殊的属性children
<Link to='/about'>About</Link>
3.2、Route
作用: 注册路由,默认为模糊匹配
属性:
path:组件跳转匹配的路径
component:跳转路径对应的组件
exact:true 开启严格匹配模式(一般不用,会导致二级路由出现问题)
<Route path='/about' component={About}/>
<Route exact path='/about' component={About}/>// 开启严格匹配模式
3.3、NavLink
作用: 导航需要高亮显示,会默认高亮加上active样式,可以通过activeClassName
自定义高亮央视
属性:
to:跳转路径
activeClassName:自定义高亮样式
标签体为特殊的属性children
<NavLink activeClassName='atguigu' to='/about'>About</NavLink>
3.4、Switch
作用: 当一个路径对应两个组件时,会都显示,外层加上Switch可以使路由只显示相同路径下的第一个组件
<Switch>
<Route path='/home' component={Home}/>
<Route path='/home' component={Test}/>
</Switch>
3.5、Redirect
作用: 当找不到对应组件时,默认跳转组件路径
属性:
to:组件跳转路径
<Redirect to='/about' />
4、嵌套路由
二级路由要添加上一级路由的路径
// news的上一级路由为home,前面必须加上/home
<NavLink to='/home/news'>News</NavLink>
<Route path='/home/news' component={News} />
5、向路由组件传递参数数据
5.1、params参数
1) 向路由组件传递params参数
<Link to={'/home/message/detail/tom/18'}>标签体内容</Link>
2) 声明接收params参数
<Route path='/home/message/detail/:name/:age' component={Detail}></Route>
3) 路由组件接收参数(this.props.match.params
)
const {name, age} = this.props.match.params
5.2、search参数
1) 向路由组件传递search参数
<Link to={'/home/message/detail/?name=tom&age=18'}>标签体内容</Link>
2)search参数无需声明接收,正常注册路由即可
<Route path='/home/message/detail' component={Detail}></Route>
3) 路由组件接收参数
需要借助qs将拿到的字符串参数改为对象
import qs from 'qs' //安装依赖为npm install qs --save
const {search} = this.props.location
const {name, age} = qs.parse(search.substring(1)) // 截掉问号
5.3、state参数
1) 向路由组件传递state参数
<Link to={{pathname:'/home/message/detail', state:{name:'tom', age: 18}}}>标签体内容</Link>
2)state参数无需声明接收,正常注册路由即可
<Route path='/home/message/detail' component={Detail}></Route>
3) 路由组件接收参数
const {name, age} = this.props.location.state || {}
6、编程式路由导航
借助this.prosp.history
对象上的API对操作路由跳转、前进、后退
this.prosp.history.push()
this.prosp.history.replace()
this.prosp.history.goBack()
this.prosp.history.goForward()
this.prosp.history.go()
replaceShow = (id,title) => {
// replace跳转+携带params参数
// this.props.history.replace(`/home/message/detail/${id}/${title}`)
// replace跳转 + 携带query参数
// this.props.history.replace(`/home/message/detail?id=${id}&title=${title}`)
// replace跳转 + 携带state参数
this.props.history.replace('/home/message/detail', {id:id, title: title})
}
pushShow = (id, title) => {
// push跳转+携带params参数
// this.props.history.push(`/home/message/detail/${id}/${title}`)
// push跳转+携带query参数
// this.props.history.push(`/home/message/detail?id=${id}&title=${title}`)
// push跳转+携带state参数
this.props.history.push('/home/message/detail', {id:id, title: title})
}
7、lazyLoad
//1.通过React的lazy函数配合import()函数动态加载路由组件 ===> 路由组件代码会被分开打包
const Login = lazy(()=>import('@/pages/Login'))
//2.通过<Suspense>指定在加载得到路由打包文件前显示一个自定义loading界面
<Suspense fallback={<h1>loading.....</h1>}>
<Switch>
<Route path="/xxx" component={Xxxx}/>
<Redirect to="/login"/>
</Switch>
</Suspense>
8、BrowserRouter与HashRouter的区别
1.底层原理不一样:
BrowserRouter使用的是H5的history API,不兼容IE9及以下版本。
HashRouter使用的是URL的哈希值。
2.path表现形式不一样
BrowserRouter的路径中没有#,例如:localhost:3000/demo/test
HashRouter的路径包含#,例如:localhost:3000/#/demo/test
3.刷新后对路由state参数的影响
(1).BrowserRouter没有任何影响,因为state保存在history对象中。
(2).HashRouter刷新后会导致路由state参数的丢失!!!
4.备注:HashRouter可以用于解决一些路径错误相关的问题。
8、其它
6.1、Fragment
加上后可以不用必须有一个真实的DOM根标签了,类似于vue中的telement
<Fragment><Fragment>
<></>
6.2、Context
一种组件间通信方式, 常用于【祖组件】与【后代组件】间通信
1、创建Context容器对象
const XxxContext = React.createContext()
2、渲染子组时,外面包裹xxxContext.Provider, 通过value属性给后代组件传递数据
<xxxContext.Provider value={数据}>
子组件
</xxxContext.Provider>
在应用开发中一般不用context, 一般都用它的封装react插件
3、后代组件读取数据
//第一种方式:仅适用于类组件
static contextType = xxxContext // 声明接收context
this.context // 读取context中的value数据
//第二种方式: 函数组件与类组件都可以
<xxxContext.Consumer>
{
value => ( // value就是context中的value数据
要显示的内容
)
}
</xxxContext.Consumer>
6.3、组件优化
优化点:
1、只要执行setState(),即使不改变状态数据, 组件也会重新render() ==> 效率低
2、只当前组件重新render(), 就会自动重新render子组件,纵使子组件没有用到父组件的任何数据
解决:使用PureComponent,PureComponent重写了shouldComponentUpdate(), 只有state或props数据有变化才返回true
import React, { PureComponent } from 'react'
export default class Parent extends PureComponent {}
6.4、render props
类似于vue中slot
1、children props(组件间无法传递数据)
<A>
<B>xxxx</B>
</A>
{this.props.children}
2、render props
<A render={(data) => <C data={data}></C>}></A>
A组件: {this.props.render(内部state数据)}
C组件: 读取A组件传入的数据显示 {this.props.data}
6.5、错误边界
错误边界(Error boundary):用来捕获后代组件错误,渲染出备用页面(只能捕获后代组件生命周期产生的错误)
使用:getDerivedStateFromError配合componentDidCatch
// 生命周期函数,一旦后台组件报错,就会触发
static getDerivedStateFromError(error) {
console.log(error);
// 在render之前触发
// 返回新的state
return {
hasError: true,
};
}
componentDidCatch(error, info) {
// 统计页面的错误。发送请求发送到后台去
console.log(error, info);
}
6.6、封装NavLink
作用:多个Link有一样样式时,会导致代码冗余,如以下情况
import React, { Component } from 'react'
import { NavLink } from 'react-router-dom'
export default class MyNavLink extends Component {
render() {
// 标签体内容通过this.props.children接收
return (
<NavLink activeClassName='atguigu' className="list-group-item" {...this.props}/>
)
}
}
6.7、解决样式丢失问题
index.html文件中,若通过./的方式引入css文件,可能会导致样式丢失
解决办法:
1)、去掉.(推荐)
<link rel="stylesheet" href="/css/bootstrap.css">
2)、使用%PUBLIC_URL%(推荐)
<link rel="stylesheet" href="%PUBLIC_URL%/css/bootstrap.css">
3)、使用HashRouter包裹App(不推荐)
import {HashRouter} from 'react-router-dom'
<HashRouter>
<App/>
</HashRouter>
6.8、qs的用法
安装: npm install qs --save
引入:import qs from 'qs'
使用:
let obj = {name:'tom', age:18} // key=value&key=value urlencoden编码形式字符串
console.log(qs.stringify(obj)) //name=tom&age=18
let str = "carName='奔驰'&price=99"
console.log(qs.parse(str)) // {carName: '奔驰', price: 99}
补充:key=value&key=value
这种格式称之为urlencoden编码字符串
九、antd的按需引入+自定主题
1.安装依赖:
yarn add react-app-rewired customize-cra babel-plugin-import less less-loader
2.修改package.json
"scripts": {
"start": "react-app-rewired start",
"build": "react-app-rewired build",
"test": "react-app-rewired test",
"eject": "react-scripts eject"
},
3.根目录下创建config-overrides.js
//配置具体的修改规则
const { override, fixBabelImports,addLessLoader} = require('customize-cra');
module.exports = override(
fixBabelImports('import', {
libraryName: 'antd',
libraryDirectory: 'es',
style: true,
}),
addLessLoader({
lessOptions:{
javascriptEnabled: true,
modifyVars: { '@primary-color': 'green' },
}
}),
);
4.备注:
1)、不用在组件里亲自引入样式了,即:import 'antd/dist/antd.css’应该删掉
2)、若上述写法报错,则根据官方文档改为最新的按需引入方式即可(或者降低less-loader版本)
官方文档:antd按需引入样式+自定义主题官方文档