react基础

概括

React是一个用于构建用户界面的JavaScript库,MVC角度React是V,可以开发web应用、移动端原生应用(react-naive)、VR应用(react360)

特点

声明式

其结构与html一样

基于组件

组件是React最重要内容
组件表示页面中的部分内容

基本使用

安装

安装命令:npm i react react-dom
react包是核心,提供创建元素、组件等功能
react-dom包提供DOM相关功能

使用

1.引入react和react-dom两个js文件
2.创建react元素
3.渲染React元素到页面中

//引入文件
<script src="./node-modules/react/umd/reeact.development.js"></script>
<script src="./node-modules/react-dom/umd/reeact-dom.development.js"></script>
//react挂载位置
<div id="root"></div>
<script>
	//创建react元素,(元素名称,元素属性,元素子节点...)(有更好的替代),元素属性可以是个对象,
	count title = React.createElement('h1', null, 'Hello React')
	//渲染react元素,(要渲染的react元素,挂载点)到页面挂载点上(重点方法)
	ReactDom.render(title, document.getElementById('root'))
</script>

脚手架

初始化项目命令:npx create-react-app my-app(my-app项目名称可修改)
启动项目命令(在根目录中):npm start

npx命令介绍

npx是npm v5.2.0引入的命令,主要为了提升包内提供的命令行工具的使用体验,以前需要先安装脚手架,使用npx无需安装脚手架包

脚手架中使用react

导入react包:import React from 'react'
导入react-dom包:import ReactDOM from 'react-dom'
调用React.createElement('h1', {hitle:'标题'}, 'Hello')方法创建react元素(第一个参数:要创建的react元素名称,第二个参数:该react元素属性,第三个及以后参数:该react元素子节点)
调用ReactDOM.render()方法把创建的react元素渲染到页面中(第一个元素:要渲染的react元素,第二个元素:dom对象,用于指定渲染到页面位置)

JSX语法基础

react脚手架中自动配置编译jsx语法包@babel/preset-react
jsx会被@babel/preset-react插件编译成createElement()方法
jsx可以使用html结构取代react创建元素,
创建react元素:const title = <h1>hello jsx</h1>
渲染react元素:ReactDOM.render(title, document.getElementById('root'))

注意点

react元素属性名使用驼峰命名法
特殊属性名:class>className,for>htmlFor,tabinndex>tabIndex
没有子节点双标签可以替换成单标签使用/>结尾
推荐使用()包裹JSX,避免js中自动插入分号陷进

嵌入js表达式

语法:{js表达式}
表达式中可以正常运行运算,方法和三元表达式,而且jsx自身也是一个表达式,但是js中对象例外,一般只出现在style属性中,而且if/for之类语句不能出现

条件渲染

可以使用if/else或三元运算符或逻辑与运算符(&&)来实现

列表渲染

渲染一组数据应该使用数组的map()方法
渲染列表式应该添加key属性,key属性的值要保证唯一
map()遍历谁给谁添加key属性
尽量避免使用索引号作为key

 const songs = [
        {id: 1, name: "我和我的祖国"},
        {id: 2, name: "中国机长"},
        {id: 3, name: "攀登者"}]
{songs.map(item =>  <li key={item.id}>{item.name}</li>)}

样式处理

行内样式:react样式使用style行内式style={{color: ‘red’, color: ‘red’}},外部是条件表达式,内部是对象,对象属性名使用驼峰表达式
类名(推荐):className=“类名”,类名在css文件中定义,并需要import导入js文件中

react组件

两种创建组件方式:类组件和函数组件
函数组件:使用函数名作为组件标签名,函数组件必须有返回值,组件名称首字母必须大写用以区分普通react元素

 //  写法一
const Hello = (props) => {      
    return <div>{props.message}</div>
}

 //  写法二
const Hello = props => <div>{props.message}</div>  

// 写法三
function Hello(props) {
    return <div>{props.message}</div>
}
//渲染组件
ReactDOM.render(<Hello/>, document.getElementById('root'))

类组件:类组件就是基于 ES6 语法,类组件继承React.Component父类,从而可以使用父类中的方法或属性,类组件必须提供render()方法,render()方法必须有返回值,使用函数名作为组件标签名,类组件名称首字母必须大写

class Hello extends React.Component {
  render() {
    return (<div className="demoClass">类组件内容</div>)
  }
}
//渲染组件
ReactDOM.render(<Hello/>, document.getElementById('root'))

