React是由Facebook进行维护的。
React是什么?
React是一个声明式,高效且灵活的用于构建用户界面的JavaScript库,使用React可以将一些简短、独立的代码片段组合成复杂的UI界面,这些代码片段被称为”组件“。
React语法
jsx语法
- React框架主要使用jsx语法(js混合html一起书写的)。但是它又和html语法不太一样,和XML语法比较相似。
①标签必须要有结束符号(例如:<input />
,无论是单标签还是双标签都要有这个闭合符号/
)。
②标签的属性一律使用驼峰命名法(例如:html5中的data-index
属性在这里提倡dataIndex
这种写法)。
③类名必须用className进行命名,才会生成class。 (注意:vue.js框架也支持jsx语法) - jsx语法的本质?
<!--使用jsx语法 -->
<script type='text/babel'>
const title = <h1>我是大标题</h1>
ReactDOM.render(title,document.getElementById('root'))
</script>
//不使用jsx语法
//上面的jsx语法会最终编译成下面的代码
<script>
const title = React.createElement('h1',{
className: 'title',
style: {
color:#f00
}
},'我是大标题');
ReactDOM.render(title,document.getElement('root'))
</script>
打印出title的值:console.log(title)
注意:vue中也有这样的写法)
3. jsx语法上的一些api?
<script type='text/babel'>
const str = '我是字符串';
const num = 998;
//在jsx语法中,凡是false相关的值,默认不渲染,这一点是和vue相一致的。
//如果想要让undefined显示出来,只需要将它转为字符串即可。例如:
//const none = String(undefined)
const none = undefined; //false,none等也不显示
const person = {
firstName: '唐',
lastName: '老鸭'
};
function getFullName(firstName,lastName) {
return firstName + lastName
};
const sexNum = 1;
const element = (
<div>
<ul>
<li>{str}</li>
<li>{num}</li>
<li>{none}</li>
<li>{getFullName(person.firstName,person.lastName)}</li>
<li>我是一个{sexNum == 1 ? '男' : '女'}孩</li>
</ul>
</div>
)
ReactDOM.render(element,document.getElementById('root'))
</script>
得到如下结果:
例子:用react实现if方法
<script type='text/babel'>
const sexNum = 1;
let str = '';
if(sexNum == 1) {
<!--想让它具有vue中v-if类似的效果,可以直接设置为-->
<!--none,false,undefined-->
str = '我是男孩'
}else {
str = '我是女孩'
}
const element = {
<div>{str}</div>
}
ReactDOM.render(element,document.getElementById('root'))
</scritp>
元素的渲染
react高性能的优势:使用一个对象去描述一个dom。
diff算法:
例如:
//原始的jsx
{
type: 'p',
props: {
children: '2019/10/29 下午 17:40:14'
}
}
//新的对象
{
type: 'p', //这一部分因为一样,不需要修改
props: {
//只需要修改这一部分
children: '2019/10/29 下午 17:40:30'
}
}
//diff算法 ==》得出需要修改的最小部分为children
//==》p.innerText='新的children'
这就是react内部的原理。
组件
- 组件渲染和元渲染的不同点:
①元素更偏向于html/css部分。
②组件更偏向于将html/css/js逻辑层封装到一起。
③组件可以进行复用,而元素不可以。
//元素渲染
let data = new Date();
let day = data.getDay();
const element = (
<div>
<h1>今天是星期{day}</h1>
</div>
)
ReactDOM.render(element,getElementById('root'))
//组件渲染在react有两种写法,组件一律使用大写字母开头。
//第一种:用函数,函数是最简单的组件。
function Element () {
let data = new Date();
let day = data.getDay();
return (
<h1>今天是星期{day}</h1>
)
}
function Container () {
return (
<div>
<h1>这是描述时间的页面</h1>
<Element />
<Element />
</div>
)
}
ReactDOM.render(<Container />,document.getElementById('root'))
//父子传值
function Element (params) {
console.log(params)
return (
<div>
<h1>今天是星期{params.day}</h1>
</div>
)
}
function Container () {
let data = new Date();
let day = data.getDay();
return (
<div>
<h1>正在进行父子传值</h1>
<Element day={day} abc='1,2,3'/>
<Element day={day}>
</div>
)
}
ReactDOM.render(<Container />,document.getElementById('root'))
//注意:如果想要修改后的变量值重新渲染到页面上,必须重新调用
//render函数,否则只是会修改,但不会重新渲染到页面上。但这是函数组件无法做到的。
//第二种组件渲染:类组件
<script type='text/babel'>
//使用解构赋值 const {Component} = React
class Clock extends React.Component {
//class Clock extends Component
constructor (props) {
super (props)
}
}
render () {
return (
<div>
<h1>今天是星期{this.props.day}</h1>
</div>
)
}
class Container extends React.Component {
constructor (props) {
super(props)
this.state = {
day: new Date().getDay()
}
}
changeDay () {
console.log(this.state.day)
this.setState = ({//可以把它当做是一个异步操作
day:4
//如果想要获得实时更改后的值,可以通过一个回调函数。
},function(){
console.log(this.state.day)
})
}
render () {
return (
<div>
<h1>我是显示时间的页面</h1>
<button onClick={this.changeDay.bind(this)}>修改</button>
<Clock day={this.state.day}/>
<Clock day={this.state.day}/>
</div>
)
}
}
ReactDOM.render(<Container/>,document.getElementById('root'))
//'React.Component'每次写这么长的代码比较麻烦,我们可以使用
//es6解构赋值:const {Component} = React
</script>
在react中,想要重新渲染页面,只有一个方法,那就是重新执行render函数。
在类组件中,存在一些生命周期,修改某一项值,会触发render的修改。
①修改类组件中的state(setState)
会触发页面的更新。
②修改类组件中的props
会触发组件的更新。
注意:
函数组件无法进行页面的重新渲染,类组件通过修改某一项值可以进行页面的重新渲染。
React生命周期(定义组件都以类的方式去定义组件,因为类组件有生命周期,函数组件没有生命周期)
React生命周期主要包含三个阶段:初始化阶段、运行中阶段和销毁阶段,在React不同的生命周期里,会依次触发不同的钩子函数。
一、初始化阶段
- 设置组件的默认属性
static defaultProps = {
name: 'sls',
age:23
};
//or
Counter.defaltProps = {name:'sls'}
- 设置组件的初始化状态
constructor () {
super();
this.state = {number:0}
}
- componentWillMount()
组件即将被渲染到页面之前触发,此时可以进行开启定时器、
向服务器发送请求等操作
- render()
组件渲染
- componentDidMount()
组件已经被渲染到页面中后触发:此时页面中有了真正的DOM元素,
可以进行DOM相关的操作
二、运行中阶段
- componentWillReceiveProps()
组件接收到属性时触发
- shouldComponentUpdate()
当组件接收到新属性,或者组件的状态发生改变时触发。
组件首次渲染时并不会触发。
shouldComponentUpdate(newProps,newState) {
if(newProps.number < 5) return true;
return false
}
//该钩子函数可以接收到两个参数,新的属性和状态,
//返回true/false来控制组件是否需要更新。
一般我们通过该函数来优化性能:
一个React项目需要更新一个小组件时,很可能需要父组件更新自己
的状态。而一个父组件的重新更新会造成它旗下所有的子组件重新执
行render()方法,形成新的虚拟DOM,再用diff算法对新旧虚拟DOM
进行结构和属性的比较,决定组件是否需要重新渲染。
但是这样的操作会造成很多性能浪费,所以我们开发者可以根据项目
的业务逻辑,在shouldComponentUpdate()中加入条件判断,从而
优化性能。
例如React中就提供了一个PureComponent的类,当我们的组价
继承于它时,组件更新时就会默认先比较新旧属性和状态,从而
决定组件是否更新。值得注意的是,PureComponent进行的是浅比
较,所以组件状态或属性改变时,都需要返回一个新的对象或数组
。
- componentWillUpdate()
组件即将被更新时触发
- componentDidUpdate()
组件被更新完成后触发。页面中产生了新的DOM的元素,可以进行DOM操作。
三、销毁阶段
- componentWillUnmount()
组件被销毁时触发。这里我们可以进行一些清理操作,例如清理定时器,取消Redux的订阅事件等等。
四、整个生命周期代码
<script type='text/babel'>
const {Component} = React
class SubCounter extends Component {
componentWillReceiveProps() {
console.log('9:componentWillReceiverProps:子组件将要接收到新的属性')
}
shouldComponentUpdate(newProps,newState){
console.log('10:shouldComponentUpdate:子组件是否需要更新')
if(newProps.number < 5) return true
return false
}
componentWillUpdate () {
console.log('11:componentWillUpdate:子组件将要更新')
}
componentDidUpdate () {
console.log('13:componentDidUpdate:子组件更新完成')
}
componentWilUnmount () {
console.log('14:componentWillUnmount:子组件将卸载')
}
render() {
console.log('12:render:子组件挂载中')
return (
<p>{this.props.number}</p>
)
}
}
class Counter extends Component {
static defaultProps = {
//1.加载默认属性
name: 'sls',
age:23
}
constructor (props) {
super(props);
//2.加载默认状态
this.state = {number:0}
}
componentWillMount () {
console.log('3:componentWillMount:父组件挂载之前')
}
componentDidMount () {
console.log('5:componentDidMount:父组件挂载完成')
}
shouldComponentUpdate(newProps,newState) {
console.log('6:shouldComponentUpdate:父组件是否需要更新')
if(newState.number < 5) return true
return false
}
componentWillUpdate () {
console.log('7:componentWillUpdate:父组件将要更新')
}
componentDidUpdate () {
console.log('8:componentDidUpdate:父组件更新完成')
}
handleClick = function () {
this.setState ({
number:this.state.number + 1
})
}
render () {
console.log('4:render:父组件挂载')
return (
<div>
<p>{this.state.number}</p>
<button onClick = {this.handleClick.bind(this)}>点我+1</button>
{this.state.number < 10 ? <SubCounter number={this.state.number}/> : null}
</div>
)
}
}
</script>
进行打印:
this指向
this的几种不同的使用场景,也就是函数调用时this的指向:
①纯函数调用this指向Window。
②事件调用:谁调用这个函数,this就指向谁。
③定时器调用:在定时器中,this指向window。
④构造函数调用:构造函数生成一个新的对象,this指向新建的这个对象。
apply()、call()、bind()三者都能改变this的指向,且第一个参数都是this的指向对象。第二个参数有所区别:
call的第二个参数往后:call的参数是直接放进去的,第二第三第n个参数全都用逗号分隔,直接放到第一个参数后面。
apply的第二个参数往后:所有参数都必须放进一个数组里面传进去。
bind除了返回的是一个新函数以外,你必须调用它才会被执行。它的参数和call一样。
当然,三者的参数不限定是String,允许是各种类型,包括函数、object等等!
实例:
<!--this永远指向调用它的对象-->
<!--this的指向只有调用的时候才知道-->
<!--箭头函数的this的指向是在定义的时候就已经绑定了-->
<script>
var name = 'window 的name'
var obj = {
name: '王力宏'
printName () {
console.log(this.name)
}
}
obj.printName() //王力宏
const fn = obj.printName //引用数据类型
//fn只是保存了obj.printName这个引用数据类型的地址
fn() //window 的name fn()==>window.fn()
</script>
react核心代码之this指向
<body>
<button id='btn'>点我</button>
</body>
<script>
var name = 'window 的name'
class Demo {
constructor () {
this.name = '我是demo'
this.demo = document.getElementById('btn')
const _this = this
//this.demo.onclick = function() {
// _this.printSome()
// }
//react核心代码之this指向
const fn = this.PrintSome
this.demo.onclick = function () {
fn.bind(_this)()
}
}
printSome () {
console.log(this)
}
}
</script>
事件处理中的this
'use strict' //严格模式,es6当中推出来的,在严格模式中,
//禁止变量未定义就直接使用,以及规定了,全局调用的函数的this的指向指向undefined。
注意:事件处理函数是没有办法传参的(在原生中,在vue或者react中,别人已经封装过了,可以传参)!
在react框架的事件处理函数中(例如:点击事件原生是onclick
全小写,而react使用驼峰命名法onClick
,vue是(@click
))。原因是事件处理函数已经被react框架进行了封装。原生的点击事件onclick
是无法进行传参的。而react和vue中的点击事件是可以传参的。
//以vue为例,进行源码的解读
<script>
let demo = {
template: `
<div>
<button @click = handleClick($event,123)></button>
</div>
`,
methods: {
handleClick($event,num) {
console.log($event,num)
}
}
}
let vm = new Vue({
el: '#app',
data: {},
components: {demo}
})
</script>
<!--vue和react封装的事件处理函数类似,此处以点击事件为例:-->
<!--const fn = this.handleClick()-->
<!--let btn = document.getElementById('btn')-->
<!--const _this = this-->
<!--btn.onclick = function () {-->
<!-- fn.call(_this,e,123)-->
<!--}-->