React
语法基础
jsx语法规范
定义虚拟DOM时,不要写引号。
标签中混入JS表达式要用{}
.
样式的类名指定不要用class
而要用className
。
内联样式。要用style={{key:value}}
的形式去写。
只有一个根标签。
标签必须闭合。
标签首字母
若小写字母开头,则将改标签为html
中同名的元素。
若大写字母开头,React
将该标签当做是组件。
列表渲染
const data = ['Angular' ,'React' ,'Vue' ];const VDOM = ( <div > <ul > { data.map((item,index) =>{ return <li key ={index} > {item}</li > }) } </ul > </div > )ReactDOM .render (VDOM ,document .getElementById ('root' ));
组件的定义方式
函数式组件
function MyComponent ( ){ return <h2 > 我是函数定义的组件</h2 > }ReactDOM .render (<MyComponent /> ,document .getElementById ('root' ));
执行ReactDOM.render(<MyComponent/>,document.getElementById('root'))
后发生了什么?
React
解析组件标签,找到MyComponent
组件。
发现组件是使用函数定义的,随后调用该函数,将返回的虚拟DOM转为真实DOM,随后呈现在页面上。
类式组件
class MyComponent extends React.Component { render ( ){ return <h2 > 我的用类定义的组件</h2 > } }ReactDOM .render (<MyComponent /> ,document .getElementById ('root' ));
执行ReactDOM.render(<MyComponent/>,document.getElementById('root'))
后发生了什么?
React
解析组件标签,找到MyComponent
组件。
发现组件是使用类定义的,随后new
出来该类的实例,并通过该实例调用到原型上的render
方法。
将render
返回的虚拟DOM转为真实DOM,随后呈现在页面上。
类的知识点补充
class Person { constructor (name,age ){ this .name = name; this .age = age; } speak ( ){ console .log (this ); } }const p1 = new Person (); p1.speak (); const x = p1.speak ;x ();
类中的方法是开启局部严格模式的。
通过类的实例调用原型链上的方法时,this指向的是类的实例。
直接调用类的原型链上的方法时,this指向的是undefined,正常情况下应该是window,但是类中的方法是开启局部严格模式的,因此指向的是undefined。
如何解决让类中的方法的this始终指向类的实例对象?在构造器中添加如下代码
this.speak = this.speak.bind(this)
直接将原型上的方法加到类的实例对象上去。
组件属性
属性的三大属性是针对于类式组件。(除了props)
state
状态不可以直接更改,要借助内置的APIsetState
(在原型链的React.Component
身上)进行更改。
setState
的过程是一种合并,而不是替换。
标准写法 :
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 class Weather extends React.Component { constructor (props ){ super (props); this .state = {isHot :false ,wind :'slight' }; this .changeWeather = this .changeWeather .bind (this ); } render ( ){ const {isHot,wind} = this .state ; return <h1 onClick ={this.changeWeather} > 天气{isHot ? 'Hot' : 'cool'}</h1 > ; } changeWeather ( ){ const isHot = this .state .isHot ; this .setState ({isHot :!isHot}); } } root.render (<Weather /> );
精简写法 :
class Weather extends React.Component { state = {isHot :false ,wind :'slight' }; render ( ){ const {isHot,wind} = this .state ; return <h1 onClick ={this.changeWeather} > 天气{isHot ? 'Hot' : 'cool'}</h1 > ; }; changeWeather = ()=> { const isHot = this .state .isHot ; this .setState ({isHot :!isHot}); } }
props
展开运算符
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 let arr1 = [1 ,3 ,5 ,7 ,9 ];let arr2 = [2 ,4 ,6 ,8 ,10 ];console .log (...arr1); let arr3 = [...arr1,...arr2];function sum (...numbers ){ return numbers.reduce ((preValue,currentValue ) => { return preValue + currentValue; }) }console .log (sum (1 ,2 ,3 ,4 )); let person = {name :'Tom' ,age :18 };console .log (...person); let person2 = {...person} person.name = 'Jack' ;console .log (person2.name ); let person3 = {name :'Tom' ,age :18 };let person4 = {name :'Jack' ,age :18 };let person5 = {...person3,...person4};console .log (person5);
props
的使用
在组件标签中直接传入props
,然后在render()
内取出props
放在页面中。
也可以用展开运算符的方式传入组件标签中。
对props
的限制
引入prop-types.js
,只用可以添加定义的类的一个属性propTypes
,在这个属性(对象)中对props
的类型和必要性进行限制。
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 class Person extends React.Component { render ( ){ const {name,age,sex} = this .props ; return ( <ul > <li > {name}</li > <li > {age}</li > <li > {sex}</li > </ul > ) } }Person .proTypes = { name :PropTypes .string .isRequired , sex :PropTypes .string , age :PropTypes .number , speak :PropTypes .func , }Person .defaultProps = { sex :'questioned' , age :18 }
props
的简写方式
直接使用类的static
属性,在类本身上添加属性或方法。
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 class Person extends React.Component { render ( ){ const {name,age,sex} = this .props ; return ( <ul > <li > {name}</li > <li > {age}</li > <li > {sex}</li > </ul > ) } static propTypes = { name :PropTypes .string .isRequired , sex :PropTypes .string , age :PropTypes .number , speak :PropTypes .func , } static defaultProps = { sex :'questioned' , age :18 } }
类式组件构造器中的props
class Person extends React.Component { constructor (props ){ super (props); console .log (this .props ); } constructor ( ){ super (); console .log (this .props ); } }
函数组件使用props
类式组件的三大属性中只有props
是可以在函数组件中使用的。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 function Person (props ){ const {name,age,sex} = props; return { <ul > <li > {name}</li > <li > {age}</li > <li > {sex}</li > </ul > } }Person .propTypes = { name :PropTypes .string .isRequired , sex :PropTypes .string , age :PropTypes .number , speak :PropTypes .func , }Person .defaultProps = { sex :'questioned' , age :18 }ReactDOM .render (<Person name ="jerry" sex ="female" age ={18}/ > ,document .getElementById ('root' ));
refs
跟Vue中的ref蛮像的
回调函数形式的ref
class Demo extends React.Component { render ( ){ return ( <div > { /*<input ref ={currentNode => this.input = currentNode} type="text"/>*/} <input ref ={this.saveInput} type ="text" /> <button onClick ={this.showInfo} > 点我</button > </div > ) } showInfo = () => { const {input} = this ; alert (input.value ); } }
Q:在使用内联回调ref
时会被调用了几次?
A:如果 ref
回调函数是以内联函数的方式定义的,在更新过程 中它会被执行两次,第一次传入参数 null
,然后第二次会传入参数 DOM 元素。这是因为在每次渲染时会创建一个新的函数实例,所以 React 清空旧的 ref 并且设置新的。通过将 ref 的回调函数定义成 class 的绑定函数 的方式可以避免上述问题,但是大多数情况下它是无关紧要的。 (在上述代码中jsx的注释中为ref的内联回调函数写法,然后可以写成class的绑定函数,当页面更新的时候就不会再执行了)
createRef()
的使用
React最为推荐的ref的使用方式
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 class Demo extends React.Component { myRef = React .createRef (); render ( ){ return ( <div > <input ref ={this.myRef} type ="text" /> <button onClick ={this.showInfo} > 点我</button > </div > ) } showInfo = () => { const input = this .myRef .current ; alert (input.value ); } }
事件处理
通过onXxx
属性指定事件处理函数(注意大小写)。
React使用的是自定义(合成)事件,而不是使用原生的DOM事件。
React中的事件是通过事件委托方式处理的(委托给组件最外层的元素)——为了更高的效率。
通过event.target
得到发生事件的DOM元素。(当发生事件的元素和处理事件的元素是同一个的时候就可以避免使用ref
)
收集表单数据
相当于vue中的 v-bind和v-model 的区别
非受控组件
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 class Login extends React.Component { handleSubmit = (event ) => { event.preventDefault (); const {username,password} = this ; console .log (username.value ,password.value ); } render ( ){ return ( <form onSubmit ={this.handleSubmit} > 用户名:<input ref ={c => this.username = c} type="text"/> 密码:<input ref ={c => this.password = c} type="password"/> <button > 登录</button > </form > ) } }ReactDOM .render (<Login /> ,document .getElementById ('root' ));
受控组件
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 class Login extends React.Component { state = { username :'' , password :'' } saveUsername = (event )=> { this .setState ({username :event.target .value }); } savePassword = (event )=> { this .setState ({password :event.target .value }); } handleSubmit = (event ) => { event.preventDefault (); const {username,password} = this .state ; console .log (username,password); } render ( ){ return ( <form onSubmit ={this.handleSubmit} > 用户名:<input onChange ={this.saveUsername} type ="text" /> 密码:<input onChange ={this.savePassword} type ="password" /> <button > 登录</button > </form > ) } }ReactDOM .render (<Login /> ,document .getElementById ('root' ));
代码优化
关于事件回调的说明:
1.在事件回调中如果传入参数写成onChange={this.saveFormData(username)}
的形式的话,会返回这个函数执行完的返回值作为事件回调。
2.onChange={this.saveFormData}
则是将整个函数作为事件回调。
高阶函数:
如果一个函数接受的参数为函数或者调用的返回值为函数,那么这个函数可以被称为高阶函数。
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 class Login extends React.Component { state = { username :'' , password :'' } saveFormData (dataType ){ return (event ) => { this .setState ({[dataType]:event.target .value }); } } handleSubmit = (event ) => { event.preventDefault (); const {username,password} = this .state ; console .log (username,password); } render ( ){ return ( <form onSubmit ={this.handleSubmit} > 用户名:<input onChange ={this.saveFormData( 'username ')} type ="text" /> 密码:<input onChange ={this.saveFormData( 'password ')} type ="password" /> <button > 登录</button > </form > ) } }ReactDOM .render (<Login /> ,document .getElementById ('root' ));
不使用柯里化的写法
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 class Login extends React.Component { state = { username :'' , password :'' } saveFormData (dataType,event ){ this .setState ({[dataType]:event.target .value }); } handleSubmit = (event ) => { event.preventDefault (); const {username,password} = this .state ; console .log (username,password); } render ( ){ return ( <form onSubmit ={this.handleSubmit} > 用户名:<input onChange ={ event => this.saveFormData('username',event)} type="text"/> 密码:<input onChange ={ event => this.saveFormData('password',event)} type="password"/> <button > 登录</button > </form > ) } }ReactDOM .render (<Login /> ,document .getElementById ('root' ));
生命周期(旧)
初始化阶段:由ReactDOM.render()
触发
constructor()
componentWillMount()
render()
componentDidMount()
(初始化,如开启定时器、发送网络请求、订阅消息)
更新阶段:由组件内部this.setState()
或父组件重新render
时触发
shouldComponentUpdate()
,需要返回true
或false
componentWillUpdate()
render()
componentDidUpdate()
卸载组件:由React.unmountComponentAtNode()
触发
componentWillUnmount()
(收尾工作,关闭定时器、取消订阅消息)
生命周期(新)
删去
componentWillUpdate()
componentWillMount()
componentWillReceiveProps()
新增
getSnapshotBeforeUpdate()
static getDerivedStateFromProps()
React中proxy
package.json
中可以直接配置一个代理。
src
中新建setupProxy.js