likes
comments
collection
share

React的发展历史和核心概念一览

作者站长头像
站长
· 阅读数 21

前言

新框架的诞生必定有它的历史背景,不会无中生有。早在2011年,Facebook 开发人员面临代码维护方面的一些问题。随着Facebook Ads 应用程序的功能多样化,团队需要更多的人来保持它的稳定性。随着时间的推移,代码量急剧增加,维护性和扩展性只靠增加开发人员是解决不了实质问题。应用程序变得难以处理,如面临着大量的级联更新,他们的代码需要紧急升级以提高开发效率。

Facebook项目有独立的数据模型,但堆积了大量的用户交互代码。因此,Jordan Walke构建了一个框架原型,使开发流程更加高效,这标志着React.js的诞生。

History

  • 2010 React的最初雏形

    1. Facebook将xhp引入其php堆栈并将其开源。
    2. Xhp基于xml语法提供可复合的组件, Facebook后来在React中引入了这种语法。
  • 2011 React的原型

    • Jordan Walke创建了FaxJS, 作为React试验阶段的产物,目前在github上还能看到。
          TestProject.PersonDisplayer = {
            structure : function() {
              return Div({
                classSet: { personDisplayerContainer: true},
          
                titleDiv: Div({
                  classSet: { personNameTitle: true },
                  content: this.props.name
                }),
          
                nestedAgeDiv: Div({
                  content: 'Interests: ' + this.props.interests
                }),
          
                ageDiv: Div({
                  content: 'Age: ' + this.props.age
                })
              });
            }
          };
    
  • 2012 面临挑战,开始新的尝试

    1. Facebook广告业务面临维护挑战,Jordan Walke基于FaxJS创建了React。
    2. 4月Instagram被Facebook收购,Instagram也希望采用React架构开发。由于React和业务耦合,Facebook面临将其开源的压力,当时大部分代码由Pete Hunt开发。
    3. 9月FaceBook CEO马克扎克伯格在TechCrunch会议上对外承诺将很快提供更好的移动体验。
  • 2013 重大发布

    1. 在JS ConfUS会议上,Jordan Walke对外介绍了React,并将其开源。
    2. 6月React可以在JSFiddle上使用
    3. 7月React、JSX支持在Ruby上使用
    4. 8月React、JSX支持在Python上使用
    5. 9月Pete Hunt在JSConfEU 2013上发表了基于React重新思考最佳实践的演讲。
    6. David Nolen 介绍了基于React的OM。解释了 React如何优于现有的其他方案,从而提高了对React 的认可度。
  • 2014 React快速扩展的一年,吸引更多的企业用户,如Netflix

    1. 年初的#reactjsworldtour会议上,React对外宣布开始建设社区,吸引更多的开发者
    2. 1月React给提供React Developer Tools,支持在Chrome上调试React应用
    3. 4月React London 2014会议召开,会议主题"如何构建响应式应用"
    4. ReactiveX.io诞生
    5. React Hot Loader发布,作为一个通用插件,支持React应用的热更新
  • 2015 React脱颖而出

    1. 年初Flipboard发布React Canvas
    2. 1月Netflix开始使用React
    3. 2月Airbnb使用React
    4. 2月React.js Conf 2015会议,Facebook发布第一版React Native
    5. 3月Facebook宣布React Native (IOS)开源
    6. 6月Dan Abramov、Andrew Clark发布React状态管理库Redux
    7. React Native(Android)发布
  • 2016 React成为主流框架

    1. 2月 React.js Conf 2016在旧金山召开
    2. 3月Mobx诞生
    3. Isaac Salier-Hellendag在React.js Conf上介绍了基于React开发的Draft.js富文本编辑器
    4. 11月基于React开发的Blueprint诞生,一个React UI组件库。
  • 2017 稳中发展的一年

    1. 年初Airbnb发布新的React开源库Sketch.app
    2. 4月在F8会议上宣布React Fiber开源
    3. 9月React 16发布,包含errors boundries、portals、fragements、Fiber架构、SSR等。
    4. 11月React 16.2.0发布,改进Fragments
  • 最近几年

    1. 2018年Iceland conference,发布React 16.3.0
    2. 2019年新的React DevTools发布
    3. 2020年发布17.0.0版本,支持React并发模式Concurrent Mode,处于试验阶段
    4. 2020年Dan Abramov等人提出Zero-Bundle-Size React Server Components
    5. 2022年3月React v18.0发布,提出并发渲染引擎( Concurrent Rendering Engine)、Server Side Components,通过可中断的渲染引擎提升用户交互体验

