react:构建用户界面的javascript库
下载:npm i react react-dom
react 核心,提供创建元素,组件的功能
react-dom 提供DOM相关的功能
基本使用--引入js文件的使用方法
<script>
const title = React.createElement('h1', null,'hello react')// 标签名 属性 子节点
ReactDOM.render(title, document.getElementById('root')) // 标签变量 挂载标签
</script>
react脚手架
创建react脚手架:npm create-react-app my-app
启动项目:npm start
jsx---javascript xml
简单使用
const title = <h1>hello react!!!</h1>
ReactDOM.render(title, document.getElementById('root')) // 标签变量 挂载标签
react与html的不同
class-->className for-->htmlFor tabindex-->tabIndex
嵌入js表达式
const name = 'mike'
const test = <h1>i am h1</h1>
const title = (<h1>{name}</h1>)
//{}中可以用任意js表达式
条件渲染
const isLoading = true
const loadData = ()=>{
if(isLoading){
return <div>loading....</div>
}
return <div>加载完毕</div>
}
const title =(
<h1>{loadData()}</h1>
)
ReactDOM.render(title,document.getElementById('root'))
列表渲染
const songs =[
{id:1,name:'痴心绝对'},
{id:2,name:'像我这样的人'},
{id:3,name:'南山南'}
]
const list ={
<ul>{songs.map(item=><li key={item.id}>{item.name}</li>) }</ul>
}
样式处理
// 行内样式
const title =(
<h1 style={{color:'red',background-color:'skyblue'}}></h1>
)
// 类名添加
const title =(
<h1 className="name"></h1>
)
react组件基础
函数创建组件--函数组件---大写字母开头---必须有返回值
function Hello(){
return <h1>hello react!!!</h1>
}
ReactDOM.render(<Hello></Hello>,document.getElementById('root'))
类组件---空return null
class Hello extends React.Component{
render(){
return <h1>hello react!!!</h1>
}
}
ReactDOM.render(<Hello></Hello>,root)
抽离js文件
//hello.js
import react from 'react'
class Hello extends React.Component{
render(){
return <h1>hello react!!!</h1>
}
}
export default Hello
// index.js
import Hello from ''
ReactDOM.render(<Hello></Hello>,root)
react事件处理
事件绑定
import react from 'react'
class Hello extends React.Component{
handleClick(){
console.log(1)
}
render(){
return <h1 onClick={this.handleClick}>hello react!!!</h1>
}
}
export default Hello
事件对象
import react from 'react'
class Hello extends React.Component{
handleClick(e){ // e事件对象
e.preventDefault()
console.log(1)
}
render(){
return <h1 onClick={this.handleClick}>hello react!!!</h1>
}
}
export default Hello
有状态组件--类组件和无状态组件---函数组件
有状态和无状态是区分组件页面是否与用户产生交互
组件中的state和setstate
import react from 'react'
class Hello extends React.Component{
// 第一种写法
constructor(){
super(){
this.state ={
count:0
}
}
}
// 第二种写法
state ={
count:0
}
render(){
return <h1>{this.state.count}</h1>
}
}
export default Hello
//获取状态 this.state.count
//修改状态 this.setstate({count:this.state.count+1})
解决this指向问题
onIncrement(){
console.log(1)
this.setState({
count:this.state.count+1
})
}
//1
<button onClick={()=>this.onIncrement()}></button>
//2
onIncrement=()=>{
console.log(1)
this.setState({
count:this.state.count+1
})
}
//3
constructor(){
super()
this.onIncrement = this.onIncrement.bind(this)
}
表单处理--受控组件和非受控组件
受控组件
import react from 'react'
class Hello extends React.Component{
state={
text:''
}
render(){
return <input value={this.state.text} onChange={e=>this.setState({text:e.target.value})}>hello react!!!</input>
}
}
export default Hello
controlSlect = e =>{
this.setState({city:e.target.value})
}
<select value={this.state.city} onChange={this.controlSelect}></select>
//复选框
<input type="checkbox" checked={this.state.falg} onChange={e=>{this.setState({falg:e.target.checked})}}></input>
受控组件的优化
1.给表单元素添加name属性,名称与state相同
import react from 'react'
class Hello extends React.Component{
state={
text:''
}
controlFrom = e =>{
// 获得当前dom值
const value = e.terget.type === 'checkbox'? e.target.checked:e.target.value
const name = e.target.name
this.setState({
[name]:value
})
}
render(){
return <input name="text" value={this.state.text} onChange={this.controlForm})}>hello react!!!</input>
}
}
export default Hello
非受控组件
import react from 'react'
class Hello extends React.Component{
constructor(){
super()
// 创建ref对象
this.textRef = React.createRef()
}
render(){
return (
// 绑定ref
<input type="text" ref={this.textRef} />
// 使用ref表单元素值
<button onClick={e=>{console.log(this.textRef.current.vaule)}}></button>
)
}
}
export default Hello
组件进阶---数据通信
<Hello age={19} fn={()=>{console.log(1)}} tag={<p>标签</p>} />
function Hello (props){
console.log(props.age)
return null
}
class Hello extends React.Component{
constructor(props){
super(props)
}
render(){
return <h1>{this.props.age}</h1>
}
}
// props是只读对象 只可以读取,不可以修改
// constructor 构造函数是不能直接拿到props
组件通信
父向子
class Parent extends React.Component{
state={
lastName='王'
}
render(){
return <Child name={this.state.lastName}></Child>
}
}
class Child extends React.Component{
render(){
return <h1>{this.props.name}</h1>
}
}
子向父
class Parent extends React.Component{
state={
msg:''
}
// 1 父组件提供回调函数
getChildMsg = (data)=>{
console.log(data)
// 4改变state
this.setState({
msg:data
})
}
render(){
// 2 子组件通过props调用回调函数
return <Child fn={this.state.lastName}></Child>
}
}
class Child extends React.Component{
state ={
msg:'hello my father!!'
}
render(){
// 3 子组件调用
return <button onClick={this.props.fn()}></button>
}
}
兄弟组件
class Parent extends React.Component{
state={
count:0
}
controlCount = ()=>{
this.setState({
count:this.state.count+1
})
}
render(){
return (
<Child1 count={this.state.count} />
<Child2 controlCount={this.controlCount} />
)
}
}
class Child1 extends React.Component{
render(){
return <h1>{this.props.count}</h1>
}
}
class Child2 extends React.Component{
render(){
return <button onClick={this.props.controlCount}>+1</button>
}
}
context组件----跨组件传递数据
const {Provider,Consumer} = React.createContext()
class Parent extends React.Component{
render(){
return (
<Provider value="pink">
<Child1 />
</Provider>
)
}
}
class Child1 extends React.Component{
render(){
return <Child2 />
}
}
class Child2 extends React.Component{
render(){
return (
<Consumer>
{data=><p>{data}</p>}
<Consumer>
)
}
}
props深入
children属性:表示组件标签的子节点。当组件标签有子节点时,自动产生
function Hello(props){
return (
<div>{props.children}</div>
)
}
<Hello>这是子节点|<p></p>|{()=>{}}</Hello>
props校验
安装包 npm i prop-types
import PropTypes from 'prop-types'
function App(props){
return null
}
App.propTypes ={
colors:PropTypes.array
}
// 约束规则 array bool func number object string element 元素
// PropTypes.bool.isRequired
// PropTypes.shape({color:PropTypes.string,fontsize:PropTypes.number}) 特定规则
// 默认值
App.defaultProps={
pageSize:10
}
组件生命周期
只有类组件有生命周期
三个阶段:创建时--》更新时--》卸载时
创建时(挂载阶段):页面加载时,执行钩子函数
执行顺序:constructor -->render--> componentDidMount
constructor:组件创建时触发,一般解决:初始化state和处理this指向问题
render:每次组件渲染都会触发,一般解决:渲染ui但是在render不能调用setState
componentDidMount:组件挂载之后,一般解决:发送网络请求和DOM操作
更新时:导致更新的事件:new props setState forceUpdate
执行顺序:render--》componentDidUpdate
componentDidUpdate:组件重新渲染后(不算第一次),一般解决:发送网络请求和DOM操作,但是要使用setState一定要判断, 不然陷入死循环。可以获得上一次的props componentDidUpdate(prevprops)
componentDidUpdate(prevprops){
if(prevprops.count!=this.props.count){
// 网络请求
// setState
// 执行两次
}
}
卸载阶段:组件从页面消失是
componentWillUnmount: 组件卸载时,一般解决清理工作:比如清理定时器。
render props---高阶组件
class Mouse extends React.Component{
// 鼠标位置
state ={
x:0,
y:0
}
// 鼠标移动事件的处理程序
handleMouseMove = e =>{
this.setState({
x:e.clientX,
y:e.clientY
})
}
// 监听鼠标移动事件
componentDidMount(){
window.addEventListener('mousemove',this.handleMouseMove)
}
render(){
return this.props.render(this.state)
}
}
class Child extends React.Component{
render(){
return <Mouse render={(mouse)=>{
return <p>x:{mouse.x} y:{mouse.y}</p>
}} />
}
}
// children 模式
class Mouse extends React.Component{
// 鼠标位置
state ={
x:0,
y:0
}
// 鼠标移动事件的处理程序
handleMouseMove = e =>{
this.setState({
x:e.clientX,
y:e.clientY
})
}
// 监听鼠标移动事件
componentDidMount(){
window.addEventListener('mousemove',this.handleMouseMove)
}
render(){
return this.props.children(this.state)
}
}
class Child extends React.Component{
render(){
return <Mouse>
{({x,y})=>{
<img src={img} alt="猫" />
}}
</Mouse>
}
}
// 优化
1.对props的属性进行校验
2.在组件卸载时解除挂载的事件
高阶组件
实际是一个可以包装组件的函数
function WithMouse(WrappedComponent){
// 调试的时候显示的名字
Mouse.displayName='WithMouse${getDisplayName(WrappedComponent)}'
function getDisplayName(WrappedComponent){
return WrappedComponent.displayName || WrappedComponent.name || 'component'
}
class Mouse extends React.Component{
// 鼠标位置
state ={
x:0,
y:0
}
// 鼠标移动事件的处理程序
handleMouseMove = e =>{
this.setState({
x:e.clientX,
y:e.clientY
})
}
// 监听鼠标移动事件
componentDidMount(){
window.addEventListener('mousemove',this.handleMouseMove)
}
render(){
return <WrappedComponent {...this.state} {...this.props} ></WrappedComponent>
}
}
return Mouse
}
react原理解密
setState异步更新数据
class Component extends React.component{
state ={
count:1
}
handClick = ()=>{
// 1
this.setState({
count:this.count+1
})
console.log(this.state.count)
// 调用handClick会打印1
//2
this.setState({
count:this.count+1
})
console.log(this.state.count)
this.setState({
count:this.count+1 // 1+1
})
// 最终count为2 打印1 setState会合并更新,执行一次render更新
// 注意后面的setState不要依赖前面setState的结果
// setState推荐代码
setState((state,props)=>{
return {
count: state.count +1
}
})
// 该形式依旧是异步操作,但是两个setState的state会实时更新
// setState的第二个参数 回调函数 会在dom渲染之后执行
setState((state,props)=>{
return {
count: state.count +1
}
},()=>{
console.log(state.count)
// 打印出2
})
}
render(){
return null
}
}
jsx语法转化过程
jsx是createELement的简化语法
jsx会被babel/preset-react转化为createElement
组件更新机制
组件更新之后,只会影响到组件和所有的后代组件
组件性能优化
1.减轻state:只存储跟组件渲染相关的数据,将没关系的数据直接挂载到this上
2.避免不必要的重新渲染:父组件的更新也会引起子组件更新, 解决方式:
//钩子函数
shouldComponentUpdate(nextProps,nextState){ // 最新的state和props
// 可以加条件判断 if(nextState.count !== this.state.count)
return false
}
// 执行顺序 shouldComponentUpdate -- > render
纯组件
将React.Component 换成 React.PureComponent
将shouldComponentUpdate() 的渲染问题自动解决
纯组件是浅对比----解决方法,返回新对象,不是只改变值
虚拟dom和diff算法----实现部分更新
虚拟dom使react可以脱离浏览器
react路由
功能从一个页面转到另一个页面
路由的基本使用
1.npm i react-router-dom
2.导入:import {BrowserRouter as Router, Route,Link} from 'react-router-dom'
3.Router组件包裹整个应用
4.Link 作为导航菜单 <Link to=""></Link>
5.使用Route组件配置路由规则和要展示的组件
<Route path="" component="{}"></Route>
Router一个react应用只能有一次 或HashRouter /#/first
link 会编译成一个a标签
路由的执行过程
点击link,url会被修改,根据url中pathname将组件渲染
编程式导航
用js代码进行路由导航
this.props.history.push('/home')
this.props.history.go(-1) //-1 上一页
默认路由
<route path="/" component="{}">
精确匹配
<Route exact path="/" component="{}">