前言
现在找工作市场上Vue和react基本上是55开,但在一线城市大部分以react技术栈开发为主,只会vue是远远不够的,这迫使我们不得不学,此篇是学习后整理出来的,算不上是文章,只能说是学习完后整理的笔记,使用通过易懂的文字加上案例代码,让小白学习起来更轻松,希望对刚学习react的小白有所帮助,ps:如有错误,请大佬评论指正
react是什么?
react是一个用于构建用户界面的 JavaScript 库,具有声明式,组件化的特点
react的基础安装
在使用react之前需要引入react和react-dom两个插件,react用于提供常见元素,组件等功能,react-dom是用于操作dom相关的功能
CDN引入
开发环境:
<script crossorigin src="https://unpkg.com/react@16/umd/react.development.js"></script>
<script crossorigin src="https://unpkg.com/react-dom@16/umd/react-dom.development.js"></script>
生产环境
<script crossorigin src="https://unpkg.com/react@16/umd/react.production.min.js"></script>
<script crossorigin src="https://unpkg.com/react-dom@16/umd/react-dom.production.min.js"></script>
npm下载
npm i react react-dom
元素创建&渲染
// 1.引入CDN
<script crossorigin src="https://unpkg.com/react@16/umd/react.production.min.js"></script>
<script crossorigin src="https://unpkg.com/react-dom@16/umd/react-dom.production.min.js"></script>
<div id="root"></div>
// 2.利用React.createElement来进行创建元素 参数一:元素名称 参数二:元素属性 参数三:元素子节点
const helloReact = React.createElement('h1', null, 'hello React')
// 3.利用ReactDOM.render来渲染元素 参数一:要渲染的元素 参数二:挂载点
ReactDOM.render(helloReact, document.getElementById('root'))
脚手架的安装与使用
react脚手架可以帮我们生成一个通用的目录结构,并且已经将我们所需要的工程环境配置好,创建命令如下
安装
npm init create-app my-reactyarn create react-app my-reactnpx create-react-app my-app(npm 5.2+才可使用) 使用使用方法同上一样,只是导入的方式不一样
import React from 'react';
import ReactDOM from 'react-dom';
const helloReact = React.createElement('h1', null, 'hello React')
ReactDOM.render(helloReact, document.getElementById('root'))
JSX
JSX是一种JavaScript的语法扩展,可以让我们高效的开发,使用以下代码与上面创建元素的代码效果是等同的,我们将JSX代码放入react中,实际上是经过bable编译之后再放入浏览器上运行的,编译之后的代码其实和上面写的代码一样,尤其是编写树状结构的时候,可以一眼看出,使用JSX来书写更加简洁,结构清晰,更加高效!
注意点:属性名使用驼峰命名
- class--->className
- for--->htmlFor
- tabindex ---> tabIndex 元素创建&渲染
const helloJSX = (<h1 className="JSX">hello JSX</h1>)
ReactDOM.render(helloJSX,document.getElementById('root'))
条件渲染
if/else
const loading = false
const loadData = () => {
if (loading) {
return (<div>数据加载中...</div>)
}
return (<div>数据加载完成...</div>)
}
ReactDOM.render(loadData(),document.getElementById('root'))
三元表达式
const loading = true
const loadData = () => {
return loading && (<div>数据加载中...</div> )
}
ReactDOM.render(loadData(), document.getElementById('root'))
与运算符
// 与运算符
const loading = true
const loadData = () => {
return loading && (<div>数据加载中...</div> )
}
ReactDOM.render(loadData(), document.getElementById('root'))
列表渲染
const nameList = [{ id:1, name: '小明', age:4 },{ id:2, name: '小刚', age:18 },{ id:3, name: '小红', age:6 }]
const list = (
<ul>
{nameList.map(item => <li key={item.id}>{ item.name}</li>)}
</ul>
)
ReactDOM.render(list, document.getElementById('root')
组件&事件绑定
组件分为有状态组件和无状态组件,存放数据的state则为状态,在react v16.8.0之前,函数组件是没有状态的,在react v16.8.0之后有了hook才有状态,一般函数组件只负责数据展示,类组件实现数据与页面动态更新。
函数组件
// 创建函数组件
function FirstComponent (){
return (<div>函数组件</div>)
}
ReactDOM.render(<FirstComponent />, document.getElementById('root'))
类组件
// 创建类组件
class FirstComponent extends React.Component{
render(){
return (<div>类组件</div>)
}
}
ReactDOM.render(<FirstComponent />, document.getElementById('root'))
组件事件绑定
class TitleClick extends React.Component{
handerClick (e) {
// 利用事件对象阻止跳转
e.preventDefault()
console.log(123);
}
render () {
// 添加点击事件onClick
return (<a href='https://www.baidu.com/' onClick={this.handerClick}>点击我</a>)
}
}
state&props
在组件中使用state来定义组件状态,通过this.setState()来修改组件状态,state只能在组件内部使用。如果在子组件中使用负组件的数据则需要使用props接收使用,在使用props的时候需要注意:props是只读对象,无法修改,如果在类组件中手动写了constructor时,需要将props传递给super(),不然无法获取props
class Counter extends React.Component{
// 1.定义组件状态
state = {
num:1
}
render () {
return (
<div>
<div>计数器:{this.state.num}</div>
<button onClick={() => {
// 2.修改组件状态
this.setState({
num:this.state.num + 1
})
}}>+1</button>
</div>
)
}
}
组件传值
父组件--->子组件
import { Component } from "react";
export class Father extends Component {
state = {
fatherVlaue: '父组件数据'
}
render () {
return (<div><Son {...this.state} /></div>)
}
}
class Son extends Component {
render () {
return (<div>{this.props.fatherVlaue}</div>)
}
}
子组件--->父组件
import { Component } from "react";
export class Father extends Component {
state = {
value: '父组件数据'
}
fatherHandeValue = (value) => {
this.setState({ value })
}
render () {
return (<div>{this.state.value}<Son sonHandeValue={this.fatherHandeValue} /></div>)
}
}
class Son extends Component {
state = {
sonValue: '子组件数据'
}
sonHandeValue = () => {
this.props.sonHandeValue(this.state.sonValue)
}
render () {
return (<div><button onClick={this.sonHandeValue}>点击传值</button></div>)
}
}
兄弟传值
import { Component } from 'react'
export class Father extends Component {
state = {
value: ''
}
fatherHandeValue = (value) => {
this.setState({ value })
}
render () {
return (<div>
<Children1 sonHandeValue={this.fatherHandeValue} />
<Children2 value={this.state.value} />
</div>)
}
}
class Children1 extends Component {
state = {
value: 'Children1传递的数据'
}
sonHandeValue = () => {
this.props.sonHandeValue(this.state.value)
}
render () {
return (<div><button onClick={this.sonHandeValue}>1--->2 传值</button></div>)
}
}
class Children2 extends Component {
render () {
return (<div>组件二:{this.props.value}</div>)
}
}
props校验
在props传递中,对于组件来说,无法保证接收到的props时什么格式的数据,react为了保证数据类型的准确性,推荐我们使用prop-types来进行校验props类型,也可以添加props默认值
- 下载
prop-type插件
npm install --save prop-types
2.在项目中使用,以下代码列出了常用的校验类型,详情配置可看官方文档
import { Component } from "react";
// 1. 导入prop-types
import PropTypes from 'prop-types';
export class Father extends Component {
state = {
str: '',
arr: [],
bool: true,
num: 0,
obj: {},
func: () => { }
}
render () {
return (<div><TypeProps {...this.state} /></div>)
}
}
class TypeProps extends Component {
render () {
return (<div>TypeProps{this.props.defaultvalue}</div>)
}
}
/**
* TypeProps:需要添加校验的组件
* propTypes:校验类型
*/
TypeProps.propTypes = {
str: PropTypes.string.isRequired, // 校验字符串并且必填
arr: PropTypes.array, // 校验数组
bool: PropTypes.bool, // 校验布尔值
func: PropTypes.func, // 校验函数
num: PropTypes.number, // 校验数值
obj: PropTypes.object, // 校验对象
}
/**
* TypeProps:需要添加校验的组件
* defaultProps:默认值
*/
TypeProps.defaultProps = {
defaultvalue: '默认值'
}
受控组件
受控组件一般是HTML中可改变自己状态的元素,一般有表单类的元素,因为这些元素自身可改变状态,所以需要让React中的state保持同步,使用this.setstate来改变state的状态,当我们给表单绑定value的时候,必须要给表单同步绑定change事件,不然则会有如下报错:
import { Component } from "react";
class ControlledInput extends Component {
state = {
value: "你好,React",
};
changeValue = (e) => {
const { value } = e.target;
this.setState({
value,
});
};
render() {
return (
<div>
<h1>表单受控组件</h1>
<input
type="text"
value={this.state.value}
onChange={this.changeValue}
/>
</div>
);
}
}
export default ControlledInput;
ref
使用createRef来创建ref对象,ref可以用来获取dom对象或者是组件,使用current来指定dom
import { Component, createRef } from "react";
class MyRef extends Component {
constructor() {
super();
// 创建ref对象
this.divRef = createRef();
}
handleValue() {
// 使用current来指定dom
this.divRef.current.innerHTML = "更改后的值";
}
render() {
return (
<div>
{/* 绑定ref */}
<span ref={this.divRef}>你好,ref</span>
<button onClick={() => this.handleValue()}>点击更改文字</button>
</div>
);
}
}
export default MyRef;
Context
当我们需要组件多层传递的时候,一层一层的传递容易造成数据丢失或者是混淆,书写起来也比较麻烦,这时候就可以用到Context,在父级组件中提供数据,子孙组件中引用数据即可,类似于Vue里面的依赖注入provide和inject
- 从react中导入createContext()
- 从createContext()里面结构出Provider和Consumer
- 用Provider包裹提供数据的父组件,使用vlaue属性传递需要提供的数据
- 用Consuerm包裹使用数据的子组件
import "./03-context.css";
import { Component, createContext } from "react";
const { Provider, Consumer } = createContext();
//提供数据的父组件
class Node extends Component {
state = {
value: "提供给子组件的值",
};
render() {
return (
<Provider value={this.state.value}>
<div className="Node">
Node:
<SubNode />
</div>
</Provider>
);
}
}
class SubNode extends Component {
render() {
return (
<div className="SubNode">
SubNode:
<Child />
</div>
);
}
}
class Child extends Component {
render() {
return (
<div className="Child">
Child:<Consumer>{(value) => <span>{value}</span>}</Consumer>
</div>
);
}
}
export default Node;
生命周期
每个组件都有自己的生命周期,简单来说就是一个组件在挂载到销毁的过程,就算是一个生命周期
- 创建时(挂载阶段):constructor ---> render ---> componentDidMount
- 更新时(更新阶段):render ---> componentDidUpdate
- 卸载时(卸载阶段):componentWillUnmount
在componentDidUpdate中使用setState时需要退出条件,不然会导致死循环
| 钩子函数 | 触发时机 | 作用 |
|---|---|---|
| constructor | 创建组件时,最先执行 | 初始化state |
| render | 组件渲染时触发 | 渲染视图在render中不能调用setState() |
| componentDidMount | 组件完成挂载后 | 实现DOM操作,发送请求 |
| componentDidUpdate | 组件更新,完成DOM渲染后 | 实现DOM操作,发送请求 |
| componentWillUnmount | 组件卸载 | 清除定时器等操作 |
| forceUpdate | 调用forceUpdate()时触发 | 强制更新 |
组件复用
当我们在实现业务的时候经常会遇到实现的逻辑时一样的,但是渲染的ui不同,如果重复复制组件代码进行实现的话,那代码中重复的代码太多,以后就不方便维护,这时候react给我们推出了render props模式和高阶组件来进行代码的复用,下面分别使用了render props模式和高阶组件来复用组件
render prop
render props指一种在 React 组件之间使用一个值为函数的 prop 共享代码的简单技术,具有 render prop 的组件接受一个返回 React 元素的函数,并在组件内部通过调用此函数来实现自己的渲染逻辑,当然官网上说明了render prop 是因为模式才被称为 render prop,任何*被用于告知组件需要渲染什么内容的函数 prop 在技术上都可以被称为 render prop,也可以使用其他方法名代替,推荐使用children
跟官网上的案例大致相同,跟随鼠标的凹凸曼打🐤,如果想看使用children模式的可以点击官网查看,只需要将render换成children,接收是放在组件内部渲染就行。
import { Component } from "react";
export class RenderProp extends Component {
render () {
return (
<div>
<Mouse render={data => (<div>x:{data.x} y:{data.y}</div>)} />
<Mouse render={data => (<img src="https://img0.baidu.com/it/u=2311657326,1601596104&fm=253&fmt=auto&app=138&f=JPEG?w=500&h=501" alt="" style={{
position: 'absolute',
top: data.y,
left: data.x,
width: 200,
height: 200,
transform: 'translate(-50%,-50%)'
}}></img>)
} />
</div >
)
}
}
class Mouse extends Component {
state = {
x: 0,
y: 0
}
// 设置鼠标移动值
handleMouse = (e) => {
this.setState({
x: e.clientX,
y: e.clientY
})
}
// 绑定鼠标移动事件
componentDidMount () {
window.addEventListener('mousemove', this.handleMouse)
}
// 解绑鼠标移动事件
componentWillUnmount () {
window.removeEventListener('mousemove', this.handleMouse)
}
render () {
// 这里return 的 render 组件使用的时候必须有个render方法,当然不一定非要叫render,方法名由自己定义,接收时保持一致就行
return this.props.render(this.state)
}
}
高阶组件
import { Component } from "react";
// 暴露的组件
export class HigtComponent extends Component {
render () {
return (<div>
<MousePosition />
<MouseCat />
</div>)
}
}
// 高阶函数
function WithMouse (MousePosition) {
class Mouse extends Component {
state = {
x: 0,
y: 0
}
handerMouse = (e) => {
this.setState({
x: e.clientX,
y: e.clientY
})
}
componentDidMount () {
window.addEventListener('mousemove', this.handerMouse)
}
componentWillUnmount () {
window.removeEventListener('mousemove', this.handerMouse)
}
render () {
return (<MousePosition {...this.state} />)
}
}
return Mouse
}
// 提供视图的组件
const Position = props => {
return (<div>x:{props.x} y:{props.y}</div>)
}
const Cat = props => {
return (<img src="https://img0.baidu.com/it/u=2311657326,1601596104&fm=253&fmt=auto&app=138&f=JPEG?w=500&h=501" alt="" style={{
position: 'absolute',
top: props.y,
left: props.x,
width: 200,
height: 200,
transform: 'translate(-50%,-50%)'
}}></img>)
}
// 用高阶组件进行封装
const MousePosition = WithMouse(Position)
const MouseCat = WithMouse(Cat)