一、简介
为什么需要React框架
和其他主流的前端框架一样,React框架也是为了构建大型的前端应用而生的。使用React框架开发应用可以避免操作大量dom操作,使用组件化方式管理应用,提升开发效率和代码可维护性。
React做了哪些工作
React提供组件来抽象界面交互单元,React会管理开发者开发的组件,根据组件中的数据计算实际要渲染的视图,并且当数据变化时候计算最小的改动,自动操作dom完成界面的变化。
当我们使用React开发web应用时候,我们在写什么
当我们使用React开发时候,我们就是在写React组件,组件的state即数据,组件的render方法返回的是视图,组件的声明周期钩子和事件的处理,是数据的变化逻辑。更准确地说是一颗组件树,根组件引用子组件,子组件可能还会引用子组件,形成一个组件树。组件树的结构和视图的盒子嵌套结构类似。
我们怎样使用React开发应用
将界面划分为组件
声明视图和数据的关系
实现数据的变化逻辑,即组件的实现
JSX
- JSX是一个 JavaScript 的语法扩展,用来描述视图结构
JSX中混合了标记语言和js语法
大括号中是js表达式
js表达式的值也可以是一个元素
JSX并不是必须,但是是React的最佳实践
二、框架基本使用
1. 脚手架的使用
https://create-react-app.dev/docs/getting-started/
npx create-react-app my-app
cd my-app
npm start
src/index.js中,将组件渲染到指定的元素(”#root”)上
import React from 'react';
import ReactDOM from 'react-dom';
import App from './App';
ReactDOM.render(
<React.StrictMode>
<App />
</React.StrictMode>,
document.getElementById('root')
);
在初始化文件src/index中可以看到引用了react和react-dom两个模块,
react模块负责react核心逻辑,react-dom负责dom操作,ReactDOM.render方法将组件挂在到指定的元素上。
src/App.js中是根组件。
2. 简单示例
import React from 'react';
import './App.css';
class App extends React.Component {
state = {
digit: 1
};
onCounterClick = () => {
this.setState({
digit: Math.random()
});
};
render() {
return (
<div onClick={this.onCounterClick}>digit:{this.state.digit}</div>
);
}
}
export default App;
声明一个React类组件,继承React.Component
该组件包含一个render方法,返回JSX(定义视图)
组件有个属性state,保存组件用到的数据
视图中标签上onClick属性注册点击回调,取值是类的一个成员方法。标签上的事件和原生的事件类似,注意是驼峰的。如onClick、onChange、onInput等
点击后通过”setState”改变状态,文本就相应地改变了
3. 视图
文本
在大括号中使用js表达式渲染文本
事件、class、style
import React from 'react';
import './App.css';
class App extends React.Component {
state = {
digit: '1'
};
onButtonClick = e => {
this.setState({
digit: e.target.dataset.digit
});
};
render() {
return (
<>
<div>{this.state.digit}</div>
<button
className="btn-2"
data-digit="1"
onClick={this.onButtonClick}
style={{fontSize: '16px', 'margin-right': 20}}
>
点击显示 1
</button>
<button
className="btn-2"
data-digit="2"
onClick={this.onButtonClick}
>
点击显示 2
</button>
</>
);
}
}
export default App;
事件回调的参数是e,这里的e并不是原生的e,而是经过React包装过的e,但是和原生的e类似,大部分场景都可以按照原生的使用方法访问这个对象
元素属性和原生类似
为了不和关键字class混淆,JSX中给元素的类名复制要用”className”
style的值是一个对象,key是css属性,value是该属性的取值,注意,key可以是驼峰的(fontSize)也可以是原生形式的(’font-size’),如果是非法格式的对象key,需要加引号;取值可以是字符串(”16px”)也可以是数字,如果是数字,默认单位是px
条件渲染
import React from 'react';
import './App.css';
class App extends React.Component {
state = {
digit: '1'
};
onButtonClick = e => {
this.setState({
digit: e.target.dataset.digit
});
};
render() {
const button = this.state.digit === '1'
? (
<button
data-digit="2"
onClick={this.onButtonClick}
>
点击显示 2
</button>
)
: (
<button
data-digit="1"
onClick={this.onButtonClick}
>
点击显示 1
</button>
)
return (
<div>
<div>{this.state.digit}</div>
{button}
</div>
);
}
}
export default App;
因为JSX是js的语法扩展,因此当需要根据条件决定是否渲染某个元素,或者根据条件决定渲染哪个元素时候,只需要使用if条件语句或者条件运算符创建元素即可。示例中根据state的digit数据判断显示按钮1还是按钮2。
也可以使用条件运算符
import React from 'react';
import './App.css';
class App extends React.Component {
state = {
isShow: false
};
onButtonClick = () => {
this.setState({
isShow: !this.state.isShow
});
};
render() {
return (
<div>
<div>
{
this.state.isShow && <span>hello, world</span>
}
</div>
<button
onClick={this.onButtonClick}
>
{
this.state.isShow ? '点击隐藏文本' : '点击显示文本'
}
</button>
</div>
);
}
}
export default App;
如果表达式返回false或者null,则React不会渲染任何元素
列表渲染
import React from 'react';
import './App.css';
class App extends React.Component {
state = {
userList: [
{id: '1', name: 'Sam'},
{id: '2', name: 'Ann'}
]
};
render() {
return (
<ul>
{
this.state.userList.map(({id, name}) => (
<li key={id}>{name}</li>
))
}
</ul>
);
}
}
export default App;
使用数组的map方法,返回一个item是JSX的数组,从而渲染这个列表,注意key属性的唯一性
表单和受控组件
背景:当使用需要用户输入内容的表单,例如input、radio等的时候,开发者需要
输入的内容同步到组件的state中
可以手动改变元素的内容。
受控组件的概念:这通过受控组件实现。所谓受控组件就是受开发者控制的表单元素。即开发者可以控制表单的取值。
具体实现:实现有两步:1. 将元素的value绑定到state,这样开发者控制state就可以控制表单的取值,因为用户输入内容后,开发者需要将其取值同步到state,因此还要绑定onChange事件,在onChange里面将改变后的value set到state中。当表单的value发生改变,会触发onChange,从而改变state。
示例:示例中在state中添加text属性,并将text绑定到input的value属性上,然后监听onChange,setState设置text。这样,input变化时候同步到state.text,就可以实时显示出来,并且还可以通过改变state来控制input的值得显示
import React from 'react';
import './App.css';
class App extends React.Component {
state = {
text: ''
};
onInputChange = e => {
this.setState({text: e.target.value});
};
onClearButtonClick = () => {
this.setState({text: ''});
};
render() {
return (
<div>
<div>{this.state.text}</div>
<input value={this.state.text} onChange={this.onInputChange} />
<button onClick={this.onClearButtonClick}>清空</button>
</div>
);
}
}
export default App;
4. 组件
React项目是由一个一个的组件组成的。
我们可以使用一个类继承React.Component实现一个组件,也可以使用函数式组件。
一个React组件包含几个部分:
state,props,方法(类方法和箭头函数方法),render属性,生命周期属性
子组件标签要大写,小写的会被解析成原生标签
1. 父子组件通信
父子组件通信主要有3种方式
props传递属性、props传递回调、父组件通过ref属性获取子组件的引用
import React from 'react';
import PropTypes from 'prop-types';
class Child extends React.Component {
name = 'child';
static propTypes = {
digit: PropTypes.number.isRequired
};
render() {
<div>
<span>digit:{this.props.digit}</span>
<button onClick={this.props.onButtonClick}>点击</button>
</div>
}
}
class App extends React.Component {
state = {
digit: 0
};
childNode = React.createRef();
onButtonClick = () => {
this.state({
digit: Math.random()
});
console.log(this.childNode.current.name);
};
render() {
return (
<div>
<Child
ref={this.childNode}
digit={this.state.digit}
onButtonClick={this.onButtonClick}
/>
</div>
);
}
}
export default App;
props 父组件给子组件传递,子组件接收,类型校验
父组件在JSX元素上通过属性传递给子组件,子组件通过this.props来访问父组件传递的属性
类型校验:propTypes属性和defaultProps属性
回调
回调其实是父组件通过props传递给子组件的一个函数,子组件可以在组件内部调用这个函数
ref 一般用在父组件直接调用子组件的方法的场景
父组件通过ref获取子组件的实例的引用,使用步骤如下:
- react.createRef
- 子组件元素设置ref属性
- 使用时候用current访问
2. 生命周期
生命周期钩子函数用来在组件从创建到销毁的某个阶段做一些逻辑。
常见的生命周期钩子函数有ComponentDidMount、ComponentWillUnmount等
通常在componentDidMount中处理开启定时器、注册监听器。在componentWillUnmount钩子中清除定时器、注销监听器。