初探React中函数组件和类组件的差异

特别声明:为感谢社区广大朋友对小站的支持,自2019年10月1日至2019年11月11日开通年费VIP通道,年费价格为 ¥299.00元。如果您喜欢小站的内容,可以点击开通会员进行全站阅读。如果您对付费阅读有任何建议或想法,欢迎发送邮件至: airenliao@gmail.com!(^_^)

自从React Hooks的出来,社区讨论Hooks的越来越多。这并不是说React Hooks就优于类组件,但是使用Hooks来构建组件时有一个巨大的可用性提升,特别是因为这些函数组件可以通过React Hooks中的钩子函数来访问状态和生命周期。

今天我们就来一起聊聊如何将React的类组件转换为函数组件,用React Hooks中的钩子函数替换类组件中的setState和生命周期方法,比如componentWillMountcomponentWillReceiveProps等。

因此,让我们首先使用状态和生命周期方法构建一个基于类的React组件。也是大家最为熟悉的ToDoList组件。该组件具备:

  • 有一个文本输入框(<input type="text" />,用户可以在输入框中输入想要的内容
  • 有一个“添加列表项”按钮(button,点击该按钮之后可以将文本输入框的内容添加到列表中(ToDoList中)
  • 显示每个待办事项的列表清单
  • 每个单独的列表项目都有一个相关联的复选框(<input type="checkbox" />),可以用来将列表项标记为已完成
  • 列表项会存储到浏览器的缓存中(本地存储),并在应用程序启动时从本地存储中再次加载

我们的组件将使用statecomponentDidMountcomponentDidUpdategetDerivedStateFromProps生命周期方法。其中一些生命周期方法(比如getDerivedStateFromProps)将以一种非人为方式使用,以便能够演示有哪些Hooks的钩子函数可以替换这些生命周期的方法。

在开始之前,先来学习关于类和函数相关的知识点。

如何区分类和函数

作为Web开发者,经常和函数打交道。但要真正的理解和掌握他们也不是件易事,特别是对于初学JavaScript的同学更是如此。至少给我自己的感觉是如此。

在这里我们不会深入的去聊函数和类,因为要真正的聊透他们,都可以去写本书了。由于我们今天要聊React的类组件和函数组件,那么在开始之前很有必要的先了解一顶点有关于JavaScript的函数和类。先来看函数吧。

函数在JavaScript中被认为是第一类公民,在JavaScript中明确的创建函数的概念非常重要。

JavaScript语言似乎和其他编程语言不同,我们可以在JavaScript中以不同的方式来创建一个函数,常见的方式主要有:

用几个简单的示例代码来演示他们之间的不同:

// Function Declaration
function Greeting(user) {
    console.log(`Hello, ${user}`)
}

Greeting('@w3cplus') // » Hello, @w3cplus

// Function Expression
const Greeting = function(user) { // 作为对象分配给变量
    console.log(`Hello, ${user}`)
}

const Methods = {
    numbers: [1, 2, 8],
    // Function Expression
    sum: function() { // 在对象上创建一个方法
        return this.numbers.reduce(function(acc, num){ // Function Expression (使用该函数作为回调函数)
            return acc + num
        })
    }
}

// Shorthand Method Definition
const Collection = { // 用于Object Literals和ES6 Class声明中
    items: [],
    // 使用函数名来定义
    // 使用一对圆括号中的参数列表和一对花括号来分隔主体语句
    add(...items) { 
        this.items.push(...items)
    },
    get(index) {
        return this.items[index]
    }
}

// Arrow Function
let empty = () =>{}

let simple = a => a > 15 ? 15 : a

let max = (a, b) => a > b ? a : b

let numbers = [1, 2, 3, 4]
let sum = numbers.reduce((a, b) => a + b)
let even = numbers.filter(v => v % 2 == 0)
let double = numbers.map(v => v * 2)

primise.then( a => {
    // ...
}).then(b => {
    // ...
})

// Generator Function
// JavaScript中的生成器函数返回这个生成器的迭代器对象

function* indexGenerator() {
    var index = 0
    while(true) {
        yield index++
    }
}

const indexGenerator = function* () {
    var index = 0
    while(true) {
        yield index++
    }
}

const obj = {
    *indexGenerator() {
        var index = 0
        while(true) {
            yield index++
        }
    }
}

// Function Constructor
const sum = new Function('a', 'b', 'return a + b')
sum(1, 2) // » 3

类是ES6中开始引入的,实质上是JavaScript现有的基于原型的继承的语法糖。实际上,类是特殊的函数,就像你能够定义的函数表达式和函数声明一样,类语法主要有两个组成部分:类表达式类声明

// 类声明
class Rectangle {
    constructor(height, width) {
        this.height = height
        this.width = width
    }
}

// 类表达式

// 匿名类
let Rectangle = class {
    constructor(height, width) {
        this.height = height
        this.width = width
    }
}

// 命名类
let Rectangle = class Rectangle {
    constructor(height, width) {
        this.height = height
        this.width = width
    }
}

而且还可以使用extends关键字在类声明或类表达式中用于创建一个类作为另一个类的子类:

class Animal {
    constructor(name) {
        this.name = name
    }

    sayHi() {
        console.log(this.name)
    }
}

class Dog extends Animal {
    sayHi() {
        console.log(`${this.name} barks.`)
    }
}
let dog = new Dog('Mitzie')
dog.sayHi() // » Mitzie barks

如果子类中存在构造函数,则需要在使用this之前首先调用super()。也可以扩展传统折基于函数的“类”

function Animal(name) {
    this.name = name
}

Animal.prototype.sayHi = function() {
    console.log(this.name)
}

class Dog extends Animal {
    sayHi() {
        super.sayHi()
        console.log(`${this.name} barks.`) 
    }
}

let dog = new Dog('Mitzie')
dog.sayHi() 

如果你想更深入的了解有关于JavaScript中的函数和类相关的知识的话,可以花点时间阅读下面相关文章:

我们回到React的世界当中来。在React中我们可以以函数形式定义一个组件,比如像下面这样:

function SayHi() {
    return <p>Hello, React</p>
}

也可以将SayHi这个组件以类的形式来定义:

class SayHi extends React.Component {
    render() {
        return <p>Hello, React</p>
    }
}

在当你要使用一个组件时,比如要使用SayHi这个组件,并不会过多的关注它是以什么方式来定义(声明)的组件,只会关心如何使用:

<SayHi />

虽然使用者不会太过关注它是怎么创建的(以哪种方式创建的),但React自身对于怎么创建组件是较为关注也会在意其差别。

如果SayHi是一个函数,React需要调用它:

// 你的代码
function SayHi() {
    return <p>Hello, React</p>
}

// React内部
const result = SayHi(props) // » <p>Hello, React</p>

如果SayHi是一个类,React需要先用new操作符将其实例化,然后调用刚才生成实例的render方法:

// 你的代码
class SayHi extends React.Component {
    render() {
        return <p>Hello, React</p>
    }
}

// React内部
const instance = new SayHi(props) // » SayHi {}
const result = instance.render()  // » <p>Hello, React</p>

无论哪种情况,React的最终目标是去获取渲染后的DOM节点,比如SayHi组件,获取渲染后的DOM节点是:

<p>Hello, React</p>

具体需要取决于SayHi组件是怎么定义的。

从上面的代码中你可能已经发现了,在调用类时,使用了new关键字来调用:

// 如果SayHi是一个函数
const result = SayHi(props); // » <p>Hello, React</p>

// 如果SayHi是一个类
const instance = new SayHi(props) // » SayHi {}
const result = instance.render()  // » <p>Hello, React</p>

那么JavaScript中的new起什么作用呢?在ES6之前,JavaScript是没有类(class)这样的概念。在这种情况之前如果要使用类这样的特性都是使用普通函数来模拟。即,在函数调用前加上new关键字,就可以把任何函数当做一个类的构造函数来用

function Fruit(name) {
    this.name = name
}

const apple = new Fruit('apple') // » Fruit {name: "apple"}
const banana = Fruit('banana')   // » undefined   

JavaScript中的new关键字会进行如下的操作:

  • 创建一个空的对象,即{}
  • 链接该对象(即设置该对象的构造函数)到另一个对象
  • 将创建的对象作为this的上下文
  • 如果该函数没有返回对象,则返回this

正如上面的示例来说:

  • 调用Fruit('apple')时前面添加了new关键字,这个时候JavaScript会知道Fruit只是一个函数,同时也会假装它是一个构造函数。会创建一个空对象({}并把Fruit中的this指向那个对象,以便我们可以通过类似this.name的形式去设置一些东西,然后把这个对象返回
  • 调用Fruit('banana')时前面没有添加new关键字,其中的this会指向某个全局且无用的东西,比如windowundefined,因此代码会崩溃或者做一些像设置window.name之类的傻事

也就是说:

// 和Fruit中的this是等效的对象
const apple = new Fruit('apple') // » Fruit {name: "apple"}

new关键字同时也把放在Fruit.prototype上的东西放到了apple对象上:

function Fruit(name) {
    this.name = name
}

Fruit.prototype.SayHi = function () {
    console.log(`Hi,我想吃${this.name}`)
}

const apple = new Fruit('苹果')

apple.SayHi() // » Hi,我想吃苹果

这就是在JavaScript中如何通过new关键字来模拟类的方式。有关于new更多的介绍可以阅读:

剩余80%内容付费后可查看

如需转载,烦请注明出处:https://www.w3cplus.com/react/class-component-vs-function-component.html

如果文章中有不对之处,烦请各位大神拍正。如果你觉得这篇文章对你有所帮助,打个赏,让我有更大的动力去创作。(^_^)。看完了?还不过瘾?点击向作者提问!

赏杯咖啡,鼓励他创作更多优质内容!
返回顶部