React优点

React基于JSX定义可重用组件,让开发变得更简单。另外,使用虚拟DOM来优化实际DOM的更新,从而加快渲染速度。以下是使用 React 的一些好处:

  • 可重用组件:使用React,您可以构建可在应用程序的多个位置使用的可重用组件。随着时间的推移,这使得开发和维护您的应用程序变得更加容易。
  • 快速渲染:React使用虚拟DOM(文档对象模型)来优化对实际DOM 的更新。这意味着当应用程序的状态发生变化时,React可以有效地更新DOM,从而加快渲染速度。
  • JSX:React引入JSX概念,它是JavaScript的语法扩展,允许您在JavaScript文件中编写类似HTML的代码。这可以更轻松地构建和理解您的UI交互定义。
  • 服务器端渲染:React可用于在服务器端渲染内容,为您的应用程序提供更快的初始加载时间,并提高您的应用程序对搜索引擎的性能。
  • 强大的开发者社区:React拥有庞大而活跃的开发者社区,您可以在需要时找到丰富的资源和支持。

学习React之前你需要了解什么

Virtual DOM

文档对象模型(DOM)是HTML和XML文档的编程接口。它将文档的结构表示为对象树,每个对象代表文档的一部分(例如元素或属性)。DOM允许程序访问和修改文档的内容和结构。

Virtual DOM是React引入的概念,它是实际 DOM 的轻量级内存表示。当加载React应用程序时,虚拟DOM会创建DOM树的虚拟表示,用于确定需要对实际DOM 进行的最少更改数量,最后更新用户界面。

这种通过Virtual DOM更新DOM的过程称为reconciliation。当Virtual DOM确定需要对实际DOM 进行更改时,它会创建一个新的Virtual DOM来表示用户界面的更新状态,然后将新树与先前的树进行比较以确定最小更新集合。当React组件的状态发生变化时,将更新Virtual DOM而不是实际DOM,这可以极大地提高依赖频繁更新的具有大量数据的应用程序的性能。

JavaScript基础

  • 基础的Javascript语法,包含变量、数组、对象、函数、闭包等概念。
  • 异步:React包含大量的async代码,因此需要了解Promise、async/await。
  • DOM文档对象模型:需要对DOM有清晰的了解,它是React更新UI交互的基础。
  • ES6+: React使用ES6+ Javascript语法,因此你需要了解箭头函数、Class、快速分隔符(spread operator)等概念。
  • 状态管理: 当开发大型应用时,通常需要基于组件式架构、状态管理(如Redux)来提升应用的开发效率、稳定性、扩展性。因此,也需要了解组价式架构、状态管理技术的理论知识。

React依赖的ES6特性

  • Arrow functions: 新的函数定义语法,函数的间接表达并且可读性高。
  • Classes:通过Javascript定义面向对象的类。
  • Modules: 按模块组织和复用业务代码, 使用import、export导入、导出模块,可通过CommonJS、AMD等定义模块,然后使用Webpack、Babel打包成目标系统可执行的代码。

React提供了哪些能力

JSX

JSX(JavaScript XML)是JavaScript的扩展,允许开发人员在他们的代码中编写类似HTML的语法。JSX提供的主要好处是它能够更简单、更快地编写复杂的用户界面。通过允许开发人员在与React组件相同的文件中使用类似HTML的语法,快速生成UI交互。

一个简单的标题组件, JSX代码<h1>Hello, React!</h1>用于创建标题元素。这类似于用HTML编写<h1>Hello, React!</h1>。

import React from 'react'; 
function Heading() { 
    return <h1>Hello, React!</h1>; 
} 
export default Heading; 

JSX元素也可以有属性,就像HTML元素一样。下面是一个使用自定义class属性呈现按钮的组件示例:

import React from 'react'; 
function CButton() { 
    return <button className="c-button">Click me</button>; 
} 
export default CButton; 

我们在JSX中使用className而不是class,因为class是JavaScript中的保留关键字。您还可以使用JSX 来呈现动态内容。下面是一个组件示例,它呈现作为props传递的项目列表:

