此笔记只作学习使用,充分参考了React官网-文档和思否上的相关博客!
前言
无论学习React还是Vue框架其基石还是JS(ES),箭头函数,类,模板字符串,let,const等
JSX简介
const element = <h1>Hello, world!</h1>;
- 这就是JSX,JavaScript的语法扩展,react推荐在React中使用JSX来描述用户界面。
- JSX并不是模版语言,而是在JS内部实现的。
- 在JSX中使用Javascript表达式是不受限制的,在JSX内的表达式要包含在大括号中。
function formName(user){
return user.firstName + ' ' + user.lastName;
}
const user={
firstName: 'liguang',
lastName: 'pert'
};
cosnt element={
<h1>
Hello , {formName(user)}!
</h1>
};
ReactDOM.render(
element,
document.getElementById('root')
);
- 推荐在JSX代码的外面扩上一个小括号,防止分号自动插入之bug
- JSX本身也是一种表达式
- 编译后,JSX被转化为普通的JS对象
- 所以,你可以在if或for语句中使用JSX,将其赋值给变量,当作参数传入,作为返回值亦可:
function getGreeting(user){
if(user){
return <h1>Hello, {formName(user)}!</h1>;
}
return <h1>Hello, Stranger.</h1>;
}
- JSX属性:
- 使用引号来定义以字符串为值的属性
const element = <div tabIndex="0"></div>
- 亦可使用打括号来定义JS表达式为值的属性
const element=<img sarc={user.avatarUrl}></img>;
- JSX嵌套
- 若JSX标签是闭合的,需要在结尾处用/>,likes XML或者HTML:
cosnt element = <img src={user.avarUrl} />;
- JSX标签可以相互嵌套:
const element= {
<div>
<h1> Hello!</h1>
<h2>Good to see you here.</h2>
</div>
};
JSX的特性更接近JS而不是HTML,所以React DOM使用 cameCase命名。
- JSX防注入攻击
const title = response.potentiallyMaliciousInput:
//直接使用是安全的
const element = <h1>{title}</h1>
-
React DOM在渲染之前默认会过滤所有传入的值,可以确保你的应用不会被攻击。所有的内容在渲染之前都转换成了字符串。可以有效防止XSS(跨站脚本)攻击。
-
JSX 代表Objects
- Babel转译器会把JSX转换成一个名为React.createElement()的方法调用。
const element={
<h1 className="greeting">
Hello People!
</h1>
};
//等价于
cosnt element = React.createElement(
'h1',
{className: 'greeting'},
'Hello, world!'
);
- React.createElement()首先会进行一些避免bug的检查,之后会返回类似下例的对象:
//简化版
const element={
type:'h1',
props:{
classname:'greeting',
children:'Hello, world'
}
};
- 这样的对象被称为“React 元素”,代表所有你在屏幕上看到的东西。React通过读取这些对象来构建DOM并保持数据内容一致。
元素渲染
- 元素是构成React应用的最小单位
const element= <h1>Hello people</h1>;
上述的语句就是元素,用来描述你在屏幕上看到的内容;
- react中的元素就是普通的对象,React DOM可以确保浏览器DOM的数据内容与React元素保持一致。
- 元素与组件是不一样的,组件是一个更广阔的范围。元素一般只是组件的一个部分。
将与阿苏渲染到DOM中
- 实例
<div id="root"></div>
<!-- 在此div中的所有内容都由react DOM来管理,称之为“根”DOM节点 -->
<!--
react开发应用一般只定义一个根节点
-->
<!-- 要将react元素渲染到根DOM节点中,通过把它们传递给reactDOM.render()的方法来将其渲染到页面上 -->
const element = <h1> Hello, People</h1>;
ReactDOM.render(element, documnet.getLElmentById('root'));
更新元素渲染
- react元素都是immutable不可变的。一旦元素被创建,其内容或属性无法更改。
- 所以,更新元素渲染的唯一办法就是创建一个新的元素将其传入ReactDOM.render(方法):
- 实例:
function tick(){
const element ={
<div>
<h1> Hello,world!</h1>
<h2>It is {new Date().toLocaleTimeString().}</h2>
</div>
};
ReactDOM.render(element, document.getElementById('root'));
}
setInterval(tick, 1000);
- 大多数React应用只会调用一次ReactDom.render()
React只会更新必要的部分
- react DOM首先会比较元素内容先后的不同,而在渲染过程中只会更新改变了的部分。
组件 & Props
- 组件可以将UI切分成一些独立的、可复用的部件,因此,我们的注意力应该在于构建每一个单独的部件。
- 组件
- 从概念上看就像是函数,可以接受任意的输入值(props),并返回一个需要在页面上展示的react元素。
函数定义/类定义组件
- 定义一个组件最简单的方式是使用Js
function Welcome(props){
return <h1>Heelo, {props.name}</h1>
}
//这个函数是一个有效的React组件,它接收一个单一的“props”对象,并返回了一个React元素。
- 使用ES6 class来定义一个组件:
class Welcome extends React.Component{
render(){
retrun <h1>Hello, {this.props.name}
</h1>;
}
}
//上面两个组件在React中是相同的。
组件渲染
- 在此之前,我们遇到的元素都是DOM标签:
cosnt element = <div />;
- 用户自定义的组件可以当作react元素使用。
cosnt element = <Welcome name="Sara" />
- 当react遇到的元素是用户自定义的组件,它会将JSX属性作为单个对象传递给该组件,这个对象称之为“props”。
function Welcome(props){
return <h1>Hello, {props.name}</h1>
}
const element = <Wecome name="Sara" />;
//<Wecome name="Sara" />调用了ReactDOM.render()方法
ReactDOM.render(
element,
document.getElementById('root')
);
//react将{name:'Sara'}作为props传入并调用Welcome组件。
//Wecome组件将<h1> Hello, Sara</h1>元素作为结果返回。
//reactDOM将DOM更新成<h1>Hello, Sara</h1>
- 组件名称必须以大写字母开头
-
表示一个DOM标签,则表示一个组件,并且在使用该组件时,必须定义或引用它。
组合组件
- 组件可以在它的输出中引用其它组件,这就可以让我们用同一组件来抽象出任意层次的细节。
- React应用中,按钮、表单、对话框、整个屏幕的内容等,这些通常都被表示为组件。
- 实例:创建一个App组件,用来多次渲染Welcome组件:
function Wecome(props){
return <h1> Hello, {props.name}</h1>
}
functionApp(){
return (
<div>
<Welcome name="Sara" />
<Welcome name="Cahal" />
<Welcome name="Edite" />
</div>
);
}
ReactDOM.render(
<App />,
document.getElementById('root')
);
- 通常,一个新的React应用程序的顶部是一个App组件。
- 组件的返回值只能是一个根元素。这是需要用一个
来包裹所有的元素的原因。
提取组件
function Comment(props){
return (
<div className="Comment">
<div className="UserInfo">
<img className="Avatar"
src={props.author.avatarUrl}
alt={props.author.name}
/>
<div className="UserInfo-name">
{props.author.name}
</div>
</div>
<div className="Comment-text">
{props.text}
</div>
<div className="Comment-date">
{formatDate(props.date)}
</div>
</div>
);
}
//这个组件接受author(Object)、text(String)、date(Date Object)作为props,用来描述一个社交媒体网站上的评论
//组件由于嵌套,变得难以被修改,可复用的部分也难以被复用。所以我们从这个组件中提取一些小组件
//提取Avatar组件
function Avatar(props){
return (
<img className="Avatar"
src={props.user.avatarUrl}
alt={props.user.name}
/>
)
}
//接着提取一个UserInfo组件,用来渲染Avatar旁边的用户名:
function UserInfro(props){
return (
<div classname="UserInfo">
<Avatar user={props.user} />
<div className="UserInfo-name">
{props.user.name}
</div>
</div>
);
}
//对Comment组件做一些小小的调整:
//进一步简化Comment组件
function Comment(props){
return (
<div classname="Comment">
<UserInfo user={props.author} />
<div className="Comment-text">
{props.text}
</div>
<div className="COmment-date">
{formDate(props.date)}
</div>
</div>
);
}
- 抽取组件,在大型应用中,构建可复用的组件完全值得。
- 当UI中的一个部分被重复使用好多次的组件都可以抽象成一个可复用的组件的绝佳选择。good job
Props的只读性
- 组件(使用函数或类定义的),它绝不能修改它自己的props
function sum(a,b){
return a + b;
}
//纯函数,没有改变自己的输入值
//下面是非纯函数,它会改变它自身的输入值
function withdraw(account, amount){
account.total -= amount;
}
- 所有的react组件必须像纯函数那样使用它们的props。
React生命周期钩子
[外链图片转存失败(img-ik4r2Mvh-1566373395416)(https://segmentfault.com/img/remote/1460000015263077?w=1087&h=668)]
挂载阶段
-
- constructor
-
- componentWillMount
-
- render
-
- componentDidMount
-
- constructor
class SignUpDialog extends React.Component{
constructor(props){
super(props);
}
render(){}
}
//ES6 class 构造方法,接受props属性对象,props由父组件传入,若父组件未传入,则指向本身。
-
- componentWillMount
- 组件被挂载到DOM前,只会调用一次,一般用更靠前的constructor代替,在其中调用this.setState()不会引起组件重新渲染。
-
- render
- 组件的唯一必要方法,根据组件的props与state返回一个React元素,用于描述组件的UI
-
- componentWillMount
- 组件被挂载到DOM后调用,且只会被调用一次,在其中调用this.setState()会引起组件重新渲染,组件本次的更新还没有执行完成,又会进入新一轮的更新,导致不断循环更新,进入死循环。
- 副作用操作,通常用于向后端请求数据。
更新阶段
-
- componentwillReactProps(nextProps)
- props变化会触发componentWillReceviceProps, setState()不会触发
-
- shouldComponentUpdate(nextProps, nextState)
- 判断组件是否继续更新,减少不必要渲染,优化
-
- componentWillUpdate
- 在render前调用,作为组件更新前执行某些工作过的地方,(shouldComponentUpdate, componentWillUpdate不能调用setState()避免引起循环调用)
-
- render
-
- componentDidUpdate(preProps, prevState)
- 组件更新后调用,可以作为更新后调用DOM的地方,两个参数代表更新前的属性和状态
卸载阶段
- 组件从DOM移除的阶段,可用于清除组件中的定时器,清除componentDidMount手动创建的DOM等等,避免内存泄漏。
State & 生命周期
- 目前我们更新UI只调用ReactDOM.render()方法来实现。(如下)
function tick(){
const element= (
<div>
<h1> Hello, world!</h1>
<h2>It is {new Date().toLocaleTimeString()}. </h2>
</div>
);
ReactDOM.render(
element,
document.getElementById('root')
);
}
setInterval(tick, 1000);
- 现在,学习如何使用Clock组件真正可重用和封装。它将设置自己的计时器,并每秒种更新一次
- 这是一个例子:
function Clock(props){
retrun (
<div>
<h1>Hello, world!</h1>
<h2>It is {props.date.toLocaleTimeString()}. </h2>
</div>
);
}
function tick(){
ReactDOM.render(
<Clock date={new Date()} />
document.getElementById('root')
);
}
setInterval(tick, 1000);
- 以上代码,没能在Clock中设置一个定时器并且每秒更新UI应该是Clock的实现细节。
- 组件的状态是私有的,是有当前组件才能调用它,折合属性不同。
- 局部状态只适用于类。
将函数转换成类(5步)
- 创建一个名称扩展为React.Component的ES6类
- 创建一个叫做render()的空方法
- 将函数体移动到render()的空方法
- 在render()方法中,使用this.props替换props
- 删除剩余的空函数声明
class Clock extends React.Component{
render(){
retrun (
<div>
<h1>H饿了咯,世界</h1>
<h2>It is {this.props.date.toLocaletimeString()}.</h2>
</div>
);
}
}
//使用类就允许我们使用其他特性,局部状态、生命周期钩子等
为一个类添加局部状态(3步)
-
- render()方法中使用this.state.date替代this.props.date
-
- 添加一个类构造函数来初始化this.state
class Clock extends React.Component{
Constructor(props){
super(props);
super(props);
this.state = {date: new Date()};
}
render(){
return (
<div>
<h1>Hello, world!</h1>
<h2>It is {this.state.date.toLocaleTimeString()}. </h2>
</div>
);
}
}
-
- 从元素移除date属性:
ReactDOM.render(
<Clock />
document.getElementById('root')
);
将生命周期方法添加到类中
- 称之为生命周期钩子
- 在组件类上声明特殊的方法,当组件挂载时,运行代码:
class Clock extends React.Component{
constructor(props){
super(props);
this.state = {data:new Date()};
}
componentDidMount(){
//当组件输出到DOM后,执行componentDidMount()钩子,在此处建立定时器
this.timerID = setInrterval(
() => this.tick(),
1000
);
}
//若不在render()中使用某些东西,它不应该在state中存在
componentWillUnmount(){
//在此,卸载计时器
clearInterval(this.timerID);
}
render() {
return (
<div>
<h1>Hello, world!</h1>
<h2>It is {this.state.date.toLocaleTimeString()}.</h2>
</div>
);
}
}
- 到此,这个程序就结束了!
- 现在回顾发生了什么以及调用顺序
正确使用状态(3件事)
-
- 不要直接更新状态
//wrong
this.state.comment = "Hello";
//corrent
this.setState({comment: 'Hello'});
//构造函数是唯一初始化this.state的地方。
-
- 状态更新可能是异步的
- React可以将多个setState()调用合并成一个调用来提高性能。
- this.props和this.state可能是异步更新的,不要依靠它们的值来计算下一个状态。
//Wrong
this.setState({
coounter: this.state.counter + this.props.increment,
});
//Corrent
this.setState((preState, props) => ({
counter: prevState.counter + props.increment
}));
-
- 状态更新合并
- 当调用setState()时,React将你提供的对象合并到当前状态。
- 实例:
constructor(props){
super(props);
this.state = {
posts:[],
comments:[]
};
}
//可以调用setState()独立地更新
componentDidMount() {
fetchPosts().then(response => {
this.setState({
posts: response.posts
});
});
fetchComments().then(response => {
this.setState({
comments: response.comments
});
});
}
//这里的合并是浅合并
数据自顶向下流动
- 状态,除了拥有并设置它的组件外,其他组件不可访问
- 组件可以选择将其状态作为属性传递给其子组件。
- 单向数据流,任何状态始终由特定组件所有,并从该状态导出的任何数据或UI只能影响树中下方的组件。
此笔记只作学习使用,充分参考了React官网-文档和思否上的相关博客!
建议学习React还是要充分的学习官网上的文档教程!