react 核心概念 : https://react.docschina.org/docs/getting-started.html(官网) 或 https://www.w3cschool.cn/react/react-tutorial.html
一、react 的 JSX:
1、概念: react 本质上是js,但是react自己用创造了一个表达式,即JSX表达式。(可以理解为,react中的js经过react编译为真正的js。原生的js就不变,像JSX这种react的语法就编译成原生的js)
官方上把 JSX 语法称为 表达式,个人觉得也可以把 JSX 称为一种 数据类型 。类似正则表达式,等号右边只有一个 正则字面量(表达式可以是只有这个字面量的,https://www.cnblogs.com/fangsmile/p/8337021.html)。
如下面的 JSX 语法:等号右边只有一个类似字面量的值,就叫 JSX字面量好了(类比正则字面量)。
const element = <h1>Hello, world!</h1>; // 等号右边的这个表达式,原生js是会报错的,无法识别。但是react 的可以识别的。
JSX 的基本语法规则 :遇到 HTML 标签(以 <
开头),就用 HTML 规则解析;遇到代码块(以 {
开头),就用 JavaScript 规则解析。 http://www.ruanyifeng.com/blog/2015/03/react.html
注意:JSX 语法不是 JS 引擎 解析的,所以不要完全使用js的语法规则去分析 JSX 代码。JSX 编译后的代码才是 js 代码。
JSX 表达式最外层只能有一个标签,这点 和 vue 是一样的。
2、JSX表达式外面的括号:JSX 标签里能够包含很多子元素,前后要加上括号。如:
const element = <img src={user.avatarUrl} />; // img标签内没有其他的子元素,可以不用括号
const element = (
<div> // div 标签里有子元素
<h1>Hello!</h1>
<h2>Good to see you here.</h2>
</div>
);
3、JSX表达式,经过 babel转译,所有的JSX表达式会变成调用 React 的 createElement 方法,变成如下代码。(JSX编译后的代码用到了React对象,所以必须要引入React对象)
import React, { Component } from 'react'; class Process extends Component { render() { return (<div>哈哈哈</div>) } } /* babel 编译后 */ import React, { Component } from 'react'; class Process extends Component { render() { return React.createElement( 'div', null, 'u54C8u54C8u54C8' ); } }
二、react元素渲染:https://react-1251415695.cos-website.ap-chengdu.myqcloud.com/docs/rendering-elements.html (一个 JSX 表达式 就是一个 react元素)
react 元素 渲染 是通过 ReactDOM.render()方法进行渲染的,将 react元素 和 挂载的 DOM对象,作为参数传递给这个方法就了。如:
const element = <h1>Hello, world</h1>; // react 元素 ReactDOM.render(element, document.getElementById('root'));
三、react组件:https://react-1251415695.cos-website.ap-chengdu.myqcloud.com/docs/components-and-props.html 或 https://www.jianshu.com/p/f5c9ec0917bb(React创建组件的三种方法)
定义组件的三种方法: 函数组件与 class 组件(创建后两个组件在 React 里是等效的,class组件有他的额外特性)
1、函数组件:(函数组件,本质上是一个函数。相比较 类组件,函数组件有很多东西是没有的。) 开始使用react,不清楚什么时候使用函数组件好。就全部使用class组件吧。
function Welcome(props) { return <h1>Hello, {props.name}</h1>; }
2、React.Component组件:以ES6的形式来创建react
的组件的(React目前极为推荐的创建有状态组件的方式,https://www.jianshu.com/p/f5c9ec0917bb)
class Welcome extends React.Component { render() { return <h1>Hello, {this.props.name}</h1>; } }
3、React.createClass 组件:以 ES5的原生的JavaScript来实现的React
组件。【这个基本和class的一样,现在开发基本 不予考虑,16.0版本已经抛弃了React.createClass方法】
var HelloMessage = React.createClass({ render: function() { return <h1>Hello {this.props.name}</h1>; } }); ReactDOM.render( <HelloMessage name="fannieGirl" />, document.getElementById('example') );
注意: 组件名称必须以大写字母开头。
总结:1、react 组件的本质是函数 返回一个JSX 表达式(react元素)。class组件也是一样,在内部 render 方法中,返回一个JSX 表达式。
2、函数组件和类组件的区别。 参考 https://blog.csdn.net/wu_xianqiang/article/details/91320529
函数组件 的性能比 类组件 的性能要高,因为类组件使用的时候要实例化,而函数组件直接执行函数取返回结果即可。为了提高性能,尽量使用函数组件。
区别 | 函数组件 | 类组件 |
---|---|---|
是否有 this |
没有 | 有 |
是否有生命周期 | 没有 | 有 |
是否有状态 state |
没有 | 有 |
3、需要使用到state的地方就必须要使用类组件。一般当前组件,调用ajxa请求数据,都好用到state
四、react元素 渲染:react组件要渲染成 DOM,需要先将组件变成 react元素(把组件当标签使用就可以变成react元素),再使用react元素的渲染方法进行渲染。如:
function Welcome(props) { return <h1>Hello, {props.name}</h1>; } const element = <Welcome name="Sara" />; // 把 Welcome组件 当标签使用就可以变成react元素了
ReactDOM.render( element, document.getElementById('root') );
五、Props:父组件传递给子组件的数据。
1、Props 的只读性:组件无论是使用函数声明还是通过 class 声明,都决不能修改自身的 props。
this.props.name //class组件中获取父组件传递过来的值,函数组件中去掉 this
2、很多文章介绍,props改变,会重新渲染子组件。这个其实是错误的,props 修改不会,引起子组件的如何变化,只是父组件中某个变量的值改变了。
往往 props 的值 是state值传递过来的。修改这个state 才导致,父组件 调用 render 方法,递归更新所有的组件树。这个时候新的props才传递进去的。
六、State:react组件的状态 ,state状态一旦改变就会调用 组件的 render 方法,重新生成虚拟DOM树,通过diff比较,更新视图(虚拟DOM本身的更新,对ui没有任何的影响,只有挂载到真实DOM上才是有效的)。
理解:state 中的属性的数据变化,与其他的变量的主要区别是,state的变化会驱动 ui 重新渲染的。
this.state.name // 读取 state 内的属性值 this.setState({ // 这个方法就是修改状态值,每次修改以后,自动调用 this.render 方法,再次渲染组件 name:'dd' })
1、设置state使用setState方法,setState方法 可能是一个异步方法(不能保证 同步执行,大部分情况是同步执行的。开发还是把它 当成异步处理,这个可以避免错误),
一个生命周期内所有的 setState 方法会合并操作。所以不会改变一个state数据就更新下 ui组件,一次性把state都设置好,在更新ui组件。
2、this.setState 修改某一对象的某个属性值: 其它属性保留不变:https://blog.csdn.net/Wcharles666/article/details/90269837 或 https://yq.aliyun.com/articles/667986(推荐,提供了3钟方法)
let paginationParam = Object.assign({}, this.state.paginationParam, {current: 1}) // 这里用到 Object.assign 将所有可枚举属性的值从一个或多个源对象复制到目标对象 this.setState({ paginationParam:paginationParam })
七、React 组件生命周期: 详细介绍,在另外一篇文章上有写 https://www.cnblogs.com/wfblog/p/11842622.html(说明了各个生命周期执行的顺序)
a、componentWillMount:
b、componentDidMount : 在第一次渲染后调用,之后组件已经生成了对应的DOM结构。
c、componentWillReceiveProps :在组件接收到一个新的prop时被调用
d、shouldComponentUpdate :
e、componentWillUpdate:在组件接收到新的props或者state但还没有render时被调用。在初始化时不会被调用。
f、componentDidUpdate :在组件完成更新后立即调用。在初始化时不会被调用。
g、componentWillUnmount:在组件从 DOM 中移除的时候立刻被调用。
八、事件处理:https://react.docschina.org/docs/handling-events.html
<button onClick={activateLasers}> Activate Lasers </button>
1、react中不能通过返回 false
的方式阻止默认行为。你必须显式的使用 preventDefault
2、JSX表达式中DOM元素绑定的事件,需要手动绑定 this,不然 事件函数中使用this会报错。https://www.cnblogs.com/yuyujuan/p/10111164.html
绑定的方法:
a、通过bind来指明当前方法中的this指向当前Home.js组件
render() { // 通过bind方法实现,可以传递参数 return <button onClick={this.handleClick.bind(this, 'test')}>Test</button>; }
b、在构造函数constructor中改变this指向。
class App extends Component { constructor(props) { super(props); this.handleClick = this.handleClick.bind(this); } handleClick(e) { console.log(e); } render() { return <button onClick={this.handleClick}>Test</button>; } }
c、使用箭头函数改变this指向。
render() { return <button onClick={() => this.handleClick()}>Test</button> }
3、事件函数中 带自定义的参数:
上面的 3种 绑定 this 的事件函数方法中,其中第二种是无法 传入自定义参数的。但是可以把需要的参数,作为这个DOM对象的属性放上去。
class Alphabet extends React.Component { handleClick(e) { this.setState({ justClicked: e.target.dataset.letter // 这里通过 获取DOM的自定义属性,获取了事件函数 需要的参数 }); } render() { return ( <div data-letter={letter} onClick={this.handleClick}> {letter} </div> ) } }
九、 条件渲染: https://react.docschina.org/docs/conditional-rendering.html
条件渲染的方式:
1、react的条件渲染是 ,通过再创建一个组件组件,这个组件中通过js的条件判断返回不同的组件。 https://react.docschina.org/docs/conditional-rendering.html
function UserGreeting(props) { return <h1>Welcome back!</h1>; } function GuestGreeting(props) { return <h1>Please sign up.</h1>; } function Greeting(props) { // 这个就是另外创建的组件 const isLoggedIn = props.isLoggedIn; if (isLoggedIn) { return <UserGreeting />; } return <GuestGreeting />; }
2、JSX 花括号中,使用 运算符 && 。 这个方法只能 相当于 if 的功能,不能作为 if...else... 的功能。
function Mailbox(props) { const unreadMessages = props.unreadMessages; return ( <div> {unreadMessages.length > 0 && (<h2>hello</h2>)} </div> ); }
3、JSX 花括号中,使用 三目运算符 condition ? true : false
render() { const isLoggedIn = this.state.isLoggedIn; return ( <div> {isLoggedIn ? ( <LogoutButton onClick={this.handleLogoutClick} /> ) : ( <LoginButton onClick={this.handleLoginClick} /> )} </div> ); }
重点:JSX表达式中花括号也是可以使用JSX元素的(把JSX元素当成一个数据,就像字符串)
十、列表 渲染 :https://react.docschina.org/docs/lists-and-keys.html
1、通过map渲染 (JSX表达式中,如果花括号只有一个数组,则react编译时,会把所有的项拼接在一起)
const numbers = [1, 2, 3, 4, 5]; const listItems = numbers.map((number,index) => <li key={index}>{number}</li> ); return( <div> {listItems} // 这里 listItems 是一个数组,react编译后,会把这个数组中的每项拼接在一个 </div> ) }
注意:列表渲染的标签必须有一个 key 属性,这点 和 VUE 是一样的原理。 官网上说,不建议使用 索引来用作 key 值,因为这样做会导致性能变差,还可能引起组件状态的问题。
十一、表单:https://react.docschina.org/docs/forms.html
1、受控组件:表单的数据保存在组件的 state 属性中,并且只能通过使用 setState()
来更新。从效果来看的话,受控组件 实现了 双向绑定的功能。
受控组件,如果没有绑定 Change 事件,通过setState()来更新,则输入不了内容。不同的 表单元素,select 、textarea 怎么处理看上面网站
class NameForm extends React.Component { state = {value: ''}; handleChange(event) { this.setState({value: event.target.value}); } render() { return ( <input type="text" value={this.state.value} onChange={this.handleChange.bind(this)} /> ); } }
a、多个受控组件,给每一个表单设置一个 onChange 事件函数,这种处理方式肯定会导致代码不好维护。处理方式是: https://blog.csdn.net/weixin_30371875/article/details/98777554
handleChange(val,event){ this.inputData[val] = event.target.value } <input onChange={this.handleChange.bind(this, 'gtPersonNum')}/> <input onChange={this.handleChange.bind(this, 'ltPersonNum')}/>
2、非受控组件:和 以前 html 的差不多的使用, 使用 ref 指向 这个表单对象。下面的,this.input 表示的就是这个input表单。
class NameForm extends React.Component { constructor(props) { super(props); this.handleSubmit = this.handleSubmit.bind(this); this.input = React.createRef(); } handleSubmit(event) { alert(this.input.current.value); event.preventDefault(); } render() { return ( <form onSubmit={this.handleSubmit}> <input type="text" ref={this.input} /> // this.input 指向当前的DOM节点 </form> ); } }
或
test(){ console.log(this.input.value); 这里直接使用DOM节点的方法,获取节点数据 } render() { return ( <input type="text" ref={(input) => this.input = input} onChange={this.test.bind(this)}/> // 这里 把 this.input 指向当前DOM 节点 ) }