import React from 'react'; 
function ProjectList({ items }) { 
    return ( 
        <ul> 
            {items.map((item) => ( 
                <li key={item.id}>{item.name}</li> 
            ))} 
        </ul> 
    ); 
} 
export default ProjectList; 

JSX必须先转译为JavaScript,然后才能在浏览器中运行。通常使用Babel工具来完成。使用此工具,您可以在React组件中编写JSX,并将其转换为浏览器可以理解的JavaScript。

State

状态是一个Javascript对象,它保存与React组件相关的数据和信息。可用于存储、管理和更新应用程序中的数据,进而允许对用户界面进行动态更改。例如,如果一个按钮需要根据用户输入更改其文本,那么这将通过使用输入的新值更新状态来完成。

组件还可以使用它们自己的状态以及其他元素的状态,以便在某些事件发生时显示相关信息或做出相应的响应。 状态可用于功能组件和类组件,下面是具有状态对象的类组件的示例:

import React, { Component } from 'react'; 
class StateComponent extends Component { 
    constructor(props){ 
        super(props); 
        this.state = { 
            count: 0 
        }; 
    } 
    render() { 
        return <h1>Count: {this.state.count}</h1>; 
    } 
} 
export default StateComponent; 

在此示例中,组件有一个状态对象state,该对象具有一个名为count的属性,初始化为0。组件使用render()方法在标题中呈现计数。另外,可使用setState()方法更新组件的状态。

import React, { Component } from 'react'; 
class StateComponent extends Component { 
    constructor(props){ 
    super(props); 
        this.state = { 
            count: 0 
        }; 
    } 

    incrementCount = () => { 
        this.setState({ 
            count: this.state.count + 1 
        }); 
    } 
    render() { 
        return ( 
            <div> 
                <h1>Count: {this.state.count}</h1> 
                <button onClick={this.incrementCount}>Increment</button> 
            </div> 
        ); 
    } 
} 
export default StateComponent; 

setState()是异步和批处理的,也就是说在短时间内多次调用setState()可以组合成一个更新。为避免任何潜在的错误,可使用setState()的回调函数以确保在执行任何其他逻辑之前状态已更新。

this.setState({ count: this.state.count + 1 }, () => { 
    console.log(this.state.count); 
}); 

Props

Props是属性的缩写,将数据从父组件传递到子组件并作为每个元素行为的输入的方法。类似于函数参数,用于将数据从父组件传递到子组件。它们还可以用于在不同元素之间传递状态,以便跟踪用户与 UI交互期间所做的更改。

import ChildComponent from './ChildComponent'; 
function ParentComponent() { 
    return <ChildComponent name="React" />; 
} 
export default ParentComponent; 

该示例中,父组件传递name属性给子组件,子组件接收name属性后可直接读取并在render函数中渲染至UI。

import React from 'react'; 
function ChildComponent({ name }) { 
    return <h1>Hello, {name}!</h1>; 
} 
export default ChildComponent; 

在ChildComponent中,可通过解构(destrcuting)方式获取局部属性。在父组件中,可通过快速分隔或属性键值对方式传递给子组件。

// 快速分隔符
const props = {name: "React", age: 25} 
<ChildComponent {...props}/> 

// 键值对形式
<ChildComponent name="React" age={25}/> 

需要注意的是,props是只读的,在子组件中不能更改props值。如果要更新state,应使用useState自定义状态。

生命周期

生命周期是React开发很重要的概念,一共包含3个阶段,每个阶段有对应的函数或事件被用于触发组件特殊的行为。以下为生命周期的三个阶段:

  • Mounting: 该阶段组件被初始化,render结果插入到DOM,生命周期方法constrcutor()、render()、DidMount()方法依次被调用。
  • Updating:当状态发生变化或者有用户交互,都会触发Updating。在该阶段,shouldComponentUpdate、componentDidUpdate、render方法将会被触发。
  • Unmounting:当组件从DOM上卸载,会触发Unmounting阶段,并且componentWillUnmount事件也会被执行。

生命周期过程包含的方法或事件:

  • constructor(props):在挂载之前constructor会被触发,用于初始化组件状态和事件。
  • componentDidMount(): 当组件被挂载和渲染之后触发,用于数据请求或服务订阅等副作用。
  • shouldComponentUpdate(nextProps, nextState): 在组件更新之前触发,通过返回的false或true控制是否需要re-render。
  • componentDidUpdate(prevProps, prevState): 当组件更新之后触发,用于执行获取新数据或更新服务等副作用。
  • componentWillUnmount(): 在组件卸载之前调用,执行清理任务,如取消服务订阅等。

