React高频困惑点解疑

为何要引入React

import React from 'react';

function A() {
  return (
    <h1>鱼鱼dev</h1>
  )
}

​ 在这里咱们并无使用到React变量,那为何还要引用React进来呢?javascript

​ 能够试一下,若是咱们省略了React的引入操做import React from 'react';,就会报如下的错误:报错信息java

​ 咱们通过babel转化后,会将上述代码转换成:react

function A() {
  return React.createElement("h1", null, "鱼鱼dev");
}

​ 因此能够看出来,JSX只是React.createElement(component, props, ...children)的语法糖。es6

为何要用className而不用class

​ React一开始的理念是想与浏览器的DOM API保持一致,而不是与HTML保持一致。因此更愿意选择DOM API中的className属性。浏览器

为何属性要用小驼峰

这里提一句,驼峰命名分为大驼峰和小驼峰,大驼峰为首字母为大写,其他每一个单词首字母大写。小驼峰为首字母小写,其他每一个单词首字母小写。还有另外两种主流的命名规则,一是下划线链接符,一是横杠链接符。

​ 同上,React的理念是与浏览器的DOM API保持一致,也就使用了DOM API中的小驼峰命名的风格。babel

为何constructor里要调用super以及传入props

​ 这里有两个问题dom

  • 为何constructor里必需要调用super
  • 为何super里必须传入props

为何constructor里必需要调用super

​ 其实这不是React的限制,而是JavaScript的限制。要想在构造函数里调用this,必须得在此以前调用过super。这里的缘由涉及到了JavaScript的”继承“。函数

​ 如若没有调用super:如若没有调用super性能

​ JavaScript是经过原型链来实现继承的,在此咱们不深究原型链,单说在原型链中super的意义。super指代着父类的构造函数,在子类的构造函数中调用super(),则意味着调用了父类的构造函数,从而使得建立子类的实例时,不光有子类的属性,还会有父类的属性this

​ 因此说,如过说在子类的构造函数中没有调用super的话,则子类的实例中不会有父类的属性值。这就使得这个继承名存实亡了。因此JavaScript规定继承时,必须得在子类的构造函数中调用super

为何super里必须传入props

​ 你必须得在super调用时传入props才能在构造函数中使用this.props,不然使用this.props会报undefined没有这个属性。不过在构造函数以后,如render函数中却能在其中使用this.props

​ 为何呢?

​ 这是由于在React的构造函数被调用以后,会给建立的React实例对象绑入一个props属性,其值就是props

const instance = new YourComponent(props);
instance.props = props

​ 不过因为是在构造函数被调用后,才绑如props属性,也就是说在构造函数执行时,this是没有props这个尚需经的。

​ 因此说,仍是得在super中传入props,不然就没法在构造函数中使用this.props

class App extends React.Component {
  constructor (props) {
    super(props);// 既调用了super,又传入了props
    console.log(this.props);// 能够访问到值,而且不会报错
  }
  render () {
    console.log(this.props);// 能够访问到值
    return <h1>鱼鱼dev</h1>
  }
}
class App extends React.Component {
  constructor (props) {
    super();// 调用了super,不传入props
    console.log(this.props);// undefined
  }
  render () {
    console.log(this.props);// 能够访问到值
    return <h1>鱼鱼dev</h1>
  }
}
class App extends React.Component {
  constructor (props) {
      // 既不调用super,也不传入props
    console.log(this.props);// 这里就直接报错了
  }
  render () {
    console.log(this.props);
    return <h1>鱼鱼dev</h1>
  }
}

​ 得益于React中的babel的强大,babel提供了es6中都不支持的实例属性的写法,也就是说实例属性你不光能够在构造函数中声明,还能够像这样写:

class A {
    a = '1'
}

​ 而若是用这种写法,也就不用在类中写构造函数了。因此继承也不用手动调用super来继承父类的实例属性,默认就帮你调用好了。

class A {
  a = '1'
}
class B extends A {
  b = '2'
}
console.log(new B().a);// '1'

​ 因此React中能够这样写:

class App extends React.Component {
  render () {
    console.log(this.props);// 有值
    return <h1>鱼鱼dev</h1>
  }
}

为何组件名要用大写开头