创建单独组件:
创建js文件,import React from ‘react’(导入react),创建函数组件或类组件,export default 组件名(导出组件)
使用组件:
入口js文件中导入组件import 组件名 from ‘组件路径’(导入封装组件),然后渲染组件

事件处理

事件绑定

react事件绑定语法和dom事件语法相似
语法:on+事件名称={事件处理程序}(事件名称采用驼峰命名,类组件中事件处理程序前必须加this.)

事件对象

通过事件处理程序的参数获取事件对象
react中事件对象叫:合成事件(对象),合成事件兼容所有浏览器没有兼容问题

组件状态

状态(状态可变,数据驱动视图)

函数组件是无状态组件,负责静态结构展示,类组件是有状态组件,负责更新ui让页面动起来
state存储状态,setState修改状态

class Hello extends React.Component {
  // 简化语法,初始化类组件的 state
  state = {
    text: '内容'
  };
  render() {
    return (
      <div className="demoClass"  onClick={() => {this.setState({
      text: "内容已经被改变"
    })}}>类组件{this.state.text}
      </div>)
  }
}

state基本使用

状态即数据,状态时是私有的,只能在在组件内使用,通过this.state获取状态

setAtate()修改状态

状态是可变的,通过`this.setAtate({要修改的数据}),不能直接修改state中的值
setState()作用是修改state,更新UI,以数据驱动视图变化

从jsx抽离事件处理程序

为了保证jsx结构清晰,将逻辑抽离到单独方法中,使用事件绑定到jsx中
注意:单独的方法中this指向undefind,jsx中使用箭头函数this指向外部环境即render方法,render方法this即组件实例

事件绑定this指向

箭头函数:箭头函数自身不绑定this,其this为外部环境this,render()方法this为组件实例
Function.prototype.bind():利用ES5中的bind方法,将事件处理程序中的this与组件实例绑定

class Hello extends React.Component {
	// 初始化类组件的 state的旧版语法
	constructor() {
		super()
		this.state = {
			text: '内容'
		};
		this.onIncrement = this.onIncrement.bind(this)
	}
	render() {
		return (<div onClick={this.onIncrement}></div>)
	}
}

class实例方法:利用箭头函数,属于实验性语法但有babel存在可以直接使用(推荐使用)

class Hello extends React.Component {
  // 简化语法,初始化类组件的 state
	state = {
		text: '内容'
	};
	onIncrement = () => {this.setState({})}
	render() {
		return (<div onClick={this.onIncrement}></div>)
	}
}

表单处理

受控组件(推荐使用)

react将state与表单元素值value绑定一起,由state值控制表单元素的值
使用步骤:
1.在state中添加一个状态,作为表单元素的value值(控制表单元素值得来源)
2.给表单元素绑定change事件,将表单元素值设置为state的值(控制表单元素值的变化)

state = {txt:''}
<input type="text" value={this.state.txt} onChange={e => this.setState({txt: e.target.value})} />

多表单优化:给表单元素添加name属性,名称与state相同
步骤:
1.给表单元素添加name属性,名称与state相同
2.根据表单元素类型获取对应值
3.在change事件处理程序中通过[name]来修改对应state

//步骤1
<input type="text" name="txt" value={this.state.txt} onChange={this.handleForm} />

//步骤3
hendleForm = e => {
	//获取当前DOM对象
	const target = e.target
	//步骤2,根据表单元素类型获取值
	const value = target.type == 'checkbox' ? target.checked : target.value
	//获取name
	const name = target.name
	//根据name设置对应state
	this.setState({
		[name]: value
	})

}

非受控组件

借助ref,使用原生DOM方式获取表单元素值,ref作用是获取DOM或组件
使用步骤:
1.调用React.createrRef()方法创建一个ref对象
2.将创建好的ref对象添加到文本框
3.通过ref对象获取文本框的值

//步骤1
constructor() {
    super()
    this.txyRef = React.createRef()
}
//步骤2
<input type="text" ref={this.txtRef} />
//步骤3
console.log(this.txtRef.current.value)

组件传参

组件是封闭的,要通过props接收外部数据
传递数据:给组件标签添加属性
接收数据:函数组件通过参数props接收数据,类组件通过this.props接收数据

//函数组件
<Hello name="lz" age={20}/>

function Hello(props) {
	return <div>{props.name}</div>
}
//类组件
class Hello extends React.Component {
	render() {
		return <div>{this.props.age}</div>
	}
}

props可以给组件传递任意类型数据(基本和复杂数据类型,html标签都可以),它是只读对象
使用类组件时,如果写了构造函数,应该将props传递给super(),否则无法在构造函数中获取到

class Hello extends React.Component {
	// 初始化类组件的 state的旧版语法
	constructor(props) {
		//推荐将props传递给父类构造函数
		super(props)
	}
	render() {
		return <div>{this.props.age}</div>
	}
}

组件通讯

父传子

1.父组件提供传递的state数据
2.给子组件标签添加属性,值为state中数据
3.子组件中通过props接收父组件中传递数据

//父组件
class Parent extends React.Component {
	state = { lastName: 'lz'}
	render() {
		return (<div><Child name={this.state.lastName} /></div>)
	}
}
//子组件
function Child(props) {
	return <div>{props.name}</div>
}

子传父

利用回调函数,父组件提供回调函数,子组件调用,将要传递数据作为回调函数的参数
1.父组件提供一个回调函数(用于接收数据)
2.将该函数作为属性的值,传递给子组件
3.子组件通过props调用回调函数
4.将子组件数据作为参数传递给回调函数

//父组件
class Parent extends React.Component {
	//提供回调函数,用来接收数据
	getChildMsg = (msg) => {}
	render() {
		return (<div><Child getMsg={this.getChildMsg}/></div>)
	}
}
//子组件
class Child extends React.Component {
	state = {childMsg: 'react'}
	handleClick = () => {
		this.props.getMsg(this.state.childMsg)
	}
	render() {
		return (<div onClick={this.handleClick}></div>)
	}
}

兄弟组件

将共享状态提升到最近的公共父组件中,由公共父组件管理这个状态
公共父组件职责:提供共享状态,提供操纵共享状态的方法
要通讯子组件只需通过props接收状态或操作状态的方法

Context

嵌套多层组件首尾传递数据使用Context,可以跨组件传递数据
使用步骤:
1.调用React.createContext()创建Provider(提供数据)和Consumer(消费数据)两个组件
2.使用Provider组件作为父节点
3.设置value属性,表示要传递数据
4.调用Consumer组件接收数据

//步骤1
const {Provider, Consumer} = React.createContext()
//步骤2
<Provider>最上层父组件中的子组件</Provider>
//步骤3
<Provider value="lz">
//步骤4
<Consumer>{data => <div>{data}</div>}</Consumer>

props深入

children属性

组件标签有子节点时,props就有该属性
children属性和普通props一样可以是任意值(文本、react元素、组件、函数)

校验

组件使用者传入数据,创建者不知道格式会传入错误会导致组件内部报错,且组件使用者不知确切错误原因
创建者创建组件时指定props类型、格式,报错时也能给出明确错误提示
使用步骤:
1.安装包prop-typesyarn add prop-types/npm i props-types
2.导入prop-types包
3.使用组件名.propTypes = {}来给组件props添加校验规则
4.校验规则通过PropTypes对象来指定

import PropTypes from 'prop-types'
function App(props) {
	return (<div>{props.colors}</div>)
}
App.propTypes = {
	//约定colors属性为array类型,类型不对则报出明确错误,以便分析错误原因
	colors: PropTypes.array
}
约束规则

1.常见类型:array、bool、function、number、object、string
2.react元素类型:element
3.必填项:isRequired
4.特定结构对象:shape({})

//常见类型
optionalFunc: PropTypes.func,
//必选
requiredFunc: PropTypes.func.isRequired,
//特定结构对象
optionalObjectWithShape: PropTypes.shape({
	color: PropTypes.string,
	fontSize: PropTypes.number
})
props默认值

设置后组件不传入值会显示默认值,设置后以设置值为准

//设置默认值
App.defaultProps = {
	pageSize: 10
}

组件生命周期

只有类组件有生命周期

创建时

三个钩子函数,执行顺序为:constructor() > render() > componentDidMount()
钩子函数 | 触发时机 | 作用

  • | - | -
    constructor | 创建组件是最先执行 | 初始化state,为事件处理程序绑定this
    render | 每次组件渲染都会触发 | 渲染UI(不能调用setState(),否则会报错)
    componentDidMount | 创建挂载(完成DOM渲染)后 | 发送网络请求,DOM操作

更新时

执行时机:setState()foreUpdate()和组件接收到新props
执行顺序:render() > componentDidUpdate()
钩子函数 | 触发时机 | 作用

  • | - | - |
    render | 每次组件渲染都会触发 | 渲染UI
    componentDidUpdate | 组件更新(完成DOM渲染)后 | 发送网络请求、DOM操作(如果是setState()则必须放在一个if条件中) |

卸载时

组件从页面消失时执行
钩子函数 | 触发时机 | 作用

  • | - | -
    componentWillUnmount | 创建卸载(从页面消失) | 执行清理工作(如清除定时器)

render-props和高阶组件

react组件复用

功能相似的多个组件复用,复用的是state和操作state的方法
两种方式:render props模式和高阶组件(hoc)两种方式不是新的api,只是演化的固定写法

render props模式

使用组件时添加一个值为函数的prop,通过函数参数获取(需在组件内实现)
使用该函数返回值作为要渲染的UI内容(需在组件内实现)
<Mouse render={(mouse) => {要渲染的UI内容}}/>

使用步骤

1.创建Mouse组件,在组件中中提供复用的状态逻辑代码(状态和操作状态的方法)
2.将要复用的状态作为props.render(srate)方法的参数暴露到组件外
3.使用props.render()的返回值作为要渲染的内容

class Mouse extends React.Component {
	//省略state和操作state的方法
	render() {
		return this.props.render(this.state)
	}
}
<Mouse render={(mouse) => <p>渲染内容</p>} />

children代替render

<Mouse>{(mouse) => <p>渲染内容</p>}</Mouse>
//组件内部
this.props.children(this.state)

代码优化

1.给render props模式添加props校验
2.组件卸载时解除mousemove事件绑定

//添加校验
Mouse.propTYpes = {
	chidlren: PropTypes.func.isRequired
}
//卸载时事件
componentWillUnmount() {
	window.removeEnentListener('mousemove', this,handleMouseMove)
}

高阶组件

高阶组件是一个函数,接收要包装组件,返回增强后组件
高阶组件内部创建一个类组件,在类组件中提供复用的状态逻辑代码,同过prop将复用的状态传递给被包装组件WrappedComponent

const EnhancedComponent = withHOC(WrappedComponent)
//高阶组件内部创建的类组件
class Mouse extends React.Component {
	//省略state和操作state的方法
	render() {
		return <WrappedComponent {...this.state} />
	}
}

使用步骤

1.创建一个函数,名称约定with开头
2.指定函数参数,参数大写字母开头(作为要渲染的组件)
3.函数内部创建一个类组件,提供复用的状态逻辑代码并返回
4.该组件中,渲染参数组件,同时将状态通过prop传递给参数组件
5.调用该高阶组件,传入要增强组件,通过返回值拿到增强后组件,并将其渲染到页面中

function withMouse(WrappedComponent) {
    class Mouse extends React.Component {
        //省略state和操作state的方法
        render() {
        	//Mouse组件的render方法中
            return <WrappedComponent {...this.state} />
        }
    }
    return Mouse
}
//创建组件
const MousePosition = withMouse(Position)
//渲染组件
<MousePosition />

设置displayName

使用多个高阶组件因为类组件名字相同所以组件名称相同,为了区分组件为高阶组件设置displayName便于调试时区分不同组件

//在高阶组件函数内,类组件后,return前,WrappedComponent为高阶组件函数形参
Mouse.displayName = `WithMouse${getDisplayName(WrappedComponent)}`
function getDisplayName(WrappedComponent) {
	renter WrappedComponent.displayName || WrappedComponent.name || 'Component'
}

传递props

高阶组件渲染组件时添加props,但是渲染到高阶组件类组件中没有把值传递到类组件中被包装组件,想要解决需要在类组件中渲染WrappedComponent时将state和this.props一起传递给组件
<WrappedComponent {...this.state} {...this.props} />

setState说明

更新数据

setState()是异步更新数据,使用时不要前后依赖,多次调用只会触发一次渲染

推荐语法

使用setState((state, props) => {})语法,state表示最新state,props表示最新props

this.setState((state, props) => {
	return {
		count: state.count + 1
	}
})

第二个参数

在状态更新(页面重新渲染)后立即执行某个操作

this.setState(
	(state, props) => {},
	() => {此回调函数在状态更新后执行},
})

组件更新机制

setState()的两个作用:修改state,更新组件(UI)
父组件重新渲染时会重新渲染子组件,但只渲染当前组件及所有子组件

组件性能优化

减轻state

state只存储与组件渲染相关数据(count/列表数据/loading等)
不用做渲染的但是需要在多个方法中用到的数据应该放到this中

class Mouse extends React.Component {
    componentDidMount() {
    	//timerId存储到this中
    	this.timerId = setInterval(() => {}, 3000)
    }
    componentWillUnmount() {
    	clearInterval(this.trmerId)
    }
    render() {}
}

避免不必要渲染

组件更新机制中父组件更新会带动子组件更新
使用钩子函数shouldComponentUpdate(nextProps, nextState)通过返回值是true或者false决定组件是否渲染,nextProps和nextState表示最新内容,更新阶段钩子函数在组件重新渲染前执行(shouldComponentUpdate > render)

class Mouse extends React.Component {
    shouldComponentUpdate() {
        //使用条件判断,决定是否重新渲染
    	return false
    }
    render() {}
}

纯组件

React.PureComponent与React.Component功能相似,React.PureComponent内部自动实现了shouldComponentUpdate钩子,纯组件内部分别对比前后两次props和state值来决定是否重新渲染组件

class Mouse extends React.PureComponent {
    render() {
        return (<div>纯组件</div>)
    }
}

纯组件内部对比是shallow compare(浅层对比),普通数据类型可以直接使用,state或props是引用数据类型时应该创建新数据,不能修改原始对象中属性值

虚拟DOM和Diff算法

虚拟DOM:本质上是一个描述UI的js对象
执行过程:

初次渲染时,react根据初始state创建一个虚拟DOM对象(树)
根据虚拟DOM生成真正DOM,渲染到页面中
数据变化后重新根据新数据创建新的虚拟DOM对象(树)
前后虚拟DOM对象根据Diff算法对比,得到需要更新内容
react只将变化内容更新到DOM中,重新渲染到页面

组件render()调用后,根据状态和JSX结构生成虚拟DOM对象
虚拟DOM的最大意义是脱离了浏览器环境束缚,能运行js的地方就能运行react可以跨平台

路由

概述

前端路由是一套映射规则,在React中式URL路径与组件的对应关系

使用

1.安装:yarn add react-router-dom
2.导入路由三个核心组件:import { BrowserRouter as Router, Router, Link } from 'react-router-dom'
3.使用Router组件包裹整个应用:<Router>根组件</Router>(一个react应用只需要使用一次)
4.使用Link组件作为导航菜单(路由入口):<Link to="/路径">菜单内容</Link>
5.使用Route组件配置路由规则和要展示组件(路由出口):<Route exact path="/路径" component={组件名}></Route>(该组件指定路由展示组件相关信息,path属性:路由规则,component属性:展示的组件,exact属性用于精准匹配路由,react默认模糊匹配)

常用组件说明

Router组件:包裹整个应用,一个应用使用一次
两种Router:BrowserRouter使用H5的histtoryAPI实现不带#更加推荐使用,HashRouter使用URL的哈希值实现带#
Link组件:用于指定导航链接(a标签)(to属性:浏览器地址栏中的pathname(location.pathname))
Route组件:指定路由展示组件相关信息(path属性:路由规则,component属性:展示的组件,exact属性用于精准匹配路由,react默认模糊匹配)

路由执行过程

1.点击Link标签(a标签),修改浏览器地址栏中的url
2.React路由监听到地址栏url变化
3.React路由内部遍历所有Route组件,使用路由规则(path)与pathname进行匹配
4.路由规则(path)能够匹配地址栏pathname时,就展示该Route组件内容

编程式导航

编程式导航:通过js代码实现页面跳转
history是react路由提供,用于获取浏览器历史记录的相关信息
push(path):跳转到某个页面,参数path表示要跳转的路径
go(n):前进或后退到某个页面,参数n表示前进或后退页面数量
this.props.history.push('/路径')
this.props.history.go(1/-1)

默认路由

默认路由:进入页面时就会展示的页面
<Route path="/" component={组件名}></Route>
路由重定向:Redirect组件用于实现路由重定向,to指定跳转到的路由地址(Redirect添加到Route导入中)
<Route exact path="/" render={() => <Redirect to="/home" />} />

匹配模式

模糊匹配模式

React路由默认是模糊匹配模式
模糊匹配规则:pathname以path开头就会匹配成功

path(代表Route组件的path属性)pathname(代表Link组件的to属性)
/所有pathname
/first/first或/first/a

精确匹配模式(推荐)

精确匹配模式:给Route组件添加exact属性
精确匹配:path和pathname完全匹配时才会展示该路由
<Route exact path="/路径" component={组件名}></Route>

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值