事件

  • React提供的事件让开发人员可以很方便、高效地相应UI交互。
  • React允许开发人员可直接在组件或DOM元素上绑定事件Handler, 事件通过on[event]语法绑定处理。
  • React内置了常用的交互事件,如onMouseEnter、onChange、onFocus、onBlur、onKeyDown、onKeyUp、onSubmit等等
import React, { Component } from 'react'; 
class CButton extends Component { 
    handleClick = () => { 
        console. log('Button clicked'); 
    } 
    render() { 
        return <button onClick={this.handleClick}>Click me</button>; 
    } 
} 
export default CButton; 

Refs

ref 是一种直接访问DOM元素或React组件的方法。Refs允许开发人员将名称或ID与任何给定组件相关联,使他们能够在以后需要时引用它。这在处理动态或不可预测的用户输入(例如文本输入字段)时特别有用。

在 React中有两种创建引用的方法:使用 createRef 方法或使用useRef Hook。以下是使用createRef方法创建ref的示例:

import React, { Component } from 'react'; 
class CComponent extends Component { 
    constructor(props) { 
        super(props); 
        this.myRef = React.createRef(); 
    } 
    handleClick = () => { 
        this.myRef.current.focus(); 
    } 
    render() { 
        return ( 
            <div> 
                <input type="text" ref={this.myRef} /> 
                <button onClick={this.handleClick}>Focus</button> 
            </div> 
        ); 
    } 
} 
export default CComponent; 

在此示例中,ref在构造函数中创建,并分配给类的myRef属性。使用ref属性将ref传递给input元素。ref的当前属性用于访问底层DOM元素。handleClick方法将触发输入元素的焦点方法。

Keys

Key用于跟踪集合中的各个组件,以便可以轻松识别和有效地操作它们,而不会在元素之间造成不必要的重复或混淆。

class CList extends Component { 
    render() { 
        const items = [ 
            { id: 1, text: 'Item x' }, 
            { id: 2, text: 'Item y' }, 
            { id: 3, text: 'Item z' }, 
        ]; 
        return ( 
            <ul> 
                {items.map(item => ( 
                    <li key={item.id}>{item.text}</li> 
                ))} 
            </ul> 
        ); 
    } 
} 
export default CList; 

在此示例中,项目列表是使用map函数呈现的。每个项目都有一个用作键的唯一id属性,key应该在它的同级中唯一并且是稳定可预测的。不建议将数组index作为键,因为它们不是标识符,当元素添加到数组或从数组中删除时索引会发生变化。

Router

路由器允许开发人员创建SPA,这些应用程序可以轻松处理导航和 URL更改,无需重新加载整个页面或执行重定向。这有助于改善用户体验,允许它们在应用程序的不同部分之间无缝转换,同时仍然保持所有必要数据的完整性。

import React from 'react'; 
import { 
BrowserRouter as Router, 
Switch, 
Route, 
Link 
} from 'react-router-dom'; 
import Home from './Home'; 
import About from './About'; 
import Contact from './Contact'; 
export default function App() { 
    return ( 
        <Router> 
            <div> 
                <nav> 
                    <ul> 
                        <li> 
                            <Link to="/">Home</Link> 
                        </li> 
                        <li> 
                            <Link to="/about">About</Link> 
                        </li> 
                        <li> 
                            <Link to="/contact">Contact</Link> 
                        </li> 
                    </ul> 
                </nav> 
                <Switch> 
                    <Route path="/about"> 
                        <About /> 
                    </Route> 
                    <Route path="/contact"> 
                        <Contact /> 
                    </Route> 
                    <Route path="/"> 
                        <Home /> 
                    </Route> 
                </Switch> 
            </div> 
        </Router> 
    ); 
} 

上述示例中,BrowserRouter作为顶层组件被用作路由映射。Link组件用来创建连接,导航到不同的路由。Switch组件用来渲染匹配URL的第一个路由。Route组件定义路由项,将URL和组件绑定。

你可以使用useParams hook方法获取URL传递的参数:

