React高频困惑点解疑

为什么要引入React

import React from 'react';

function A() {
  return (
    

鱼鱼dev

) }

​ 在这里我们并没有使用到React变量,那为什么还要引用React进来呢?

​ 可以试一下,如果我们省略了React的引入操作import React from 'react';,就会报以下的错误:

​ 我们经过babel转化后,会将上述代码转换成:

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

​ 所以可以看出来,JSX只是React.createElement(component, props, ...children)的语法糖。

为什么要用className而不用class

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

为什么属性要用小驼峰

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

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

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

​ 这里有两个问题

  • 为什么constructor里必须要调用super
  • 为什么super里必须传入props

为什么constructor里必须要调用super

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

​ 如若没有调用super:

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

​ 所以说,如过说在子类的构造函数中没有调用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 

鱼鱼dev

} }
class App extends React.Component {
  constructor (props) {
    super();// 调用了super,不传入props
    console.log(this.props);// undefined
  }
  render () {
    console.log(this.props);// 可以访问到值
    return 

鱼鱼dev

} }
class App extends React.Component {
  constructor (props) {
      // 既不调用super,也不传入props
    console.log(this.props);// 这里就直接报错了
  }
  render () {
    console.log(this.props);
    return 

鱼鱼dev

} }

​ 得益于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 

鱼鱼dev

} }

为什么组件名要用大写开头

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

若你本身是想用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 (
      
      
    )
  }
}

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

  render() {
    return (
      
    )
  }
}

​ 除这两种方法,还有一种方法:箭头函数可以解决这一问题(箭头函数的this指向完全继承于上一作用域的this指向),箭头函数又有两种写法:

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

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

  render() {
    return (
      
    )
  }
}
可能大家会想,为什么React不自己bind呢?因为每次调用的时候,都bind一次,会影响性能,倒不如一开始就bind好,然后在调用时候直接调用。

你可能感兴趣的:(react.js)