react全家桶
体验 React
React 从诞生之初就是可被逐步采用的,因而你可以按需引入或多或少的 React 特性。不管你是想体验下 React,用它给简单的 HTML 页面增加一点交互,还是要开始一个完全由 React 驱动的复杂应用,该章节内容里的链接都能帮你快速开始。
在线体验
如果你对体验 React 感兴趣,可以尝试在线代码编辑器。从 CodePen,CodeSandbox,Glitch, 或者 Stackblitz 开始一个 React 版本的 Hello World 模版。
如果你喜欢使用自己的文本编辑器,也可以下载这个 HTML 文件,然后编辑文件内容,最后再用浏览器从本地文件系统打开文件,预览页面效果。注意:这个文件中包含一个低效率的运行时代码转换脚本,所以我们推荐仅在简单的演示项目中使用。
在网站中添加 React
你可以立即在 HTML 文件中添加 React,然后选择逐渐拓展它的应用范围,或只在一些动态小部件中使用它。
创建新的 React 应用
当你刚开始一个 React 应用时,通过 HTML 的 script 标签引入 React 依然是最好的选项,因为这能让你的项目立即启动。
但随着应用越来越大,你可能会需要更加集成化的安装方式。我们推荐了一些 JavaScript 工具链,它们适合大型应用。它们只需很少甚至零配置,就能让你充分利用丰富的 React 生态。立即尝试。
JSX语法糖
官方提示: React 不强制要求使用 JSX,但是大多数人发现,在 JavaScript 代码中将 JSX 和 UI 放在一起时,会在视觉上有辅助作用。它还可以使 React 显示更多有用的错误和警告消息。
-
使用js表达式需要加括号
{}
: 表达式是一个值const name = 'Josh Perez'; const element = <h1>Hello, {name}</h1>; ReactDOM.render( element, document.getElementById('root') ); //等效的两种写法 const element = React.createElement( 'h1', {className: 'greeting'}, 'Hello, world!' ); const element = ( <h1 className="greeting"> Hello, world! </h1> ); //React.createElement() 会预先执行一些检查,以帮助你编写无错代码,但实际上它创建了一个这样的对象: // 注意:这是简化过的结构 const element = { type: 'h1', props: { className: 'greeting', children: 'Hello, world!' } };
-
属性使用
内联样式使用style={{key:value}}形式,如果原生css属性中有-,则属性第二个字符大写如font-size需要改为fontSize
//class <-> className
//style=“color:red;front-size:20px” <-> style={{color:‘red’ fontSize:‘20px’}}
3. jsx中渲染: 最外层只有一个根标签
```react
const element = (
<div>
<h1>Hello!</h1>
<h2>Good to see you here.</h2>
</div>
);
-
标签
//标签必须关闭否则报错 <input /> //不要使用自定义标签,除非你想使用组件,且组件名首字母需要大写 //react识别标签如果是小写字母开头则将其转为html标签如果没有与之对应的标签则报错 <good></good> //首字母大写则是组件,如果没有该组件则报错 <Good></Good>
-
循环
const element = ( <ul> { //item中需要有id属性赋予key唯一值 data.map(item,index)=>{ return <li key={item.id}>{item}</li> } } </ul> );
react开发工具
官方调试工具: https://chrome.google.com/webstore/detail/react-developer-tools/fmkadmapgofadopljbjfkapdkoienihi
组件
函数组件
//安装react插件React-Native/React/Redux snippets for es6/es7 代码片段 在vscode里快捷创建函数组件输入 rfc回车
function Demo () {
this;//babel编译会开启严格模式this指向undefined,在js中指向的是Window
return <h2>demo</h2>
}
ReactDOM.render(<Dome/>,document.getElementById('demo'))
类式组件
//代码片段 在vscode里快捷创建类式组件 rcc回车
//必须继承react内置类 类名大写
class Person extends React.Component{
name
age
//需要初始化
constructor(name,age){
//this指向实例对象
this.name = name
this.age=age
}
render(){
//必须有render方法,且必须有返回值
return <h1>honshen</h1>
}
}
//调用了new Person得到实例对象,且调用render方法,方法中的this指向其实例对象
ReactDOM.render(<Person/>,document.getElementById('demo'))
组件三大属性
state
constructor (prpos){
this.state = {} //初始化state
this.setState() //更新状态
this.state.name //读取name
}
props
<B title="abd" />
class B extends Components{
//读取属性
this.props
}
ref
//ref类似于getElementById
class B extends Components{
func = () => {
const {scanIn} = this.refs
scanIn.value //输入框的值
this.input.value //获取输入的值
}
render(
<input ref="scanIn" />
<input ref={(input)=>{this.input = input}} /> //推荐
)
}
高阶函数–柯里化函数
//不使用柯里化函数
function func(event,data) {
//函数体
}
<input onChange={(event)=>{this.func(event,data)}}
//柯里化函数
function func (data){
return (event)=>{
//函数体
}
}
<input onChange={this.func(data)}
React脚手架
//全局安装
npm i -g create-react-app
//创建应用
create-react-app my-app
webpack配置文件都已隐藏通过: yaen eject暴露webpack.config.js
脚手架配置代理
//建议使用axios请求库
//使用json-server配合调试 axios github地址: https://github.com/typicode/json-server
npm i -g json-server
//在项目文件夹下创建db.json
//在db.json目录监视监视json
json-server --watch db.json
//axios github地址: https://github.com/axios/axios
npm i axios --save
//get请求
axios({
//请求类型 GET POST PUT DELETE
method: "GET",
//请求地址
url: ""
//请求体
data:{
}
}).then((res)=>{
//回调
})
//方法1: 在package.json中配置
"proxy": "http://localhost:5000"
//方法二: 新建setupProxy.js文件
const proxy = require('http-proxy-middleware');
module.exports = function(app){
app.use(
proxy('请求路径',{
target: '目标地址',
changeOrigin: true, //修改请求源host
pathRewrite: {'^请求路径':''}
})
),
proxy('请求路径',{
target: '目标地址',
changeOrigin: true, //修改请求源host
pathRewrite: {'^请求路径':''}
})
)
}
组件通信
父子组件通信
//父组件直接传递数据给子组件
<Son data={this.state}/>
//子组件接收数据
const {data} = this.props
状态提升
//子组件将state提升到父组件里
//子组件修改父组件的数据
change(){}
<Son change={this.change} />
消息订阅与发布
订阅消息:
1. 消息名
2. 消息发布
//使用库 PubSubJs
npm i pubsub-js --save
//在componentDidMount(){}中订阅消息
PubSub.subscribe('消息名',(_,data)=>{
//处理
})
Redux状态管理js
集中管理组件中多个共享的状态
谷歌浏览器插件: redux-dev-tools
npm i redux-devtools-extension --save
//修改store.js
import {composeWithDevTools} from 'redux-devtools-extension'
import {createStore,combineReducers,applyMiddleware} from 'redux'
import thunk from 'redux-thunk'
const allReducer = combineReducers({
//对象
})
export default createStore(allReducer,composeWithDevTools(applyMiddleware(thunk))
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-KX7REb6I-1619771333081)(https://images-1300732204.cos.ap-chengdu.myqcloud.com/MarkDown/Snipaste_2021-04-01_21-04-56.png.png)]
三个原则:
1.单一数据源
2.State 是只读的
3.使用纯函数来执行修改
// 1.如何得到此对象?
import {createStore} from 'redux";
import reducer from './reducers'
const store = createStore(reducer)
// 2.此对象的功能?”
getState() //得到state
dispatch(action) // 分发action,触发reducer调用,产生新的 state
subscribe(listener) //注册监听,当产生了新的state时,自动调用
redux使用引用类型进行的是浅比较例如数组返回不能这样reture arr.unshift(data)
这样返回的是arr的地址和原来的值一样redux不会更新状态,可以使用return [data,...arr]
redux必须使用纯函数:同一个输入只有同一个输出
组件通信 Context
适用于组组件与后代组件通信
//在组组件中创建上下文对象Con是一个容器对象
const Con = React.createContext()
const {Provider} = Con
const {Consumer} = Con
//子组件被上下文包裹
<Provider value={data}>
<Son /> //在son的内部组价都能收到data但必须声明使用,通过后代组件的this.context
</Provider>
//在son组件内部想要使用context的组件需要声明使用=类式组件
static contextType = Con
this.context; //获取context对象
//也可以使用这样的方式在需要使用数据的地方--函数组件
<Consumer>
{
value => {
//函数
//返回一个组件
}
}
</Consumer>
SPA和React路由
SPA: 单页面应用,点击页面不会刷新只会做局部更新
react路由
import 'react-router-dom';
<BrowserRouter>
<HashRouter>
<Route>
<Redirect>
<Link> //默认是push,点击后向栈顶加入,当使用 <Link replace={true} />时不能回退历史记录
<NavLink>
<Switch>
{//注册路由,模糊匹配}
<Route path="路径" component={组件名} />
{//严格匹配,不能随便开}
<Switch>
<Route exact={true} path="路径" component={组件名} />
</Switch>
{//路由组件会默认收到一些props, this.props查看一下
//如果多个路由的path一致则同时展示两个组件,但这种不好,建议合并两个为一个组件
//路由较多时使用<Switch>
/*react样式丢失问题: 当在多级路由路径下刷新时,index.html中引入的css样式会请求相应css但路由所在路径刷新导致请求失败react会返回index.html的内容所以css请求返回的是index.html
解决方法: 1. 引入css使用根路径
2. 使用%PUBLIC_URL%作为样式的路径,推荐
3. 使用HashRouter
*/}
//当模糊匹配失败时
<Switch>
<Route path="" /
<Redirect to="" ///写在所有路由的最下方
</Switch>
react嵌套路由
//例如请求 /home/me 先匹配/home 当/home/me是Home组件
//组件1中
<Switch>
<Route path="/home/login"/>
<Route path="/home/me" component={Me}/> //匹配成功
<Redirect to="home/me" /> //写在所有路由的最下方
</Switch>
//app中
<Switch>
<Route path="/index"/>
<Route path="/home" component={Home}/> //匹配成功
<Redirect to="index" /> //写在所有路由的最下方
</Switch>
路由传参
//1. 路径传参params 路由占位符 :
<Link to={`/index/${id}/${info}`} /> //传参
<Route path = "/index/:id/:info" component={Index}></Route> //在注册路由时声明接收参数this.props.match.params中能够拿到id和info
//2. 传递search参数
<Link to={`/index?id=${id}&info=${info}`} /> //传参
<Route path = "/index/" component={Index}></Route> //无需申明接收参数,this.props.location.search中得到?id=12&info=12需要对search参数编码
//需要引入库 querystring
import qs from 'querystring'
qs.stringify(obj); //将对象转换为字符串
qs.parse(str); //将字符串转换为对象 qs.parse(this.props.location.search.slice(1))去除?号
//3. 传递state参数,这里的state不是组件的state,而是路由的参数,前两种参数传递会在地址栏显示但state传参不会
<Link to={{pathname: '/index',state:{id:1,info:2}}} />
<Route path = "/index/" component={Index}></Route> //无需申明接收参数在组件的this.props.location.state中,此种情况下state的参数由BrowserRouter维护,刷新页面不会丢失值,当清空浏览器的历史记录时会丢失
编程式路由导航
//路由组件中的导航函数
replace () {
this.props.history.replace('/idnex') //不能返回
//携带state参数
this.props.history.replace('/index',{id: 2,title: 1})
//前进
this.props.history.goForward(); //前进
this.props.history.goBack(); //后退
}
push (){
this.props.history.push('/idnex') //可返回
}
//一般组件导航
import {withRouter} from 'react-router-dom
class Header extends Component {
}
export default withRouter(Header) //withRouter是一个函数返回值是一个新组件,加工了Header组件使之可以使用路由组件的一些函数
HashRouter与BrowserRouter区别
1.底层原理
BrowserRouter使用的是H5的history API不兼容IE9及以下版本。HashRouter使用的是URL的哈希值。
2.ur1表现形式不一样
BrowserRouter的路径中没有#
例如: localhost: 3000/demo/testHashRouter的路径包含#,例如: localhost:3000/#/demo/test
3.刷新后对路由state参数的影响
(1).BrowserRouter没有任何影响,因为state保存在history对象中。
(2).HashRouter刷新后会导致路由state参数的丢失。
4.备注: HashRouter可以用于解决一些路径错误相关的问题。
Ant-design 蚂蚁设计
按钮及icon
//按钮
import 'antd/dist/antd.css';
import { Button } from 'antd';
<Button type="primary">Primary Button</Button>
<Button type="dashed" shape="circle" icon={<SearchOutlined />} /> //引入图标
//图标
import 'antd/dist/antd.css';
import {
HomeOutlined,
SettingFilled,
SmileOutlined,
SyncOutlined,
LoadingOutlined,
} from '@ant-design/icons';
<HomeOutlined />
<SettingFilled />
<SmileOutlined />
<SyncOutlined spin />
<SmileOutlined rotate={180} />
<LoadingOutlined />
//样式按需引入
官网中: https://ant.design/docs/react/use-with-create-react-app-cn
建议3.x版本: https://3x.ant.design/docs/react/use-with-create-react-app-cn
主题
https://3x.ant.design/docs/react/use-with-create-react-app-cn
https://3x.ant.design/docs/react/customize-theme-cn
项目构建
npm run build
# 使用第三方库快速搭建服务 seve会以当前文件夹作为服务器根目录
npm i serve -g #-g要写在最后
serve # 启动服务器
serve demo # 以当前文件夹下的demo文件夹作为根目录
如果报错检查一下系统环境变量PATH中是否添加了npm的bin路径
扩展
setState的两种写法
react的状态更新是异步的
state = {
//对象
}
//法1
func = () => {
this.setState({
//对象
})
//此处同步获取不到更新状态
}
//法2 对象式的setState
func = () => {
//setState是异步更新,callback回调里的状态是更新后的
//新状态不依赖原状态
this.setState({/*对象更新*/},callback())
}
//法3 函数式的setState
func1 = ()=>{
//state 就是this.state值 props是this.props
//这里也可以有回调函数
//新状态依赖原状态
this.setState(func2(state,props))
}
func2 = (state,props) = > return {/*对象*/};
懒加载 Lazy_load
import {lazy, Suspense} from 'react
//对路由组件使用
const Index = lazy(()=>{import('./Index')})
//用的时候会引入index组件,当请求Index组件在请求中时会显示Suspense组件
<Suspense fallback={/*可以写正在加载中的组件名,这个组件必须不能是懒加载组件*/}>
<Router>
<Index />
<Login />
</Router>
</Suspense>
钩子 Hooks
让函数式组件使用类式组件的state等具有生命周期
//state Hook 状态
function Index () {
const [状态,更新状态的方法] = React.useState(状态初始化值)
return (/*组件*/)
}
//effect Hook 生命周期
function Index () {
//第一个参数是生命周期函数,第二个是[]表示不检测其他改变,为空表示检测所有改变
//当[]中写入变量时表示检测变量的改变componentDidUpdate
React.useEffect(()=>{
//生命周期函数componentDidMount
return //该函数返回一个函数,这个函数是componentWillUnmount
},[])
return (/*组件*/)
}
//ref Hook
function Index () {
const ref = React.useRef()
return (<input type="text" ref={ref} /></inpu>)
}
多级嵌套解决 Fragment
import {Fragment} from 'react'
//react解析会丢弃 Fragment组件,该组件只能拥有key属性用于遍历
render(){
return (
<Fragment>
<div />
</Fragment>
)
}
//类似于,但空标签不能写任何属性
render(){
return (
<>
<div />
</>
)
}
组件优化 PureComponent
组件问题: 当子组件不使用父组件的状态时,父组件更新render也会触发子组件的render,只要执行setState即使不改变状态也会触发render
//使用PureComponent组件重写shouldComponentUpdata
/*
shouldComponentUpdata(nextProps,nextState){
//参数为下一个即将改变的props或state
return true;//更改组件状态
return false;//不会更改组件状态
}
*/
import {PureComponent} from 'react'
//修改继承
class Index extends PureComponent {
func = ()=>{
//别这样做,PureComponent是浅比较会导致状态不能更新
const obj = this.setState;
obj.name = "honshen";
this.setState(obj);
}
}
插槽技术 renderProps
//自定义组件中B在A的标签体内不会在页面展示被收集在A的this.props.children中
<A>
<B /> //想要展示B可以将A组件中加入B组件或 在A组价中添加 {this.props.children}
</A>
//当B想要使用A的state时,解决上述问题
<A render{(name)=>{<B name={name}/>}} /> //很灵活
class A {
render(){
const name = ''
return (
{this.props.children(name)})
}
}
错误边界 ErrorBoundary
在生产环境有效
//在容易发送错误的子组件的父组件中使用
class Father extends Components{
state = {
hsaError: ''; //准备一个状态,用于标识子组件是否出错
}
static getDerivedStateFromError (error) {
//子组件报错时会触发该函数
//返回一个错误对象
return {hasError: error}
}
componentDidCatch(error, errorInfo) {
// 你同样可以将错误日志上报给服务器
logErrorToMyService(error, errorInfo);
}
render(
{this.state.hasError?"Error":<son/>}
)
}
//componentDidCatch(){在这个地方统计出错反馈给服务器} //子组件渲染出错时调用类似getDerivedStateFromError
name}/>}} /> //很灵活
class A {
render(){
const name = ‘’
return (
{this.props.children(name)})
}
}
## 错误边界 ErrorBoundary
**在生产环境有效**
```react
//在容易发送错误的子组件的父组件中使用
class Father extends Components{
state = {
hsaError: ''; //准备一个状态,用于标识子组件是否出错
}
static getDerivedStateFromError (error) {
//子组件报错时会触发该函数
//返回一个错误对象
return {hasError: error}
}
componentDidCatch(error, errorInfo) {
// 你同样可以将错误日志上报给服务器
logErrorToMyService(error, errorInfo);
}
render(
{this.state.hasError?"Error":<son/>}
)
}
//componentDidCatch(){在这个地方统计出错反馈给服务器} //子组件渲染出错时调用类似getDerivedStateFromError