​ 前面有提到,JSX是React.createComponent(component, props, ...children)的语法糖,在这里component的类型能够是stringReactClasstype

  • 当component值为string类型,react会以为他是原生dom节点
  • 当component值为ReactClasstype,react会以为他是自定义组件

这是在React.createComponent(component, props, ...children),而在JSX中是如何区别是string仍是ReactComponent呢?若是是大写开头,是ReactComponent,若是是小写开头,是string

function A() {
    // 小写开头,React认为它是原生dom节点。babel后
    // 为React.createComponent("div",null);
    return <div></div>
}
import MyComponent from './myComponent.js';
function A() {
    // 大写开头,React认为他是自定义组件。babel后
    // 为React.createComponent(MyComponent,null);
    return <MyComponent></MyComponent>
}
import myComponent from './myComponent.js';// 这里有个改动,引入自定义组件名小写开头
function A() {
    // 小写开头,React认为它是原生dom节点。babel后
    // 为React.createComponent("myComponent",null);
  // 但因为dom节点中没有myComponent,则报错
    return <myComponent></myComponent>
}

若你自己是想用ReactComponent,但却一小写开头:

若你自己是想用ReactComponent,但却一小写开头

为何调用方法要 bind this

​ 相信若是你们有研究过JavaScript的this指向问题的话,都知道像下面这种状况,函数中的this指向会丢失:

const a = {
    func: function () {
        console.log(this);
    }
}
const nextA = a.func;
nextA();// 打印undefined(严格模式下)
a.func();// 打印{func: f}

​ 这就是你们常说的this指针丢失的问题。

​ React中的事件绑定也是这样的:

class Foo extends React.Component {
  handleClick () {
    this.setState({ xxx: aaa })
  }

  render() {
    return (
      <!-- 在这里我绑定事件未bind(this),结果是当触发click事件,执行handleClick函数时,内部this指向为undefined -->
      <button onClick={this.handleClick}>
        Click me
      </button>
    )
  }
}

​ 这是由于onClick={this.handleClick}其实是分为两步的:

const handleClick = this.handleClick;
...onClick = handleClick

​ 这就发生了上文所说的this指针丢失。

​ 因此必须得在一开始就肯定死this指向,以保证即便执行了const handleClick = this.handleClick;`...onClick = handleClick`也不会发现this指针丢失的现象。

​ 具体方法有一下两种:

class Foo extends React.Component {
  handleClick () {
    this.setState({ xxx: aaa })
  }

  render() {
    return (
      // 在绑定的时候再bind(this)。性能不是很好,多处地方调用同一
      // 函数的话,得重复bind
      <button onClick={this.handleClick.bind(this)}>
        Click me
      </button>
    )
  }
}
class Foo extends React.Component {
    constructor () {
    // 在构造函数里bind(this)。缺点就是写起来不顺手......
    // 没有人会习惯在类里声明了函数,再去构造函数里去bind一次把
        this.handleClick = this.handleClick.bind(this);
    }
  handleClick () {
    this.setState({ xxx: aaa })
  }

  render() {
    return (
      <button onClick={this.handleClick}>
        Click me
      </button>
    )
  }
}

​ 除这两种方法,还有一种方法:箭头函数能够解决这一问题(箭头函数的this指向彻底继承于上一做用域的this指向),箭头函数又有两种写法:

class Foo extends React.Component {
    constructor () {
    // 在构造函数里bind(this)。缺点就是写起来不顺手......
    // 没有人会习惯在类里声明了函数,再去构造函数里去bind一次把
        this.handleClick = this.handleClick.bind(this);
    }
  handleClick () {
    this.setState({ xxx: aaa })
  }

  render() {
    return (
        // 至关于每次点击事件的时候就声明一个匿名函数,这个比第一种方法还费性能
      <button onClick={e => {this.handleClick}}>
        Click me
      </button>
    )
  }
}
class Foo extends React.Component {
  // 这是我最喜欢的写法了,美观而又省性能
  handleClick = () => {
    this.setState({ xxx: aaa })
  }

  render() {
    return (
      <button onClick={this.handleClick}>
        Click me
      </button>
    )
  }
}
可能你们会想,为何React不本身bind呢?由于每次调用的时候,都bind一次,会影响性能,倒不如一开始就bind好,而后在调用时候直接调用。
相关文章
相关标签/搜索