React学习笔记(基础)
本文主要依据react官网的学习脉络,记录自己的学习过程,同时添加了个人见解,如有理解不妥的地方欢迎评论
1.React介绍
react由facebook公司推出,因为该公司不满足于市面上现有的前端框架,他们认为MVC
只会让前端越来越复杂,因此react就诞生了,react只关注与视图层,官方对react的定位是:一个快速构建前端视图的javaScript库。
2.React的特点
-
虚拟DOM
如果没有虚拟DOM,每次DOM改变的时候,都需要在浏览器中进行渲染。每一次DOM改变的时候,浏览器都需要重新计算CSS,进行布局处理,然后重新渲染页面,这都需要时间。而虚拟DOM完成的事情是最小化DOM改变,然后批处理DOM变化,在必要的时候才重新渲染页面。
例如:当前页面显示的
张三
李四
,当我添加王五
时,DOM会重新渲染3个标签,虚拟DOM则可以直接在后面加上新增加的王五
-
组件化
-
什么是组件化
return <>
<Modal visible={modalVisible} title={editId ? `修改` : `新增`} onCancel={handleModalClose}
onOk={handleSubmit}>
<RfidTagForm ref={formRef} editId={editId}/>
</Modal>
</>
......
const RfidTagForm = forwardRef<RfidTagFormHandle,RfidTagFormProps>(({editId}, componentRef) => {
const [form] = Form.useForm()
const { resetFields, setFieldsValue, validateFields } = form;
......
return <Form form={form} layout={'vertical'}>
<Form.Item label={`罐号`} name={`potNum`}>
<Input/>
</Form.Item>
<Form.Item label={`标签卡ID`}>
<Row gutter={8}>
<Col span={12}>
<Form.Item name={`tagId`}>
<Input/>
</Form.Item>
</Col>
<Col span={12}>
<Button type={"primary"} onClick={handleReadTag}>读取卡号</Button>
</Col>
</Row>
</Form.Item>
</Form>
})
export default RfidTagForm
代码中RfidTagForm即为组件,它接受任意的入参(即 “props”),并返回用于描述页面展示内容的 React 元素。
-
组件化有两个显著的特点:
封装:一个组件所需的数据封装于组件内部;
组合:一个组件可以与其他组件通过组合的方式实现更加复杂的业务逻辑;
-
声明式代码
声明式代码指的是:让开发者按照“我要做什么”,而不是“我要让计算机做什么”去思考如何实现业务需求。
以感受到天气太热,编写代码为例:
-
纯粹的JavaScript语法
在 React 中,没有任何特殊的专有的 React 语法需要理解和记忆,所有的组件,数据操作,业务逻辑都是通过使用 JavaScript 语法实现的。
这意味着你想要使用 React 只需理解 React 的思想和几个关键的 API 就可以立即开始使用 React 进行复杂应用的开发。并且 React 还鼓励你使用函数式编程思想进行开发,你可以在 React 开发中,使用你的任何函数式编程技巧。
3.环境搭建
比较简单,网上有很多。
React环境搭建链接
这里建议在github上下载一个nvm,node的版本管理工具,亲测好用。
4.核心概念
4.1JSX
个人理解:用js写html的内容,其格式比较像是html语言,但事实上是编译后会变成js函数被调用
示例:
function formatName(user) {
return user.firstName + ' ' + user.lastName;
}
const user = {
firstName: 'Harper',
lastName: 'Perez'
};
const element = (
<h1> Hello, {formatName(user)}! </h1>
);
ReactDOM.render(
element,
document.getElementById('root')
);
Babel 会把 JSX 转译成一个名为 React.createElement()
函数调用。
以下三种代码完全相同
const element = (
<h1 className="greeting">
Hello, world!
</h1>
);
const element = React.createElement(
'h1',
{className: 'greeting'},
'Hello, world!'
);
const element = {
type: 'h1',
props: {
className: 'greeting',
children: 'Hello, world!'
}
};
4.2 元素&组件
元素是构成 React 应用的最小砖块,它描述了你在屏幕上想看到的内容,与浏览器的 DOM 元素不同,React 元素是创建开销极小的普通对象。React DOM 会负责更新 DOM 来与 React 元素保持一致。
示例:
const element = <h1>Hello, world</h1>;
此处区分一下元素和组件:
元素:
const element = React.createElement(
'h1',
{className: 'greeting'},
'Hello, world!'
);
组件分为两种:函数式组件&&class组件
函数式:const Element= () => React.createElement(...)
class组件:
import React from 'react'
import ReactDOM from 'react-dom'
class App extends React.Component {
render () {
return (
// 注意这里得用this.props.name, 必须用this.props
<h1>欢迎进入{this.props.name}的世界</h1>
)
}
}
export default App;
无论是函数式组件还是class组件,目的都是为了返回一个元素.由此可以看出元素和组件的区别。
组件可以接收任何入参,即Props.例如 我们刚刚定义的App组件做里使用到了this.props.name,那么我们在使用此组件的时候就可以给他传一个 name参数供其使用.具体如下:
import React from 'react';
import ReactDOM from 'react-dom';
import './index.css';
import App from './App';
import reportWebVitals from './reportWebVitals';
ReactDOM.render(
<React.StrictMode>
<App name="Sara" />
</React.StrictMode>,
document.getElementById('root')
);
注意:为了让react可以区分dom原有标签和自己定义的组件,在定义组件时首字母要大写.
Props具有只读性,在组件中对拿到的props不可进行修改操作,
正确:
function sum(a, b) { return a + b;}
错误:
function withdraw(account, amount) { account.total -= amount;}
4.3 State&生命周期
首先介绍State,state与props类似,不过state是私有的,只在当前组件使用,与props用于传参不同
每次组件更新时 render
方法都会被调用,但只要在相同的 DOM 节点中渲染 <Clock />
,就仅有一个 Clock
组件的 class 实例被创建使用。这就使得我们可以使用如 state 或生命周期方法等很多其他特性。(啥意思)
挂载&卸载
当 Clock
组件第一次被渲染到 DOM 中的时候,就为其设置一个计时器。这在 React 中被称为“挂载(mount)”。
同时,当 DOM 中 Clock
组件被删除的时候,应该清除计时器。这在 React 中被称为“卸载(unmount)”。
生命周期函数:
-
componentDidMount()
方法会在组件已经被渲染到 DOM 中后运行,组件挂载完成时触发的函数
- 组件将要挂载时触发的函数:componentWillMount
- 是否要更新数据时触发的函数:shouldComponentUpdate
- 将要更新数据时触发的函数:componentWillUpdate
- 数据更新完成时触发的函数:componentDidUpdate
- 组件将要销毁时触发的函数:componentWillUnmount
- 父组件中改变了props传值时触发的函数:componentWillReceiveProps
正确地使用state
不要直接修改 State
例如,此代码不会重新渲染组件:
// Wrongthis.state.comment = 'Hello';
而是应该使用 setState()
:
// Correctthis.setState({comment: 'Hello'});
4.4 事件处理
使用:
- React 事件的命名采用小驼峰式(camelCase),而不是纯小写。
- 使用 JSX 语法时你需要传入一个函数作为事件处理函数,而不是一个字符串。
react中如果需要使标签的默认事件不生效的方法:
import React, {Component} from 'react';
class Button extends React.Component{
doClick(e){
//阻止标签的默认事件
e.preventDefault();
alert("do click");
}
render(){
return (
<a href="https://www.baidu.com/" onClick={this.doClick}>
事件处理测试
</a>
)
}
}
export default Button
重点:
你必须谨慎对待 JSX 回调函数中的 this
,在 JavaScript 中,class 的方法默认不会绑定 this
。如果你忘记绑this.handleClick
并把它传入了 onClick
,当你调用这个函数的时候 this
的值为 undefined
。
关于为组件添加事件时,要注意this的绑定问题.(个人理解:组件在渲染时已经不在当前组件内,此时的this指向的不是我们所需要的组件),所以要加一个this绑定:
绑定方法一:
class Toggle extends React.Component {
constructor(props) {
super(props);
this.state = {isToggleOn: true};
// 为了在回调中使用 `this`,这个绑定是必不可少的
this.handleClick = this.handleClick.bind(this); }
handleClick() {
this.setState(state => ({
isToggleOn: !state.isToggleOn
}));
}
render() {
return (
<button onClick={this.handleClick}>
{this.state.isToggleOn ? 'ON' : 'OFF'}
</button>
);
}
}
export default Toggle
绑定方法二(和一类似):
import React, {Component} from 'react';
class Toggle extends React.Component {
constructor(props) {
super(props);
this.state = {
isToggeleOn: true
}
console.log("constructor()"+this)
// 把this绑定给handleclick事件
// this.handleClick = this.handleClick.bind(this);
}
handleClick(title) {
alert(title)//输出abc
console.log("handleClick()"+this)
this.setState(state => ({
isToggeleOn: !state.isToggeleOn
}))
}
render() {
return(
// 把this绑定给handleclick事件
<button onClick={this.handleClick.bind(this,"abc")}>
{this.state.isToggeleOn ? 'ON':'OFF'}
</button>
)
}
}
export default Toggle;
绑定方法三(ES6的箭头函数)
import React, {Component} from 'react';
class Toggle extends React.Component {
constructor(props) {
super(props);
this.state = {
isToggeleOn: true
}
console.log("constructor()"+this)
// 把this绑定给handleclick事件
// this.handleClick = this.handleClick.bind(this);
}
// es6的箭头函数自动绑定上下文
handleClick=()=> {
console.log("handleClick()"+this)
this.setState(state => ({
isToggeleOn: !state.isToggeleOn
}))
}
render() {
return(
// 把this绑定给handleclick事件
<button onClick={this.handleClick.bind(this)}>
{this.state.isToggeleOn ? 'ON':'OFF'}
</button>
)
}
}
export default Toggle;
向事件处理程序传递参数
<button onClick={(e) => this.deleteRow(id, e)}>Delete Row</button>
<button onClick={this.deleteRow.bind(this, id)}>Delete Row</button>
4.5 条件渲染
4.6 列表&Key
function NumberList(props) {
const numbers = props.numbers;
const listItems = numbers.map((number) =>
<li key={number.toString()}> {number}
</li>
);
return (
<ul>{listItems}</ul>
);
}
const numbers = [1, 2, 3, 4, 5];
ReactDOM.render(
<NumberList numbers={numbers} />,
document.getElementById('root')
);
如果 不定义key,会报警告:a key should be provided for list items
.
深入解析为什么 key 是必须的
4.7表单
相对于html表单元素定义了固有的参数而言,react组件可以使用state来起任何名字的参数进行操作,没什么可说的,照着官网示例操作即可.
官网表单示例
4.8 组合
4.9 包含关系
我们遇到组件引用组件时,可以使用特殊的 children
prop 来将他们的子组件传递到渲染结果中:
function FancyBorder(props) {
return (
<div className={'FancyBorder FancyBorder-' + props.color}>
{props.children}
</div>
);
}
这使得别的组件可以通过 JSX 嵌套,将任意组件作为子组件传递给它们。
function WelcomeDialog() {
return (
<FancyBorder color="blue">
<h1 className="Dialog-title">
Welcome
</h1>
<p className="Dialog-message">
Thank you for visiting our spacecraft!
</p>
</FancyBorder>
);
}
可以看出,我们把
…
…
两个元素的内容 以
{props.children}
为参数进行传递
4.10特例关系
实际就是我们对目标组件做一些个性化定制,在原组件的基础上产生一个个性化定制的新组件
function Dialog(props) {
return (
<FancyBorder color="blue">
<h1 className="Dialog-title">
{props.title}
</h1>
<p className="Dialog-message">
{props.message}
</p>
</FancyBorder>
);
}
function WelcomeDialog() {
return (
<Dialog
title="Welcome"
message="Thank you for visiting our spacecraft!" />
);
}
我们可以说 WelcomeDialog
是Dialog
的特殊实例.