为什么要引入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
的类型可以是string或ReactClasstype。
- 当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好,然后在调用时候直接调用。