react
框架库
react-router
路由pubsub
消息管理redux
状态管理ant-design
UI库
要点
jsx
最后翻译过来就是React.createElement
方法createElement(element,options,content)
elment
元素名称options
属性对象content
属性里面的内容demo: createElement('h1',{id:'test'},'hello react')
- 虚拟dom解析后是Object
jsx注意事项
- 变量用一个大括号括起来;
- 如果引用
class
样式,需要用className
- 定义的
jsx
不需要引号,如果有多层,用小括号括起来 - 内联样式
style
要用对象的形式,且属性要用驼峰命名 - 只允许一个根标签
- 标签必须闭合
自定义组件以大写字母开头
const name='zhangsan' const Rdom=(
{name}
{name}
react
创建函数组件- 函数名大写,且要有返回值
引用需要用标签,且标签要有闭合
function Demo(){ return
demo test
} ReactDOM.render(,document.getElementById('test'))
react
创建类组件- 自定义组件类必须继承
React.Component
- 类组件里面必须定义
render
方法,并且需要返回值 - 调用组件,渲染到页面,组件是命名是大写开头
- 调用组件会
new
一个组件,然后调用render
方法,且类里面this
指向的是组件创建的实力对象,也叫组件实力对象。
class MyComponent extends React.Component{ render(){ return (
test
) } } ReactDOM.render(,document.getElementById('test')) - 自定义组件类必须继承
react
事件以及state
1.节点绑定事件需要用
on
+事件名,需要驼峰命名2.事件的回调函数需要用
bind
重新绑定,否则this
会丢失,或者用箭头函数赋值给属性,绑定该属性3.
state
对象里面需要定义需要变化的状态(属性)4.更改
state
里面的状态,需要用this.setState
方法5.自定义方法需要用箭头函数赋值给属性,或者用
bind
方法绑定this
class ChangeWeather extends React.Component{ constructor(){ super() this.state={ flag:true } this.changeFlag=this.changeFlag.bind(this) } render(){ const {flag}=this.state return (
今天天气很{flag?'好':'不好'}
) } changeFlag(){ this.setState({ flag:!this.state.flag }) } } ReactDOM.render(,document.getElementById('test')) //上面例子精简版 class ChangeWeather extends React.Component { state = { flag: true } render() { const { flag } = this.state return (
今天天气很{flag ? '好' : '不好'}
) } changeFlag = () => { this.setState({ flag: !this.state.flag }) } } ReactDOM.render(, document.getElementById('test')) react
里面的props
1.通过标签传递属性,可以通过扩展运算符进行传递
2.组件里面通过
props
引用3.如果是变量需要用大括号的形式进行传递
class ChangeWeather extends React.Component{ render (){ return (
{this.props.title} {this.props.age} {this.props.obj.test} {this.props.test}) } } const obj={ test:123 } ReactDOM.render(, document.getElementById('test'))
props
的属性限制以及默认值1.16.x版本之前是用
React.propsType.xxxx
进行限制,之后的版本是需要引入propstype.js
,然后再类的propTypes
属性上面进行定义2.限制属性的时候,直接定义到类上面进行限制
3.如果是函数限制传的类型为
func
,而不是function
4.如果没有传值,需要给默认值,需要再类的
defaultProps
属性上面进行定义默认值5.定义的这些规则需要咋渲染之前进行定义
6.定义的属性规则也可以用静态属性进行定义
static
//直接定义到类上面 class ChangeWeather extends React.Component{ render (){ return (
{this.props.title} {this.props.age} {this.props.obj.test} {this.props.test}) } } //需要再渲染之前进行规则定义,在类的propTypes上面进行定义 ChangeWeather.propTypes = { title: PropTypes.string.isRequired, fun:PropTypes.func.isRequired//函数要用func这个类型 } //默认值需要再类的defaultProps属性上面进行定义 ChangeWeather.defaultProps={ title:"fdsaf", fun:function(){} } const obj={ test:123, } ReactDOM.render(, document.getElementById('test')) //用静态属性的方式 class ChangeWeather extends React.Component { static propTypes = { title: PropTypes.string.isRequired, fun: PropTypes.func.isRequired } static defaultProps = { title: "fdsaf", fun: function () { } } render() { return (
{this.props.title} {this.props.age} {this.props.obj.test} {this.props.test}) } } const obj = { test: 123, } ReactDOM.render(, document.getElementById('test')) react
里面的ref
1.在节点里面通过
ref
属性进行绑定2.在逻辑里面通过
refs
接受3.接受的
ref
是一个真实节点class Test extends React.Component{ render(){ return (
,document.getElementById('test')) 4.
ref
通过回调方式获取/* ref 回调函数的形式 */ class Reftest extends React.Component{ inputel=null btnClick=() => { alert(this.inputel.value) } render(){ return (
{this.inputel=el}}/>) } } ReactDOM.render(,document.getElementById('test')) ref
如果用内联的方式进行获取,在数据更新的 时候会调用两次回调函数,第一次会给回调函数传null
第二次会传当前的节点。解决办法为直接用绑定的回调函数。只会调用一次回调函数,不会调用多次。但是内联和绑定的方式不会有任何影响。开发中一般用内联的方式进行开发。ref
的创建方式三,通过React.createRef
的方式创建。该函数调用后会返回一个对象,key
为current
的对象,值为节点。有多少个ref
就要调用几次前面的函数。官方推荐写法/* ref React.createRef的方式 */ class Test extends React.Component{ //有多少个就要创建多少个 refstest=React.createRef(); btnclick=()=>{ console.log(this.refstest.current.value); } render(){ return (
//这里了是需要绑定上面返回的属性) } } ReactDOM.render(,document.getElementById('test'))
react
的非受控组件
- 所有的表单组件现用现取的组件。取值需要用到
ref
react
的受控组件所有的输入框的值都存到状态里面,类似
vue
的双向绑定/* 受控组件*/ class Test extends React.Component { state = { name: '' } getTestValue = (e) => { this.setState({ name: e.target.value }) } handleClick = () => { console.log(this.state.name); } render() { return (
, document.getElementById('test'))
函数的柯力化以及高阶函数
1.高阶函数:
1.1函数返回一个函数
1.2 函数传参,该参数为一个函数
2.柯力化函数:
2.1函数返回函数,最后返回的函数一起处理前面函数的参数。
class Test extends React.Component { state = { name: '' } getTestValue = (name) => { return (event) => { console.log(name,event); } } handleClick = () => { console.log(this.state.name); } render() { return (
, document.getElementById('test')) react
的声明周期ReactDOM.unmountComponentAtNode(el)
卸载组件//只要一获取值就销毁组件 class Test extends React.Component { getValue(key, e) { ReactDOM.unmountComponentAtNode(document.getElementById('test')) } render() { return (
{ this.getValue('userName', e) }} />) } } ReactDOM.render(, document.getElementById('test')) react
组件生命周期1.
componentDidMount
//组件挂在时调用,只调用一次2.componentWillUnmount //组件即将销毁
/* 组件生命周期演示 */ class Test extends React.Component { state = { count: 1 } timer = null; render() { const { count } = this.state; return (
数字:{count}点击我, document.getElementById('test')) 子组件生命周期执行顺序
constructor
componentWillMount
render
componentDidMount
componentWillUnmount
getSnapshotBeforeUpdate
//初始化的声明周期执行顺序 class Demo extends React.Component { constructor(props) { super(props) console.log('constructor'); this.state = { count: 1 } } componentWillMount() { console.log('componentWillMount'); } componentWillUnmount() { console.log('componentWillUnmount'); } render() { console.log('render'); const { count } = this.state return (
展示的count:{count}点击我卸载组件, document.getElementById('test')) class A extends React.Component{ state={ count:1 } render(){ console.log('render'); const {count}=this.state return (
count {count}点击我{this.props.name}) } } ReactDOM.render(, document.getElementById('test'))class Demo extends React.Component { state = { arr: [] } componentDidMount() { setInterval(() => { const { arr } = this.state; this.setState({ arr: ['新闻' + (arr.length + 1), ...arr] }) }, 1000) } // 新的声明周期,在更新前执行,可以像componentDidUpdate声明周期里面传递一个参数 getSnapshotBeforeUpdate() { return this.refs.list.scrollHeight } componentDidUpdate(prepops, prestates, scrollheight) { console.log(this.refs.list.scrollHeight, '...', scrollheight); this.refs.list.scrollTop += this.refs.list.scrollHeight - scrollheight } render() { return (
{ this.state.arr.map((item, index) => { return ({item}) }) }, document.getElementById('test')) react diff
算法- 通过数据渲染成虚拟
dom
- 通过
key
值去判断是否存在相同的值,如果key
值相同,判断对应的内容是否相同,若内容相同复用原来的dom
,如果不相同,创建新的dom
渲染到页面,如果key
值不相同直接生成dom
渲染到页面 - 使用
index
作为key
值存在的问题:可能会使效率低下(旧dom
可能复用不了),页面错位(若数据存在逆反操作)。
- 通过数据渲染成虚拟
react
脚手架1.全局安装
npm i -g create-react-app
2.进入要创建项目的目录控制台执行
create-react-app projectName
即创建成功样式模块化
1.利用文件名
XXX.module.css
,引入方式import hello from './xxx.module.css'
在用的地方用hello.className
的形式进行2.利用
less
进行嵌套react
组件父子通信1.父传子利用
props
进行传递2.子传父利用
props
给子组件传递函数,子组件通过props
接收该函数,然后调用,且可以传值。- 公共用的状态放到父组件中(状态提升),状态定义到哪里,对应的方法就对应在哪个组件中
解决跨域
利用
package.json
的proxy
字段进行代理。请求的时候用本地的域名加端口号加接口名进行请求//pageage.json "development": [ "last 1 chrome version", "last 1 firefox version", "last 1 safari version" ], "proxy":"http://localhost:5000"
//请求的地址,端口号为本地的3000,3000代理到服务器为端口号5000的服务器 axios.get('http://localhost:3000/students') .then((res) => { console.log(res.data) }) }
2.在项目目录src/下新建setupProxy.js文件,然后写入如下代码:
const proxy = require('http - proxy - middleware'); module.exports = function (app) { app.use(proxy('/api', { target: 'http://localhost:5000', secure: false, changeOrigin: true, pathRewrite: { "^/api": "" }, })); };
兄弟组件之间的通信,利用
pubsubjs
安装
pubsub-js
yarn add pubsub-js
引入
pubsubjs
并在传递数据的组件中进行消息发布,在需要获取数据的组件中进行消息订阅componentDidMount() { this.id=Pubsub.subscribe('getData', (name, stateObj) => { this.setState(stateObj) }) } componentWillUnmount(){ pubsub.undescribe(this.id) } request=()=>{ axios.get( 'http://localhost:3000/xxx' ) .then((res) => { Pubsub.publish('getData',{ loading: false, users: res.data.items, err:false }) }) .catch((err) => { Pubsub.publish('getData',{ loading: false, err:err.message }) }) }
react
路由1.安装
react-router-dom
yarn add react-router-dom
引入必要组件
import React, { Component } from 'react' import { Link, Route } from 'react-router-dom' import About from './components/About' import Home from './components/Home' export default class App extends Component { render() { return ( ) } }
import React from 'react'; import ReactDOM from 'react-dom'; import { BrowserRouter } from 'react-router-dom' import './index.css'; import App from './App'; ReactDOM.render(
- 通过
this.props.children
可以获取标签体内容 switch
组件包裹路由只会匹配第一个匹配到 的路由进行渲染对应组件react
多级路由导致样式丢失问题public
文件下的index.html
文件引入文件路径修改为/
绝对路径的这种形式,或者用%PUBLIC_URL%
这种形式进行引入。- 或者将
BrowserRouter
组件换成HashRouter
组件
`Router
组件中的exact`属性用于精准匹配路由Redirect
组件用于没有匹配到路径的时候,调转到默认路由,一般放到注册组件最后Switch
组件用于匹配到第一个路由就不往后面进行匹配NavLink
用于路由高亮显示,可以自定义className
Link
用于路由跳转嵌套路由
1.子组件注册组件需要带上父组件的
path
- 父组件
import React, { Component } from 'react' import { Switch, Route ,Redirect} from 'react-router-dom' import About from './pages/About' import Home from './pages/Home' import MyNavLink from './components/MyNavLink' import './App.css' export default class App extends Component { render() { return (
React Router Demo
About Home - 子组件
import React from "react"; import Message from "./Message"; import News from "./News"; import MyNavLink from "../../components/MyNavLink"; import { Switch, Route,Redirect } from 'react-router-dom' export default class Home extends React.Component { render() { console.log(this.props); return (
Home组件内容
-
News -
message
路由组件传参
params
传参1.跳转的地方需要进行参数按顺序传递
2.注册的地方需要进行按顺序进行接收
3.子组件通过
this.props.match.params
获取import React, { Component } from 'react' import { Route } from 'react-router'; import { Link } from 'react-router-dom'; import MsgDetail from './MsgDetail'; export default class Message extends Component { state = { students: [{ id: 1, name: '张三', age: 12 }, { id: 2, name: '李四', age: 13 }, { id: 3, name: '王二', age: 14 }] } render() { const { students } = this.state; return (
-
{
students.map((item) => {
return (
- {item.name} ) }) }
4.子组件进行获取
import React, { Component } from 'react'; class MsgDetail extends Component { render() { console.log(this.props); const { name, age } = this.props.match.params; return (
{name}----{ age}); } } export default MsgDetail;search
传参(类似vue
里面的query
)1.在跳转的地方进行传递
2.在子组件的
this.props.location.search
进行取值3.需要将
search
参数进行转换成对象的形式import React, { Component } from 'react' import { Route } from 'react-router'; import { Link } from 'react-router-dom'; import MsgDetail from './MsgDetail'; export default class Message extends Component { state = { students: [{ id: 1, name: '张三', age: 12 }, { id: 2, name: '李四', age: 13 }, { id: 3, name: '王二', age: 14 }] } render() { const { students } = this.state; return (
-
{
students.map((item) => {
return (
- {item.name} ) }) }
4.子组件进行获取
import React, { Component } from 'react'; import qs from 'querystring' class MsgDetail extends Component { render() { console.log(this.props); const { search } = this.props.location.search const { name,age}=qs.parse(search.slice(1)) return (
{name}----{ age}); } } export default MsgDetail;state
传参1.路由跳转的地方需要通过对象的方式进行传递,需要包含
path
以及state
属性。state
属性是一个对象2.子组件通过
this.props.locaiton.state
进行获取3.
state
传递的参数在地址栏上面是看不到的,上面的两种都是在地址栏上面看的到的。import React, { Component } from 'react' import { Route } from 'react-router'; import { Link } from 'react-router-dom'; import MsgDetail from './MsgDetail'; export default class Message extends Component { state = { students: [{ id: '00122', name: '张三', age: 12 }, { id: '00233', name: '李四', age: 13 }, { id: '003432423', name: '王二', age: 14 }] } render() { const { students } = this.state; return (
-
{
students.map((item) => {
return (
- {item.name} ) }) }
4.子组件进行接收
import React, { Component } from 'react'; import qs from 'querystring' class MsgDetail extends Component { render() { console.log(this.props); const { name,age}=this.props.location.state return (
{name}----{ age}); } } export default MsgDetail;
编程式组件导航
通过
this.props.history
的api
进行跳转jump = (name,age) => { this.props.history.push('/home/message/msgdetail', { name, age } ) }
withRouter
若想在一般组件中使用路由跳转的api
可以引入withRouter
方法import React, { Component } from 'react' import { withRouter } from 'react-router-dom' class Header extends Component { goBack = () => { this.props.history.goBack() } goFowrd = () => { this.props.history.goForward() } render() { return (
React Router Demo
antd
按需引入,可参考地址https://3x.ant.design/docs/react/use-with-create-react-app-cn
中的高级配置redux
1.安装
redux
yarn add redux
2.创建
redux
文件夹,在该文件下创建store
文件,action
文件,reducer
文件,constent
文件
3.store.js
import { createStore } from "redux";
import countRedux from './count_redux'
export default createStore(countRedux)
4.count_action.js
import {ADD,DESC } from './constent'
export const add = (data) => ({
type: ADD,
data
})
export const desc = (data) => ({
type: DESC,
data
})
5.count_redux.js
import { ADD,DESC} from './constent'
export default function (preState=0, action) {
const { type,data}=action
switch (type) {
case ADD:
return preState + data
case DESC:
return preState - data
default:
return preState
}
}
6.constent.js
export const ADD='add'
export const DESC='desc'
7.index.js
import React from "react";
import ReactDOM from "react-dom";
import App from "./App.js";
import store from './redux/store'
ReactDOM.render( , document.getElementById('root'))
store.subscribe(() => {
ReactDOM.render( , document.getElementById('root'))
})
异步
action
1.引入并安装
redux-thunk
2.利用中间件传入
thunk
3.
action
返回的是一个函数,该函数会带有dispatch
函数参数代码如下:
store.js
import { createStore, applyMiddleware } from "redux"; import countRedux from './count_redux' import thunk from 'redux-thunk' export default createStore(countRedux, applyMiddleware(thunk))
action.js
export const addAsync = (data, time) => { //返回一个带有dispatch的参数的函数 return (dispatch) => { setTimeout(() => { dispatch(add(data))//调用同步action },time) } }
react-redux
1.引入并安装
yarn add react-redux
2.创建容器组件,用于操作状态以及包裹ui
组件(链接ui
组件以及redux
)
// 引入要展示的UI组件
import countUi from '../../components/Count'
// 用于连接UI组件以及redux
import { connect } from 'react-redux'
// 引入action
import { add, desc, addAsync } from '../../redux/count_action'
// 将store里面的状态映射到props,可以用过UI组件进行操作(ui组件通过this.props.xxx获取)
function mapStateToProps(state) {
return {
count: state
}
}
// 将store里面的操作状态的方法映射到props,可以用过UI组件进行操作(ui组件通过this.props.xxx获取)
function mapDispatchToProps(dispatch) {
return {
add: (num) => {
dispatch(add(num))
},
desc: (num) => {
dispatch(desc(num))
},
addasync: (num, time) => {
dispatch(addAsync(num,time))
}
}
}
export default connect(mapStateToProps, mapDispatchToProps)(countUi)
- 通过
props
传递给子组件store
UI
组件的优化写法// 引入要展示的UI组件 import countUi from '../../components/Count' // 用于连接UI组件以及redux import { connect } from 'react-redux' // 引入action import { add, desc, addAsync } from '../../redux/count_action' // 将store里面的状态映射到props,可以用过UI组件进行操作(ui组件通过this.props.xxx获取) /* function mapStateToProps(state) { return { count: state } } */ // 将store里面的操作状态的方法映射到props,可以用过UI组件进行操作(ui组件通过this.props.xxx获取) /* function mapDispatchToProps(dispatch) { return { add: (num) => { dispatch(add(num)) }, desc: (num) => { dispatch(desc(num)) }, addasync: (num, time) => { dispatch(addAsync(num,time)) } } } */ // export default connect(mapStateToProps, mapDispatchToProps)(countUi) // 优化写法 export default connect( state => ({ count: state }), //这里可以返回一个简单对象,内部可以自动分发action { add: add, desc: desc, addasync: addAsync } )(countUi)
5.引入
react-redux
可以不用手动监听转态变化import React from "react"; import ReactDOM from "react-dom"; import App from "./App.js"; // import store from './redux/store' ReactDOM.render(
, document.getElementById('root')) // 引入react-redux后可以不用手动监听状态变化 // // 监测redux转态改变,redux状态改变会重新渲染页面 // store.subscribe(() => { // ReactDOM.render( , document.getElementById('root')) // }) 6.利用
react-redux
的provider
传递store
import React from "react"; import ReactDOM from "react-dom"; import App from "./App.js"; import store from './redux/store' import { Provider } from 'react-redux' ReactDOM.render(
多个
reducer
利用combineReducers
函数合并import { createStore, applyMiddleware ,combineReducers } from "redux"; import countRedux from './reducers/count_redux' import person from "./reducers/person"; import thunk from 'redux-thunk' export default createStore( // 多个reducer用combineReducers合并 combineReducers({ count: countRedux, person: person, }), applyMiddleware(thunk) );
reducer
必须是一个纯函数,即传入的什么参数,就返回改参数,且传入的参数不能被改写。如果传入的是对象或者是数组。返回的值也应该是一个对象或数组,不可以在原有的对象或数组中进行操作再返回import { ADD_PERSON } from "../constent"; export default function (prestate=[],action) { const { type, data } = action; switch (type) { case ADD_PERSON: return [data,...prestate] default: return [] } }
redux
配置的开发者工具- 谷歌浏览器安装
redux-devtools
- 在项目中安装
redux-devtools-extension
在
store.js
中进行如下配置import { createStore, applyMiddleware } from "redux"; import thunk from 'redux-thunk' import { composeWithDevTools } from "redux-devtools-extension"; import reducers from "./reducers"; export default createStore( // 多个reducer用combineReducers合并 reducers, composeWithDevTools(applyMiddleware(thunk)) );
- 谷歌浏览器安装
打包后的文件如何在本地启动
1.全局安装
serve
插件npm install -g serve
进入打包的文件目录运行
serve
命令