学一门框架,首先要熟悉官方文档,不然框架总会学得有所欠缺。尤其当使用过React一段时候后,对此深有体会。于是便依据官方文档做了以下的学习笔记。建议大家也去官方文档学习,毕竟每个人对文档的理解都有所不同。
JSX是一个JavaScript的语法扩展,可以很好地呈现页面的交互形式,而且还具有JavaScript的全部功能。 使用JSX可以生成React元素。 浏览器默认是不支持JSX的,所以jsx语法必须使用@babel/preset-react进行编译,编译的结果React.createElement()这种Api的代码
使用JSX能给人在视觉上带来辅助作用,还可以让React显示更多的信息,以便于进行页面的开发。
我们可以在JSX中使用任何有效的JavaScript表达式。 JSX的表达式在经过编译后会被转换为普通的JavaScript函数
JSX中的DOM建议使用 驼峰式 的命名规则来定义属性的名称,例如:
class => className
tabindex => tabIndex
在JSX中可以通过使用引号来指定字符串的字面量,例如:
const element = ;
还可以使用大括号插入一个JavaScript的表达式
const element = ;
注意不要在大括号外面加上引号,对于同一个属性不能同时使用这两个符号。 当一个dom便签中不需要内容时,我们可以使用单闭合标签。 JSX可以防止 XSS注入攻击,其原理是在J会在ReactDom渲染之前进行转义为字符串。
JSX会被Babel转义为React.createElement()函数调用
const element = (
Hello, world!
);
可以写成
const element = React.createElement(
'h1',
{className: 'greeting'},
'Hello, world!'
);
代码会通过React.createElement()预先执行一些检查,以便不会出现错误。
定义:props是父子组件之间通信的纽带,它表示的是父组件传递过来的自定义属性和children。
props是一个对象,通过props可以访问到父组件传递过来的自定义属性和children。
props是只读的(不能修改它)
通过props,可以向子组件传递ReactNode(ReactElement、基本数据类型、数组、JSX对象)、普通对象、函数等。
ReactDOM元素负责更新DOM使其与React元素保持一致。 将一个React元素渲染到根DOM节点,使用的是ReactDOM.render()方法,将需要渲染的元素传入即可。
React元素是不可变对象,一旦被创建后就不可以被修改。 因此,当我们需要更新页面是需要重新调用ReactDOM.render()方法,对之前状态与新状态进行合并比较生成出最小差异树最后改变DOM。即:React只更新它需要更新的部分
什么组件?组件就是一个段可以被复用的代码块。组件化的意义:封装、快速开发。
函数组件(无状态组件)
特点:它只有props,没有state状态,也没有this、生命周期、ref、上下文等特性。所以它的性能较好。
function Welcome(props) {
return Hello, {props.name}
;
}
class组件(有状态组件)
特点:它有props,也有state状态、this、生命周期、ref、上下文等特性。相对于函数式组件其性能较差。
class Welcome extends React.Component {
render() {
return Hello, {this.props.name}
;
}
}
注意:组件名称必须为大写字母开头 React会将小写字母开头的标签视为原生DOM便签。 所有React组件都必须保护它们的props不被更改。 但是,可以使用state对数据进行操作。
React v16.8 以后,现在市场中主流是Hooks+函数式组件
简单来说就是组件从生到死的过程。
挂载阶段【3】constructor / render / componentDidMount
更新阶段【2】render / componentDidUpdate
卸载阶段【1】componentWillUnmount
构造器函数,它的入参是props(由父组件传递过来的自定义属性和children)
第一行代码必须是 super(props),调用父类的构造器函数
组件自有的state只能在这里定义(先定义后使用),state就是所谓的声明式变量
在这里不能修改props,在这里也不能用props来做运算。
在这里不要把props和state交叉赋值(运行),在React代码逻辑,永远要保持props和state的独立性。
在这里不要使用this.setState()修改state。
一般情况下,不要在这里写业务逻辑,比如DOM、BOM操作等都不要在这里做。
但,有时候我们需要改变this指向时,可以这里做。
相当于vue中mounted(),表示挂载阶段已完成。
各种业务逻辑(DOM操作、ref操作、调接口、开定时器)等都可以在这个做。
在这里可以this.setState(),默认还是异步的。
相当于vue中的updated(),表示更新阶段已完成。
在当前组件中,有三种方式触发更新阶段:props变化、this.setState()、this.forceUpdate()
使用场景:this.setState()这个异步工作完成时,我们去做另一件非render的事件。
在这里可以使用this.setState(),但必须给终止条件。(你可以想一想递归)
这个生命周期,是更新阶段的,发生在render()之后。
相当于vue中beforeDestroy(),表示当前组件即将被销毁。
在这里一般用于清除定时器、长连接、清缓存等。
在这里可以调用this.setState(),但毫无意义。
这个生命周期相当于一个用于控制更新的“开关”
this.forceUpdate()它会绕过这个生命周期。shouldComponentUpdate对this.forceUpdate()不影响。
适用于要考虑props和state两重数据流对视图渲染的影响的优化,但这个优化方案异常复杂,所以实践工作中几乎没人用。React官方提供了一个 PureComponent 构造类,彻底地解决上述这个难以解决的优化问题。
这个生命周期,是React所有生命周期中必须要有的。
这个生命周期横跨两个阶段,在挂载阶段和更新阶段都执行。
组件初始化、this.setState时、this.forceUpdate时、props变化时,它会执行。
在更新阶段,如果当前有shouldComponentUpdate并返回false时,render()将不执行。
在挂载阶段,render()一定会执行,shouldComponentUpdate不影响render()。
特别提醒:
1、在“自定义渲染方法”中不能使用this.setState()。
2、在render()内部、return()之间,不能使用到this.setState()。
3、在JSX中不能调用this.setState()方法。
render() 方法一定要返回 ReactNode,不能返回void。
1、不要直接修改State,因为这样不会让组件被重新渲染,而要在函数方法中调用setState()方法。
2、State的更新可能是异步的,React会把多个setState()方法调用合并成为一个调用。
例如:this.setState({num:1,age:1});
this.setState({age:2});
//会被合并成为
this.setState({num:1,age:2});
//最后进行DOM渲染
3、不要直接操作state中的值,例如:
++this.stete.num
这样虽然会更新页面,但是不建议这么做。我们可以使用this.stete.num+1
更新数据。4、State的更新会被合并,
this.setState()
方法会将state中的数据进行浅合并,即会将state中的数据进行替换。this.state = { a: { aa:1, ab:2 } }; //对上述state进行如下的更新操作 this.setState({a:{aa:123}}) //state的数据会变为如下数据 this.state = { a: { aa:123 } };
因此,我们需要先将复杂的数据进行展开,然后对要处理的值进行改变.
this.state = { a: { aa:1, ab:2 } }; //对上述state进行如下的更新操作 this.setState({a:{...a,aa:123}}) //state的数据会变为如下数据 this.state = { a: { aa:123, ab:2 } };
5、this.setState()方法的异同步情况
- 在合成事件(on*开头的系列事件、生命周期钩子)中,this.setState()是异步的。
- 在微任务函数体中(Promise.then)中,this.setState()是同步的。
- 在非合成事件中(定时器、DOM事件)中,this.setState()是同步的。
数据是向下流动的(单向数据流) state中的数据除了拥有并设置了他的组件外,其他组件都无法访问。 即从该 state 派生的任何数据或 UI 只能影响树中“低于”它们的组件。
React 事件的命名采用小驼峰式(camelCase),而不是纯小写。例如:onclick ==> onClick
使用 React 时,你一般不需要使用
addEventListener
为已创建的 DOM 元素添加监听器。事实上,你只需要在该元素初始渲染的时候添加监听器即可。1、如何绑定事件?
语法一:使用ES5的方式来绑定事件 onClick={this.handle.bind(this)}
事件处理器的最后一个参数永远都事件对象。
语法二:使用ES6的方式来绑定事件 onClick={()=>this.handle()}
需要手动传递事件对象。
建议:使用“语法二”来绑定事件。
注意:必须谨慎对待 JSX 回调函数中的
this
,
class 的方法默认不会绑定this
。如果你忘记绑定this.handleClick
并把它传入了onClick
,当你调用这个函数的时候this
的值为undefined
。handleClick(){ console.log('this is:', this); //“this is: undefined” } render() { return ( ); }
React中条件渲染主要以下几种方案
条件渲染:元素的显示与隐藏,这里巧用的都是JSX语法。
1、单一元素的条件渲染
通过花括号包裹代码,然后使用逻辑与 (&&) 运算符进行短路操作
语法:{ bol &&
} 2、两个元素的条件渲染
三目运算符
语法: { bol ?
: } 3、多个元素的条件渲染
语法:建议封装“自定义的渲染函数” function renderThing() { return
} 4、动态class
语法:className={'类名的拼接'}
5、动态style
语法:style={ css样式的键值值 }
6、阻止组件的渲染
让
render
方法直接返回null
,组件可以不进行任何渲染。例如:官方的示例
function WarningBanner(props) { if (!props.warn) { return null; } return (
Warning!); } class Page extends React.Component { constructor(props) { super(props); this.state = {showWarning: true}; this.handleToggleClick = this.handleToggleClick.bind(this); } handleToggleClick() { this.setState(state => ({ showWarning: !state.showWarning })); } render() { return (); } } ReactDOM.render(, document.getElementById('root') ); 在组件的
render
方法中返回null
并不会影响组件的生命周期
1.列表渲染:
React官方建议使用map()进行渲染。因为map数组方法在React中能对源数据进行处理,返回一个新的jsx数组。
在使用map生成一个jsx列表元素时,我们需要对其分配一个唯一的
key
属性,以确保当列表能正确的进行更新操作。当map嵌套过多时,我们可以对其进行封装提取成为一个组件。
2.注意:
key 只是在兄弟节点之间必须唯一,在不同组件之间可以有相同的key。key 会传递信息给 React ,但不会传递给你的组件,因此,我们无法通过props读取到key,我们可以使用其他的属性对key进行传值。
在React中使用表单控件时,建议使用受控表单组件,即:表单/类表单的value或checked由state控制着,其value数据通常保存在组件的 state 属性中,并且只能通过使用
setState()
来更新。例如:
this.setState({ a: e.target.value })}>
存在多个受控组件时,我们可以使用给每个组件设置一个name值,然后通过event.target.name的值进行对应的操作。
原则:除了文件上传以外,都要使用受控表单。
当两个组件之间需要共享一个状态(数据)时,我们的做法是把这个状态(变量)定义它们共同的最近的一个父组件中。依靠自上而下的数据流,而不是尝试在不同组件间同步 state。
对于状态提升,我们一般都是使用“受控组件”来进行操作。
例如:
import React, { PureComponent } from 'react' const Aitem = props => { const { value, onValue } = props return (
A onValue(Number(ev.target.value))} />) } const Bitem = props => { const { value, onValue } = props return (B onValue(Number(ev.target.value)/2)} />) } export default class Class extends PureComponent { constructor(props) { super(props) this.state = { a:1 } } render() { const {a} = this.state return ( <>this.setState({a})} /> this.setState({a})} /> > ) } }
React 有十分强大的组合模式。React推荐使用组合而非继承来实现组件间的代码重用。
React有一个特殊的children,可以将他们的子组件传递到渲染结果中。这与vue的slot插件十分类似。如果理解了vue的插槽,那么React的props.children也能够理解清楚。
例如:
import React, { PureComponent } from "react"; const Comp = (props) => { return (
{props.children}) } export default class Composition extends PureComponent { render() { return ( <>Composition> ) } } 这是children1这是children2既然 React的props.children与vue的v-slot:default相似。
那么React是不是也有特定的“插槽”呢?
答案是肯定的,毕竟Vue借鉴的React。
React使用使用对应的prop进行渲染,因为我们可以将任何东西作为 props 进行传递。
import React, { PureComponent } from "react"; const Comp = (props) => { return (
) } export default class Composition extends PureComponent { render() { return ( <>{props.left}{props.right}Composition这是left
对于组件的继承,React官方给出的建议是:如函数、对象或者类。组件可以直接引入(import)而无需通过 extend 继承它们。
如果用组件使用继承的话我们可能会看到这样的代码出现
class Modal extends PureComponent {}
class DefaultHeaderModel extends Modal {}
class MiniHeaderModal extends Modal {}
class DefaultFooterDefaultHeaderModal extends DefaultHeaderModel {}
class CustomFooterMiniHeaderModal extends MiniHeaderModal {}
......