React实用知识
wen 2021/8/2 React
# 1. 注意事项
- 状态(state)不可直接更改;状态必须通过setState进行更新,且更新是一种合并,不是替换。
- 只要执行setState(),即使不改变状态数据,组件也会重新render();优化:重写shouldComponentUpdate()方法,比较新旧state或props数据, 如果有变化才返回true, 如果没有返回false;使用PureComponent。
- 高阶函数:如果一个函数符合下面2个规范中的任何一个,那该函数就是高阶函数;若A函数,接收的参数是一个函数;若A函数,调用的返回值依然是一个函数。常见的高阶函数有:Promise、setTimeout、arr.map() 等等。
- 函数的柯里化:通过函数调用继续返回函数的方式,实现多次接收参数最后统一处理的函数编码形式。
- key是虚拟DOM对象的标识, 在更新显示时key起着极其重要的作用。
# 2. 概念
- 协调(reconciliation)是什么
当一个组件的 props 或 state 发生变化时,React 通过比较新返回的元素和之前渲染的元素来决定是否有必要进行实际的 DOM 更新。当它们不相等时,React 将更新 DOM。这个过程被称为 协调(reconciliation)。
- 如何用一个动态键名来设置状态
如果你使用 ES6 或 Babel 转码器来转换你的 JSX 代码,那么你可以用计算属性命名完成。
handleInputChange(event) {
this.setState({ [event.target.id]: event.target.value })
}
1
2
3
2
3
- 每次组件渲染时,函数被调用的常见错误是什么
你需要确保在传递函数作为参数时,没有调用该函数。
render() {
// 错误❌: handleClick 被调用而不是作为引用被传入
return <button onClick={this.handleClick()}>{'Click Me'}</button>
}
// 取而代之的是传递函数本身,不加圆括号。
render() {
// 正确:handleClick 是作为一个引用传递的!
return <button onClick={this.handleClick}>{'Click Me'}</button>
}
1
2
3
4
5
6
7
8
9
10
2
3
4
5
6
7
8
9
10
- 片段(fragments)是什么
这是 React 中常见的模式,用于一个组件返回多个元素。片段让你可以对一个 children 的列表进行分组,而无需在 DOM 中添加额外的节点。
render() {
return (
<React.Fragment>
<ChildA />
<ChildB />
<ChildC />
</React.Fragment>
)
}
// 这里还有一个短语法可以用,但是很多工具不支持
render() {
return (
<>
<ChildA />
<ChildB />
<ChildC />
</>
)
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
- 为什么片段(fragments)比 div 容器要好
片段的速度更快一些,并且由于没有创建额外的 DOM 节点而使用更少的内存。这只有在非常大和深的树上才会体现出真正的好处;一些 CSS 机制,如 Flexbox 和 CSS Grid 有一个特殊的父子关系,在中间添加 div 会使其难以保持所需的布局;DOM 检查器不那么杂乱。
- 什么是 React 中的传递门(Portal)
传递门是一种推荐的方式,可以将子节点渲染到父组件的 DOM 层次结构之外的 DOM 节点中。
ReactDOM.createPortal(child, container);
1
第一个参数是任何可渲染的 React children,比如一个元素、字符串或片段。第二个参数是一个 DOM 元素。
# 3. 记录学习到的代码
# 起步
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>hello_react</title>
</head>
<body>
<!-- 准备好一个“容器” -->
<div id="test"></div>
<!-- 引入react核心库 -->
<script type="text/javascript" src="../js/react.development.js"></script>
<!-- 引入react-dom,用于支持react操作DOM -->
<script type="text/javascript" src="../js/react-dom.development.js"></script>
<!-- 引入babel,用于将jsx转为js -->
<script type="text/javascript" src="../js/babel.min.js"></script>
<script type="text/babel" > /* 此处一定要写babel */
//1.创建虚拟DOM
const VDOM = <h1>Hello,React</h1> /* 此处一定不要写引号,因为不是字符串 */
//2.渲染虚拟DOM到页面
ReactDOM.render(VDOM,document.getElementById('test'))
</script>
</body>
</html>
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
# 关于虚拟 DOM
- 本质是Object类型的对象(一般对象)。
- 虚拟DOM比较“轻”,真实DOM比较“重”,因为虚拟DOM是React内部在用,无需真实DOM上那么多的属性。
- 虚拟DOM最终会被React转化为真实DOM,呈现在页面上。
# JSX 语法规则
- 定义虚拟 DOM 时,不要写引号。
- 标签中混入 JS 表达式时要用 {}。
- 样式的类名指定不要用 class,要用 className。
- 内联样式,要用 style = {
{key:value}
} 的形式去写。 - 只有一个根标签。
- 标签必须闭合。
# JS 语句与表达式的区别
- 表达式:一个表达式会产生一个值,可以放在任何一个需要值的地方。
- 语句(代码)。if(){} 、for(){}、switch(){case:xxxx}。
const data = ['Angular','React','Vue'];
<div>
<h1>前端js框架列表</h1>
<ul>
{
data.map((item,index)=>{
return <li key={index}>{item}</li>
})
}
</ul>
</div>
1
2
3
4
5
6
7
8
9
10
11
2
3
4
5
6
7
8
9
10
11
# 定义组件
// 函数式组件
function MyComponent(){
console.log(this); // undefined,因为babel编译后开启了严格模式
return <h2>我是用函数定义的组件(适用于【简单组件】的定义)</h2>
}
ReactDOM.render(<MyComponent />, document.getElementById('test'))
// 执行了ReactDOM.render(<MyComponent/>.......之后,发生了什么
// 1.React解析组件标签,找到了MyComponent组件。
// 2.发现组件是使用函数定义的,随后调用该函数,将返回的虚拟DOM转为真实DOM,随后呈现在页面中。
// 类式组件
class MyComponent extends React.Component {
constructor(props){
super(props);
this.state = { test: 'test' };
this.test = this.test.bind(this)
}
test() {
console.log('test');
}
// render是放在哪里的—— MyComponent的原型对象上,供实例使用。
// render中的this是谁—— MyComponent的实例对象 <=> MyComponent组件实例对象。
render(){
console.log('render中的this:',this);
return <h2>我是用类定义的组件(适用于【复杂组件】的定义)</h2>
}
// state 简写方式
state = { test: 'test' };
// 自定义方法--要用赋值语句的形式 + 箭头函数
test = () => {
this.setState({ test: 'otherTest'});
}
}
ReactDOM.render(<MyComponent/>,document.getElementById('test'))
// 执行了ReactDOM.render(<MyComponent />.......之后,发生了什么
// 1.React解析组件标签,找到了MyComponent组件。
// 2.发现组件是使用类定义的,随后new出来该类的实例,并通过该实例调用到原型上的render方法。
// 3.将render返回的虚拟DOM转为真实DOM,随后呈现在页面中。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
# Props 相关
// 基本使用
const props = { test1: 'test', test2: 17, test3: {} }
ReactDOM.render(<Demo {...props} />, document.getElementById('test'));
// 限制操作 -- 类组件、函数组件都是类似的
Demo.propTypes = {
test1: PropTypes.string.isRequired,
test2: PropTypes.number
};
Demo.defaultProps = {
test1: 'test',
test2: 17
};
1
2
3
4
5
6
7
8
9
10
11
12
13
2
3
4
5
6
7
8
9
10
11
12
13
# Refs 相关
// 基础
class Demo extends React.Component{
// 展示左侧输入框的数据
showData = () => {
const {input1} = this.refs
alert(input1.value)
}
// 展示右侧输入框的数据
showData2 = (event) => {
const {input2} = this.refs
// 有两种方式取值
alert(input2.value)
alert(event.target.value)
}
render(){
return(
<div>
<input ref="input1" type="text" placeholder="点击按钮提示数据"/>
<button onClick={this.showData}>点我提示左侧的数据</button>
<input ref="input2" onBlur={this.showData2} type="text" placeholder="失去焦点提示数据"/>
</div>
)
}
}
// 回调函数形式
const { input1, input2 } = this;
<input ref={c => this.input1 = c } type="text" placeholder="点击按钮提示数据"/>
<input ref={c => this.input2 = c } onBlur={this.showData2} type="text" placeholder="失去焦点提示数据"/>
// 回调函数形式--优化
saveInput = (c)=>{
this.input1 = c;
console.log('@', c);
}
<input ref={this.saveInput} type="text"/>
// React.createRef
// React.createRef调用后可以返回一个容器,该容器可以存储被ref所标识的节点,该容器是“专人专用”的
myRef = React.createRef();
<input ref={this.myRef} type="text" placeholder="点击按钮提示数据"/
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
# 事件处理
- 通过onXxx属性指定事件处理函数(注意大小写):React使用的是自定义(合成)事件, 而不是使用的原生DOM事件 -- 为了更好的兼容性;React中的事件是通过事件委托方式处理的(委托给组件最外层的元素) -- 为了的高效。
- 通过event.target得到发生事件的DOM元素对象 -- 不要过度使用ref。
# 函数接收参数--柯里化处理
<form onSubmit={this.handleSubmit}>
用户名:<input onChange={this.saveFormData('username')} type="text" name="username"/>
密码:<input onChange={this.saveFormData('password')} type="password" name="password"/>
<button>登录</button>
</form>
saveFormData = (dataType) => {
return (event) => {
this.setState({ [dataType]: event.target.value })
}
}
// 优化
用户名:<input onChange={event => this.saveFormData('username', event) } type="text" name="username"/>
密码:<input onChange={event => this.saveFormData('password', event) } type="password" name="password"/>
saveFormData = (dataType,event)=>{
this.setState({ [dataType]: event.target.value })
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19