import { useParams } from "react-router-dom"; 
function User() { 
    let { id } = useParams(); 
    return <h3>ID: {id}</h3>; 
} 
function App() { 
    return ( 
        <Router> 
            <div> 
                <nav> 
                    <Link to="/">Home</Link> 
                    <Link to="/user/42">User 42</Link> 
                </nav> 
                <Switch> 
                    <Route path="/user/:id"> 
                        <User /> 
                    </Route> 
                    <Route path="/"> 
                        <Home /> 
                    </Route> 
                </Switch> 
            </div> 
        </Router> 
    ); 
}

Flux

Flux是一个架构模式,用于管理React组件的单项数据流。它提供清晰的结构,将应用交互和UI交互层分离,使代码有比较清晰的结构,易于维护并且扩展性高。

Flux架构主要的组成部分为:

  • Actions:被用户或系统触发的某些事件行为,如button click。
  • Dispatcher:接收Actions并分发到对应的stores。
  • Stores: 存储应用状态state, 接收dispatcher分发的Action,并更行对应的state。
  • Views: 表示用户交互的React组件,它们接收stores的状态并渲染相应的视图。

Flux应用是单项数据里,这意味着数据流只会有一个方向:从action->store->view。

HOC

  • Higher Order Components (HOCs) 作为更高级的React组件,被设计为封装其他组件,并提供额外的功能。
  • HOC可以理解为可复用的代码片段,用于让开发人员快捷地提供诸如数据获取、状态管理、权限认证等通用功能,减少不必要的重复代码。
  • HOCs为开发人员提供了一种方式,可以通过将通用逻辑抽象到单独的组件,来分离业务关注点,并且可以在整个应用重复使用这些通用的逻辑。分离业务关注点,这有助于保持代码的条理性、扩展性,降低由于缺乏维护而引入的错误风险。此外,由于HOC依赖于组合而不是继承,因此与传统的面向对象编程原则(如类和对象)相比,它们更容易让初学者掌握。

以下HOC示例添加新属性name到组件:

const withName = (WrappedComponent) => { 
    return class extends React.Component { 
        render() { 
            return <WrappedComponent name="John" {...this.props} /> 
        } 
    } 
} 
class MyComponent extends React.Component { 
    render() { 
        return <div>My name is {this.props.name}</div> 
    } 
} 
const MyComponentWithName = withName(MyComponent) 

withName高阶组件作为一个函数,接收类型为组件的参数,并返回一个新组件。新的组件附加了额外的prop属性name。

另一个HOC示例,用来按条件(用户角色)渲染组件:

const withRoleCheck = (WrappedComponent) => { 
    return class extends React.Component { 
        render() { 
            const { userRole, ...rest } = this.props 
            if (userRole === 'admin') { 
                return <WrappedComponent {...rest} /> 
            } else { 
                return <div>You do not have permission to access this page</div> 
            } 
        } 
    } 
} 
class AdminPage extends React.Component { 
    render() { 
        return <div>Welcome to the admin page</div> 
    } 
} 
const AdminPageWithRoleCheck = withRoleCheck(AdminPage) 

最佳实践

当开发React应用,保持好的实践可以确保应用代码清晰、可维护、便于开发。

  • 编写组件的props和state尽量简单,Props、state仅用于提供组件渲染必要的数据,其他额外的数据应隔离在组件外,如单独的store。

  • 组件render函数应尽量简单,render函数仅负责返回UI交互需要的JSX,其他额外的逻辑需要从render中移除。

  • 组件应模块化、可复用,这意味着每一个组件应该仅负责的UI局部片段,并且每个组件在应用的任何位置都可以被复用。

还有一些附加的最佳实践点:保持组件业务聚焦和简单、使用函数式组件开发、持续更新React的版本、尽量为组件或元素提供key、使用lint作为编译调试工具。

总结

React从2011年被提出,从React的发展历史来了解它不失为一个好的方法,了解发展过程能够更加深入地体会到为什么会有后序的各种Hooks、状态管理、路由管理等等。

当我们在使用基于React实现的各种开源库时,了解清楚React框架本身提供的生命周期、路由、HOC等功能,才能够更合理、高效地使用这些开源库。

参考

  1. The History of React.js on a Timeline
  2. 你真的懂 React 吗
  3. Introduction to React JS

写在最后,如果大家有疑问可直接留言,一起探讨!感兴趣的可以点一波关注。