Facebook Open Sources React UI Library to Eliminate DOM Mutations

[JSConfUS 2013] Tom Occhino and Jordan Walke: JS Apps at Facebook — JSConf

JSConf

Traditional MVC frameworks fail at scale because bi-directional data binding creates brittle mutation cascades, which React eliminates by treating views as pure transforms that compute minimal browser patches.

AI-assisted summary of [JSConfUS 2013] Tom Occhino and Jordan Walke: JS Apps at Facebook — original source. Check the original for full context.

Source published:

Key ideas

  • Facebook officially open-sourced React at JSConf US 2013, releasing the client-side UI library on GitHub after roughly 18 to 24 months of internal production development. (17:57)
  • React rejects two-way data binding and observable models, instead using a unidirectional flow where interfaces are declared as pure transforms of application state and re-rendered conceptually from scratch on every change. (2:37)
  • To bypass the performance bottleneck of touching real browser DOM nodes, React generates an internal element representation and reconciles changes via recursive tree diffing to execute only minimal DOM updates. (3:44)
  • The acquisition of Instagram acted as the key forcing function to decouple React from proprietary Facebook infrastructure, proving its standalone viability by powering the web profiles on instagram.com. (15:28)

Detailed notes

## Background Published by JSConf on YouTube on August 5, 2013, this presentation features Facebook engineers Tom Occhino and Jordan Walke delivering their talk titled "JS Apps at Facebook." While no video description is provided on the page, the session serves as the historic public unveiling of React and its underlying architectural philosophy.

## TL;DR Tom Occhino and Jordan Walke challenge traditional client-side MVC architectures, arguing that bi-directional data binding and manual view mutation create brittle, overly complex systems. They introduce React, a library designed around the radical mental model of re-rendering the entire user interface from scratch whenever state changes, made computationally feasible through an internal reconciliation diffing engine. Walke demonstrates how JSX, declarative components, and state transitions via setState isolate updates and unlock decoupled workflows such as server-side rendering. Occhino closes by explaining how Instagram's acquisition served as the forcing function to untangle React from Facebook's proprietary infrastructure, culminating in the live announcement that React had just been open-sourced on GitHub.

## Atmosphere & style The presentation carries an engaging split dynamic, alternating between Occhino’s conversational, high-level storytelling and Walke’s deliberate, code-centric walkthrough. There is a palpable sense of bold, quiet conviction as the speakers casually challenge the prevailing front-end orthodoxies of MVC and observable data models with radical concepts like JSX and full-tree re-renders. The talk transitions smoothly from philosophical debate to hard-nosed engineering realities, ending on an electric, historic high note when the room erupts in applause at the surprise reveal that React was published to open source just thirty minutes before.

## Bulletin - [17:57](https://www.youtube.com/watch?v=GW0rj4sNH2w&t=1077s) Facebook officially open-sourced React at JSConf US 2013, releasing the client-side UI library on GitHub after roughly 18 to 24 months of internal production development. - [02:37](https://www.youtube.com/watch?v=GW0rj4sNH2w&t=157s) React rejects two-way data binding and observable models, instead using a unidirectional flow where interfaces are declared as pure transforms of application state and re-rendered conceptually from scratch on every change. - [03:44](https://www.youtube.com/watch?v=GW0rj4sNH2w&t=224s) To bypass the performance bottleneck of touching real browser DOM nodes, React generates an internal element representation and reconciles changes via recursive tree diffing to execute only minimal DOM updates. - [15:28](https://www.youtube.com/watch?v=GW0rj4sNH2w&t=928s) The acquisition of Instagram acted as the key forcing function to decouple React from proprietary Facebook infrastructure, proving its standalone viability by powering the web profiles on instagram.com.

## Key takeaways - [00:00](https://www.youtube.com/watch?v=GW0rj4sNH2w&t=0s) Traditional MVC and MVW frameworks rely on observable models and bi-directional data binding, which inherently encourage and complicate manual state mutation. - [00:00](https://www.youtube.com/watch?v=GW0rj4sNH2w&t=0s) Facebook sought an architecture that eliminates manual mutation orchestration, adopting a single, controlled update channel that flows downward toward the view. - [02:37](https://www.youtube.com/watch?v=GW0rj4sNH2w&t=157s) The conceptual model behind React is rendering the entire view from scratch on every event, bypassing the need for complex state-synchronization logic. - [02:37](https://www.youtube.com/watch?v=GW0rj4sNH2w&t=157s) Because direct DOM teardown and recreation is prohibitively expensive, React introduces a reconciliation layer to make full conceptual re-renders computationally feasible. - [03:44](https://www.youtube.com/watch?v=GW0rj4sNH2w&t=224s) JSX serves as an optional, lightweight syntactic transform that maps XML tags directly into JavaScript function calls without altering line numbers or breaking linter diagnostics. - [03:44](https://www.youtube.com/watch?v=GW0rj4sNH2w&t=224s) JSX tags do not instantiate actual DOM nodes; they create lightweight internal React element representations. - [05:53](https://www.youtube.com/watch?v=GW0rj4sNH2w&t=353s) React components function as pure transforms from input properties (`this.props`) to UI descriptions, avoiding deep object-oriented inheritance hierarchies. - [10:12](https://www.youtube.com/watch?v=GW0rj4sNH2w&t=612s) Interactive components act as state machines, utilizing `getInitialState` to define their baseline and `setState` to perform discrete state transitions that trigger re-rendering. - [12:17](https://www.youtube.com/watch?v=GW0rj4sNH2w&t=737s) The component lifecycle separates initial string markup generation from object instantiation and event binding, enabling server-side rendering on separate machines. - [12:17](https://www.youtube.com/watch?v=GW0rj4sNH2w&t=737s) Reconciliation recursively compares subsequent render trees to compute minimal DOM patches, updating deeply nested text nodes without reading from the DOM. - [14:19](https://www.youtube.com/watch?v=GW0rj4sNH2w&t=859s) React incorporates escape hatches for raw DOM access, facilitating gradual adoption alongside existing third-party JavaScript libraries and legacy codebases. - [14:19](https://www.youtube.com/watch?v=GW0rj4sNH2w&t=859s) Performance optimization hooks allow developers to prune entire subtrees from the reconciliation diff search space as an afterthought rather than a structural development tax. - [15:28](https://www.youtube.com/watch?v=GW0rj4sNH2w&t=928s) Prior to open sourcing, React was heavily battle-tested across core Facebook features including Chat, comments, notifications, like buttons, and buddy lists. - [15:28](https://www.youtube.com/watch?v=GW0rj4sNH2w&t=928s) Instagram engineers refactored React to eliminate proprietary internal dependencies, successfully running all of instagram.com on the framework within weeks. - [17:57](https://www.youtube.com/watch?v=GW0rj4sNH2w&t=1077s) Facebook concluded the presentation by open-sourcing React under the Facebook GitHub organization and inviting community contributions via IRC and mailing lists.

## Detailed walkthrough ### [00:00](https://www.youtube.com/watch?v=GW0rj4sNH2w&t=0s) Rethinking UI Architecture: Beyond MVC and Mutation

Tom Occhino opens the presentation by thanking Chris and explaining the split format of the talk: Tom will handle the high-level conceptual background, while Jordan Walke will cover the deeper technical details of how the library works under the hood. Tom focuses directly on client-side web browser applications, noting that while Facebook runs Node.js and other server infrastructure, the core question they have revisited constantly is how to structure user interfaces. Over the years, the web development community moved the platform forward through frameworks that embraced Model-View-Controller (MVC) or its variants like MVVM and AngularJS's "Model View Whatever" (MVW). Tom notes that while he genuinely admires Angular's declarative data binding—describing it as something of a "Holy Grail in terms of APIs"—most of these frameworks share a fundamental architectural dependency on observable models.

```mermaid flowchart LR accTitle: Bi-directional Data Binding Cycle accDescr: Diagram contrasting bi-directional MVC binding against React's unidirectional rendering flow.

subgraph BiDirectional["Traditional MVC / MVW (Bi-directional)"] M["Observable Model"] <-->|"Two-way data binding"| V["Views / DOM"] end

subgraph UniDirectional["Facebook Architecture (Unidirectional)"] In["User Input / Updates"] --> Flow["Single Channel Flow"] Flow --> State["Application State"] State -->|"Blow away and re-render"| RV["View (Pure Transform)"] end ```

In standard MVC systems, models implement observable objects backed by event listeners and event emitter APIs. Developers then use two-way (bi-directional) data binding to link views to these models: when a model mutates, views re-render or patch their nodes, and when a user alters a view, it directly updates the model, cascading mutations back out to other dependent views across the system. Tom emphasizes that this observable pattern fundamentally relies on and encourages continuous mutation. As Tom puts it:

> “So this observable model pattern encourages mutation though and mutation is complex.”

Starting several years prior during a complete rewrite of Facebook Chat, the team set out to drastically reduce the amount of mutation that application engineers had to write or manually coordinate.

- Acknowledges Peter Higgins and Dojo's pioneering role in client-side framework design ("Dojo did it first"). - Identifies mutation as an unavoidable, necessary evil in stateful user interfaces, but argues that application developers should not be responsible for orchestrating it by hand. - Contrasts bi-directional binding cascades with a single, controlled update channel that feeds data downward toward the view layer.

---

[02:37](https://www.youtube.com/watch?v=GW0rj4sNH2w&t=157s) The Dream of Re-rendering Everything: Introducing React

When evaluating how to eliminate manual view mutation, the Facebook team arrived at a radical mental model: whenever application state changes, simply destroy the existing UI and rebuild the entire view tree from scratch. Tom acknowledges the immediate skepticism of this approach, recognizing that anyone familiar with web performance will point out that touching the real DOM is notoriously slow and that tearing down and re-creating HTML elements on every real-time event would be prohibitively expensive.

Despite the obvious DOM performance bottleneck, Facebook realized that from a developer-experience and conceptual standpoint, rendering from scratch on every change is vastly superior because it frees engineers from writing state-synchronization logic. To bridge the gap between this clean conceptual model and real-world browser constraints, they spent 18 to 24 months developing a dedicated library designed to make total re-renders feasible without the DOM overhead. Tom formally introduces this library as React:

> “We want to build something that gives us the conceptual simplicity of rendering from scratch on every single event, um, but you know, without the overhead because that's prohibitively expensive.”

- Development on React began roughly 18 months to two years before this presentation (circa late 2011 to early 2012). - The core design goal was preserving the developer ergonomics of a pure, top-down full re-render while abstracting away the underlying browser penalties.

---

[03:44](https://www.youtube.com/watch?v=GW0rj4sNH2w&t=224s) Declarative Views and the JSX Syntax

Jordan Walke takes the stage to explain how React realizes these design goals. React minimizes developer-facing mutation through a strictly declarative programming model. Rather than writing procedural DOM manipulation code, developers provide a non-mutative description of what the user interface should look like across all points in time. React explicitly ships without built-in observable data binding; instead, it replaces the need for observables with declarative UI definitions.

To make declaring tree structures natural inside JavaScript, Facebook introduced an optional embeddable XML syntax named JSX. Jordan emphasizes that unlike older XML-in-script proposals, JSX is not a heavy domain-specific language or runtime framework; it is a straightforward syntactic transform that converts XML tags directly into standard JavaScript function invocations.

```mermaid flowchart TD accTitle: JSX Compilation and Component Rendering Pipeline accDescr: Pipeline showing how JSX transforms to function calls, produces virtual React elements, and reconciles into minimal DOM patches.

subgraph SyntaxTransform["Build / Compile Step"] JSX["JSX Syntax<br/><code>&lt;ActionButton text='...' /&gt;</code>"] -->|"Lightweight AST Transform"| JS["Function Invocations<br/><code>React.DOM.div(...)</code>"] end

subgraph Runtime["React Runtime"] JS --> ElementTree["React Component Tree<br/>(Lightweight Objects, not DOM)"] ElementTree --> Reconcile{"Diff with Previous Tree"} Reconcile -->|"Compute Minimal Mutations"| DOMPatch["Single Span Text Update"] end ```

The resulting code preserves original line numbers precisely, ensuring that linters (such as JSHint) and stack traces report errors on the exact lines where variables or syntax issues appear. Furthermore, tags like `<div>` in JSX do not instantiate real browser DOM nodes; they instantiate lightweight internal React element representations.

- JSX is fully optional: engineers can choose to write raw JavaScript function calls directly, though JSX provides a far more readable representation of nested interfaces. - Tooling simplicity and fast compilation workflows were prioritized over creating an entirely separate templating language. - Linters can immediately spot out-of-scope component identifiers because custom tags map directly to in-scope JavaScript variables.

---

[05:53](https://www.youtube.com/watch?v=GW0rj4sNH2w&t=353s) Custom Components, Props, and Pure Transforms

Jordan explains that React is primarily an abstraction tool designed to build reusable custom components while encapsulating UI implementation details. Components are instantiated with JSX using standard XML-style attributes, which are passed into the component instance as `this.props`. Rather than relying on heavyweight object-oriented class hierarchies, components are defined by passing an object specification to `React.createClass`.

```javascript // Example component definition structure discussed by Jordan var ActionButton = React.createClass({ render: function() { return ( <button onClick={this.props.onAction}> <span>{this.props.text.toUpperCase()}</span> </button> ); } }); ```

A component must supply a `render` method, whose entire responsibility is to describe the component's UI at any given point in time. Inside JSX, curly braces allow arbitrary JavaScript expressions to be interpolated, transforming properties directly into the rendered output (such as running `.toUpperCase()` on `this.props.text`). Because JSX is embedded directly in JavaScript, event handlers like `onAction` can accept first-class functions rather than string evaluations. As Jordan describes this mental model:

> “And so it helps to view, at least initially, a react component as a transform from these properties into a user experience and you can just kind of imagine these being routed to their proper locations.”

When a component's input properties change over time, the `render` function runs again, producing a new description of the UI. React then compares this description with the previous output and computes the absolute minimum set of DOM mutations required to bring the browser into alignment—such as updating only the `textContent` of an inner `<span>` while leaving the outer `<button>` untouched.

- `React.createClass` mixes React’s base component behaviors into the prototype while deliberately avoiding deep object-oriented class inheritance trees. - Attributes passed to components are not restricted to strings; they accept functions, objects, and any valid JavaScript expression. - The `render` method is a pure projection over time, not a one-off setup script that only runs during initial page load. - Having demonstrated stateless components driven solely by properties, Jordan transitions to explaining how React manages internal state for complex, interactive applications.

[10:12](https://www.youtube.com/watch?v=GW0rj4sNH2w&t=612s) Components as State Machines: `getInitialState` and `setState`

Jordan Walke demonstrates how React handles interactive applications that require more than simple stateless views. If developers want to build real application logic, components need to assume the role of a state machine, which inherently requires an initial state. React provides a dedicated method called `getInitialState` to define this starting baseline. In the walkthrough example, a counter component reuses the previously defined `ActionButton` component twice—once to increment and once to decrement. The component initializes its state property `count` from `this.props.initialCount` (which receives a value of `4` in the demo).

```mermaid flowchart LR accTitle: React Component State Transition accDescr: Flow diagram showing how user actions trigger setState to transition state and re-render the view. subgraph UI ["User Interaction"] A["ActionButton Click"] end subgraph StateMachine ["State Transition"] B["addToCount(delta)"] C["setState()"] end subgraph View ["Render Output"] D["render()"] E["H3 Text Node Updated"] end A --> B B --> C C --> D D --> E ```

Just as components can read `props` inside their `render` function and rely on React to keep them fresh, they can read `this.state` with the identical guarantee. Any time state or props change, the render output stays satisfied by the React system. For example, an `<h3>` tag displays the current `this.state.count`, but developers are free to insert arbitrary JavaScript expressions or ternaries to show custom contextual messaging depending on the numeric tally. To trigger actual mutations, the component binds an `addToCount` callback to the buttons' action events with a delta of `+1` or `-1`. As Jordan explains the architectural concept behind state updates: “If our components are a state machine then set state is how you perform State transitions on the state machine and what we do is we set the next count to be equal to the current count plus the Delta.”

[12:17](https://www.youtube.com/watch?v=GW0rj4sNH2w&t=737s) Component Lifecycle: Initial Render, Server Rendering, and Reconciliation

Jordan outlines the two primary stages of a React component's lifecycle: initial rendering and data reconciliation. In the initial render pass, React prioritizes architectural flexibility by computing a single giant markup string across the entire component hierarchy tree and injecting it into the target container. Immediately following that markup generation, a secondary phase instantiates all backing objects and wires up the event handlers. Because these two initial phases are decoupled, they can run in completely separate computing environments across the globe. As Jordan points out: “So you could in theory render the markup on a server and then attach all the event handlers and instantiate all the backing views on the client two completely separate machines.”

```mermaid flowchart TD accTitle: React Lifecycle and Reconciliation Flow accDescr: Diagram illustrating the two phases of React component lifecycle from initial string markup to recursive diffing. subgraph InitialPhase ["Initial Render Stage"] A["Traverse Component Tree"] --> B["Generate Giant Markup String"] B --> C["Inject DOM Markup"] C --> D["Instantiate Backing Objects & Bind Event Handlers"] end subgraph UpdatePhase ["Reconciliation Stage"] E["Data / State Change"] --> F["Subsequent render() Invocations"] F --> G["Recursive Tree Diffing"] G --> H["Serialize Minimal Mutation Commands"] H --> I["Apply Diff to DOM (Zero Unnecessary Reads)"] end ```

The second lifecycle phase begins whenever underlying data changes and the user interface must be refreshed. React handles this through reconciliation: recursively comparing the outputs of subsequent invocations of `render` to compute the smallest structural diff possible. Rather than tearing down elements, React serializes minimal mutation commands across a logical bridge to the DOM. If a single class name, style attribute, or child node changes fifty levels deep inside a complex hierarchy—such as button text changing from "book flight" to "book hotel"—React executes only the single DOM mutation necessary to update that inner text span, avoiding reads from the DOM entirely.

[14:19](https://www.youtube.com/watch?v=GW0rj4sNH2w&t=859s) Escape Hatches, Pragmatic Integration, and Pruning the Diff Search Space

Acknowledging that conference talks typically showcase contrived demos, Jordan addresses real-world constraints by detailing React's escape hatches. Applications rarely exist in a vacuum; developers must integrate with preexisting libraries and legacy codebases. React accommodates this by offering hooks to drop down directly into raw DOM access whenever necessary. Furthermore, because computing a diff across massive tree structures can become computationally expensive, React provides optional escape hatches that allow developers to help guide the reconciliation engine.

These performance escape hatches allow engineers to prune large sections of the diff search space early, signaling to React when whole subtrees have not changed and do not need inspection. Jordan emphasizes that performance tuning is designed to be an afterthought rather than a structural tax on everyday development: “So those are completely optional you can you can put them in your code or you can take them out and the same application runs just faster or slower so it's treat it's it's treating this optimization step as a completely a completely separate part of development it's not core you don't start with the optimizations but you you identify the bottlenecks and apply them where necessary.”

[15:28](https://www.youtube.com/watch?v=GW0rj4sNH2w&t=928s) Production Battle-Testing at Facebook and the Instagram Forcing Function

Jordan hands the presentation back to Tom Occhino, who reinforces why escape hatches were vital to React's survival and adoption inside Facebook. Instead of requiring developers to rewrite entire systems from scratch, React allowed teams to adopt it piecemeal. If a team needed an auto-growing textarea or a third-party autocomplete widget, they could simply ask React for the underlying DOM node and initialize their existing JavaScript plugins directly. As Tom highlights: “Another hook that you get is basically at any point you can say just give me a DOM node and let me write JavaScript.” This evolutionary, non-dogmatic strategy enabled React to gain significant ground over the preceding 18 months to two years, powering heavily trafficked Facebook features including comment threads, like buttons, chat windows, buddy lists, and notifications.

The catalyst for open sourcing React arrived when Instagram was acquired by Facebook about a year prior. Historically, Facebook presentations at JSConf carried the caveat that internal technologies were too tightly bound to internal server and build infrastructures to ever be released. Instagram wanted to adopt Facebook's frontend infrastructure, but their stack ran outside of Facebook's corporate environments. Taking inspiration from a talk by Rebecca Murphey about authoring code as though it is open source from day one, Facebook's engineers set out to decouple React from proprietary internals.

- Instagram engineers served as the critical forcing function, refactoring React and its build tooling so it could run anywhere. - Within one to two weeks of joining, the Instagram team used React to build the entire web profile experience on `instagram.com`. - With all of `instagram.com` successfully running on React, Facebook had an external-style client proofing the framework's modularity.

[17:57](https://www.youtube.com/watch?v=GW0rj4sNH2w&t=1077s) Open Source Release Announcement and Community Invitation

Having validated React outside of Facebook's monolithic codebase with Instagram, the engineering team realized the technology was ready for the broader JavaScript ecosystem. Tom surprises the JSConf audience with a live release announcement: “The Logical next step for all the people that have been working on this this is you know you know we want to open source this and actually incidentally we did that about a half hour ago.”

The audience breaks into enthusiastic applause as Tom invites the community to explore the newly launched project on GitHub under the Facebook organization. In closing, Tom adopts a humble tone, stressing that React is simply a tool that solved difficult synchronization problems for Facebook and that the team is eager for community feedback. He encourages attendees to get involved, contribute, and report bugs across the newly established GitHub repository, IRC chat room, and Google Groups mailing list.

## Notable quotes > [02:37](https://www.youtube.com/watch?v=GW0rj4sNH2w&t=157s) We want to build something that gives us the conceptual simplicity of rendering from scratch on every single event, um, but you know, without the overhead because that's prohibitively expensive. — Tom Occhino

## Mentioned Tom Occhino, Jordan Walke, Chris, Facebook, Node.js, AngularJS, Peter Higgins, Dojo, React, JSX, JSHint, Instagram, Rebecca Murphey, GitHub, Google Groups, IRC

[Source](https://www.youtube.com/watch?v=GW0rj4sNH2w)

中文简报

Facebook开源React UI库以消除DOM变更 传统MVC框架在大规模应用中表现不佳,因为双向数据绑定会引发脆弱的级联变更;React通过将视图视为纯转换并计算最小浏览器补丁,彻底消除了这一问题。 Tom Occhino与Jordan Walke对传统的客户端MVC架构提出质疑,指出双向数据绑定和手动视图变更会导致系统脆弱且过于复杂。他们推出了React,该库围绕一种颠覆性的思维模型设计,即每当状态发生改变时都从头重新渲染整个用户界面,并通过内部的协调diff引擎在计算上使其可行。Walke展示了JSX、声明式组件以及通过setState进行的状态转换如何隔离更新,并解锁诸如服务端渲染等解耦工作流。Occhino最后说明了收购Instagram如何成为促使React脱离Facebook专有基础设施的关键推手,并现场宣布React刚刚已在GitHub上正式开源。 Facebook在JSConf US 2013上正式开源了React,在经历了内部约18至24个月的生产环境开发后,将这一客户端UI库发布到了GitHub上。 (17:57) React摒弃了双向数据绑定和可观察对象模型,转而采用单向数据流;在该模式下,界面被声明为应用状态的纯转换,并在理论上每次发生变更时都从头重新渲染。 (2:37) 为绕过操作真实浏览器DOM节点的性能瓶颈,React生成了一套内部元素表示,并通过递归树比对来协调变更,从而仅执行最小限度的DOM更新。 (3:44) 收购Instagram成为将React与Facebook专有基础设施解耦的关键推手,并通过驱动instagram.com上的网页个人主页,证明了其独立运作的可行性。 (15:28)

Reading summary

Facebook introduced React at JSConf US 2013, arguing that user interface complexity stems from manual state mutation and bi-directional data binding in traditional MVC frameworks. Presenters Tom Occhino and Jordan Walke explain how React substitutes manual DOM manipulation with a declarative, top-down re-rendering mental model backed by a virtual reconciliation algorithm. The Flaws of Observable MVC Architecture (0:00) Traditional client-side web architectures—ranging from early frameworks like Dojo to newer MVC, MVVM, and Angular-style MVW systems—rely heavily on observable models and two-way data binding. In these setups, when a model mutates, dependent views update, and user inputs mutate models back, creating cascading mutation loops across the interface. Occhino notes that while declarative data binding can offer attractive APIs, observable patterns inherently encourage continuous, complex mutation that application engineers must manually manage and synchronize. Declarative Components as Pure State Projections React eliminates manual DOM updates by adopting the conceptual model of destroying and recreating the entire interface whenever state changes. Rather than authoring procedural DOM mutations, engineers define reusable components using an object specification via React.createClass. Walke explains that a component's render method acts as a pure projection over time, translating input properties (this.props) and local state machine data (this.state) into an interface description. State transitions are triggered explicitly via setState, avoiding hidden bi-directional side effects. Decoupled Lifecycle, JSX, and Reconciliation (12:17) To implement continuous re-rendering without the prohibitive performance penalties of real DOM access, React introduces a lightweight intermediate layer. Optional JSX syntax compiles directly into JavaScript function calls that produce lightweight component objects rather than real DOM nodes. During an initial render, React can output a continuous markup string and defer event binding, making it possible to render the initial UI on a server before hydrating it on the client. On subsequent updates, React's reconciliation engine recursively diffs the new element tree against the old one, issuing minimal, batch-serialized DOM mutations—such as updating a single text node deep in the tree without reading from the browser DOM. Pragmatic Adoption and the Open Source Release (15:28) Because full rewrites are impractical, React was designed with pragmatic escape hatches. Developers can access raw DOM nodes directly to integrate legacy scripts, third-party libraries, or auto-growing textareas, and they can prune the diffing search space to optimize performance bottlenecks only after they are identified. After 18 to 24 months of internal production use on features like Facebook Chat, comments, and likes, the acquisition of Instagram forced the engineering team to decouple React from proprietary Facebook build infrastructure. Following a complete rebuild of instagram.com using the library, Facebook officially open-sourced React during the presentation. Occhino emphasized that React was not intended as a universal dogma, but as a practical tool engineered to solve Facebook's internal UI synchronization bottlenecks. With the public release on GitHub, IRC, and Google Groups, the team sought community testing and feedback to see how well its reconciliation and component models would generalize across the broader web ecosystem.

精炼阅读摘要

在 JSConf US 2013 上,Facebook 工程师 Tom Occhino 与 Jordan Walke 正式开源了 React,提出通过声明式视图、单向数据流与虚拟 DOM 协调机制,取代传统 MVC 架构中脆弱的手动状态变更与双向数据绑定。 反思传统 MVC 与频繁变更的困境 (0:00) Tom Occhino 指出,当时前端社区普遍采用基于可观察模型(Observable Models)的 MVC、MVVM 或 AngularJS 等框架。这类系统依赖双向数据绑定,当模型发生变动时视图更新,而视图交互又反向修改模型,极易引发难以追踪的级联变更。Facebook 团队在重构 Facebook Chat 期间认识到,状态虽然不可避免,但让业务开发者手动编排这些状态变更既繁琐又容易出错。团队认为理想的心智模型应当是单向流动的:每当数据或事件产生变化时,直接舍弃现有 UI 并从零开始全量重新渲染,从而彻底免除开发者编写状态同步逻辑的负担。 声明式组件模型与 JSX 编译转换 (3:44) 为了在真实浏览器中低成本实现“全量重渲染”的心智模型,Facebook 研发了 React。Jordan Walke 解释,React 摒弃了内置的可观察数据绑定,转而采用声明式编程。开发者不再编写过程式的 DOM 操纵代码,而是使用轻量语法扩展 JSX 描述 UI 在任意时间点的状态。JSX 并非复杂的运行时模板引擎,而是在构建阶段直接转译为纯 JavaScript 函数调用,精确保留原始代码行号以便于代码检查工具和错误堆栈排错。通过 `React.createClass` 构建的组件本质上是接收属性(`props`)并输出 UI 描述的纯函数式投影,且不依赖深层面向对象继承体系。 状态机模型与高效协调机制 (10:12) 对于具有交互逻辑的复杂组件,React 允许将其作为状态机进行建模。组件通过 `getInitialState` 初始化状态,并通过调用 `setState` 执行状态转换与驱动后续重渲染。在生命周期层面,React 将组件拆分为初始渲染与更新协调两个阶段。在初次渲染时,React 可以计算并拼接出整棵组件树的完整标记字符串注入容器,该机制允许在服务端生成 HTML 字符串、随后在客户端无缝挂载事件监听器。当后续状态变化时,React 的协调算法(Reconciliation)会递归对比前后虚拟元素树,计算出极小化的 DOM 操作指令,仅针对变更的叶子节点进行原地修改,避免了对真实 DOM 的冗余读取与低效重建。 渐进集成设计与 Instagram 生产实战 (14:19) 为了在不强求推倒重写的前提下平滑接入已有系统,React 设计了实用的“逃生通道”(Escape Hatches)。开发者既可以直接请求底层的真实 DOM 节点以挂载第三方插件,也能通过优化钩子剪枝协调算法的遍历空间,避免对未变动子树进行不必要的 Diff 计算。Tom Occhino 补充说明,这种务实设计使 React 在内部演进了 18 至 24 个月,成功支撑了 Facebook 评论流、聊天窗口及通知等核心功能。而在 Facebook 收购 Instagram 后,Instagram 团队出于其独立架构的诉求,推动了 React 从 Facebook 专有内部工具链中解耦,并仅耗时一到两周便完全基于 React 构建了 `instagram.com` 的 Web 个人主页,充分验证了该库的跨项目独立可用性。 React 在演讲当天正式在 GitHub 上开源。主讲人强调,React 并非无所不包的庞大框架,而是 Facebook 用于解决复杂状态同步问题的一款务实工具,其未来的持续演进仍有待社区的实际反馈、缺陷提交与生态协作来进一步验证。

Detailed notes — zh

## Background 该视频由 JSConf 官方频道于 2013 年 8 月 5 日发布在 YouTube 上,记录了 Facebook 工程师 Tom Occhino 与 Jordan Walke 在 JSConf US 2013 上的技术分享。视频页面未提供额外的文字简介或补充说明。 ## TL;DR 演讲直击传统前端 MVC 架构中双向绑定与可观察模型导致状态突变失控的弊端,阐述了 Facebook 转向单向数据流与“全量重新渲染”心智模型的思考过程。Jordan Walke 深入剖析了 React 库的底层设计,包括可选的 JSX 语法、以状态机为核心的组件模型,以及通过虚拟协调(Reconciliation)计算最小 DOM 补丁的高效更新机制。Tom Occhino 随后展示了 React 在 Facebook 点赞、聊天等核心业务与 Instagram 全站的落地成效,并宣布 React 已在演讲前半小时正式对外开源。 ## Atmosphere & style 现场弥漫着技术破局的探索感与发布新框架时的历史性躁动,Tom 的宏观架构思辨与 Jordan 的务实底层解析形成极具说服力的双人配合。当演讲者抛出“推倒全量重绘”等颠覆常识的构想时,台下经历了从困惑怀疑到被精巧的协调机制彻底折服的情感转折。最后宣布 React 刚在半小时前正式开源的瞬间,全场爆发出真诚而热烈的掌声,将严谨的技术论证推向了兼具谦逊与里程碑意义的高潮时刻。 ## Bulletin - [17:53](https://www.youtube.com/watch?v=GW0rj4sNH2w&t=1073s) Facebook 在 JSConfUS 2013 正式开源声明式 JavaScript 库 React,提出以单向数据流和虚拟协调(Reconciliation)消除频繁 DOM 突变的开发复杂度。 - [02:20](https://www.youtube.com/watch?v=GW0rj4sNH2w&t=140s) React 提出“每次事件全量重新渲染”的心智模型,底层通过递归比对前后虚拟树并生成最小 DOM 补丁,避免浏览器实际重绘带来的性能损耗。 - [03:44](https://www.youtube.com/watch?v=GW0rj4sNH2w&t=224s) JSX 作为轻量语法糖被引入以配合声明式设计,其一对一保留行号的编译机制可完全兼容现有 linter 静态分析,且在技术实现上完全可选。 - [15:26](https://www.youtube.com/watch?v=GW0rj4sNH2w&t=926s) Instagram 被收购后推动了 React 与 Facebook 内部基础设施的彻底解耦,并在一至两周内采用 React 全面构建了全新的网页版个人主页。 ## Key takeaways - [00:19](https://www.youtube.com/watch?v=GW0rj4sNH2w&t=19s) 传统 MVC 与 MVVM 架构过度依赖可观察模型(Observable Models)与双向数据绑定,容易形成交织扩散的状态突变链条并引发难以追踪的同步 Bug。 - [02:20](https://www.youtube.com/watch?v=GW0rj4sNH2w&t=140s) Facebook 主张单向数据流动(One-Directional Data Flow),将包含用户输入在内的所有状态变更汇集至单一通道分发。 - [02:20](https://www.youtube.com/watch?v=GW0rj4sNH2w&t=140s) React 的核心设计理念是在概念上支持每次事件都推倒视图从零重绘,同时通过框架底层技术抹平 DOM 操作的昂贵性能开销。 - [03:44](https://www.youtube.com/watch?v=GW0rj4sNH2w&t=224s) React 的组件渲染是完全声明式的,组件本质是将 Props 映射为用户界面外观的转换函数,彻底抛弃了内置 Observable 机制。 - [03:44](https://www.youtube.com/watch?v=GW0rj4sNH2w&t=224s) JSX 仅作为轻量转换层存在,负责将标签映射为普通 JavaScript 函数调用,保持低工具链侵入性且代码行号精准映射。 - [05:53](https://www.youtube.com/watch?v=GW0rj4sNH2w&t=353s) React 组件的核心生命周期函数 render 具有纯粹职责:在任意时间点根据当前的 properties 与 state 精确描述界面的状态与外观。 - [10:12](https://www.youtube.com/watch?v=GW0rj4sNH2w&t=612s) 有状态组件被抽象为有限状态机,通过 getInitialState 定义初始状态,并通过调用 setState 显式触发状态转移与重新渲染调度。 - [12:17](https://www.youtube.com/watch?v=GW0rj4sNH2w&t=737s) React 初次渲染分为两步:先在内存中递归遍历组件树生成标记字符串(可同构运行于服务端),随后在客户端实例化对象并绑定事件。 - [12:17](https://www.youtube.com/watch?v=GW0rj4sNH2w&t=737s) 更新阶段的协调(Reconciliation)机制通过比对前后 render 输出计算出最小差异集合,即便嵌套再深也仅对真实 DOM 执行最少量的必要写操作。 - [14:20](https://www.youtube.com/watch?v=GW0rj4sNH2w&t=860s) 框架预留了访问真实底层 DOM 的逃生通道(Escape Hatches),使开发者能够无缝集成旧有原生脚本及第三方插件,支持渐进式零碎重构。 - [14:20](https://www.youtube.com/watch?v=GW0rj4sNH2w&t=860s) 性能优化被设计为独立于业务逻辑的环节,React 允许开发者在遇到瓶颈时手动对虚拟树比对搜索空间进行剪枝优化。 - [15:26](https://www.youtube.com/watch?v=GW0rj4sNH2w&t=926s) 在正式开源前,React 已在 Facebook 内部的点赞、评论、网页聊天框、好友列表以及通知等海量高并发业务中稳定运行并得到验证。 - [15:26](https://www.youtube.com/watch?v=GW0rj4sNH2w&t=926s) Instagram 团队采纳 React 的诉求成为关键倒逼机制,促使 Facebook 团队按照开源标准将 React 从内部专有基建中剥离并独立发布。 ## Detailed walkthrough ### [00:19](https://www.youtube.com/watch?v=GW0rj4sNH2w&t=19s) 前端架构的困境:MVC、双向绑定与状态突变的复杂性 Tom Occhino 首先登台致谢,并说明本次演讲将探讨 Facebook 在浏览器端进行 JavaScript 应用开发的实践方式。他笑称自己负责讲“虚”的概念层面的内容,随后把具体的技术细节留给 Jordan Walke。Tom 指出,团队多年来反复思考同一个核心问题:究竟该如何组织运行在 Web 浏览器中的 JavaScript 应用。尽管社区中涌现了大量的 JavaScript 框架(例如推动 Web 平台发展的 Dojo、以及 Angular 等),且行业共识倾向于 MVC、MVVM 乃至 Angular 提出的“Model-View-Whatever”架构,但他认为这些架构都存在一个共通的底层假定——以可观察的模型(Observable Models)为中心。 在这些传统框架中,Model 通常实现为带有事件监听机制的可观察对象,开发者依靠双向数据绑定(bi-directional data binding)将 View 与 Model 关联起来。当 Model 发生变化时更新 View,而 View 改变时反过来修改 Model,再进一步向外广播扩散至其他 View。Tom Occhino 强调,这种依赖可观察对象的方式极大地鼓励了状态突变(Mutation),而突变本身是极其复杂且难以维护的。Tom Occhino 直言:“This observable model pattern encourages mutation though and mutation is complex.”(这种可观察模型模式鼓励了突变,而突变是复杂的。) - 社区中存在多种前端架构尝试:MVC、MVVM、以及被称为“Model-View-Whatever”的模式;Tom 特别表达了对 AngularJS 声明式数据绑定 API 的欣赏。 - 传统架构的核心弊端在于依赖可观察对象和事件触发机制,导致 View 与 Model 之间的双向突变链条交织缠绕,极易引发竞态与难以追踪的状态同步 Bug。 ```mermaid flowchart LR accTitle: 传统双向数据绑定与突变流动 accDescr: 展示传统框架中 View 与 Model 之间双向触发突变的高复杂度流向 subgraph BiDirectional["传统双向绑定架构"] M["Model (Observable)"] <-->|"双向数据绑定 / 事件触发"| V["View (UI 元素)"] V -->|"触发更新并扩散"| Other["其他 Views / 依赖"] end ``` ### [02:20](https://www.youtube.com/watch?v=GW0rj4sNH2w&t=140s) 范式转换:单向数据流与全量重新渲染的构想 面对突变带来的失控,Facebook 大约在数年前重写网页聊天功能(Facebook Chat)时,开始探索截然不同的代码架构。团队确立的原则是:突变虽然是不可避免的恶,但必须尽可能减少开发者亲手编写的突变逻辑。在他们梳理的架构中,包含用户输入在内的所有数据更新都必须经过单一通道进行分发,这就是单向数据流动(one-directional data flow)。 当这些更新传递到视图层时,Facebook 团队得出了一个在当时看来极为激进的结论——最简单、最不易出错的心智模型,就是在任何底层数据改变时完全抛弃现有视图,直接从零开始重新渲染。面对台下听众显而易见的疑虑,Tom 承认这听起来反直觉,因为浏览器的 DOM 操作极其缓慢,如果每次事件触发都重新生成全部 HTML,在实时高交互应用中会造成灾难性的性能开销。但这正是 Facebook 决定攻克的方向:他们希望在保留“全量重绘”这一极简思维模式的同时,消除其实际运行时的巨大性能惩罚。正因如此,大约在演讲前一年半到两年前,Facebook 启动了名为 React 的 JavaScript 库研发。Tom Occhino 概括了他们的终极目标:“We want to build something that gives us the conceptual simplicity of rendering from scratch on every single event, but you know without the overhead because that's prohibitively expensive.”(我们想要构建一种东西,既能在每次事件发生时带给我们在概念上从零重绘的极简性,又不会产生不可承受的性能开销。) ```mermaid flowchart LR accTitle: React 单向数据流与视图渲染模型 accDescr: 展示所有更新汇入单一通道后触发视图全量重绘概念模型 Inputs["用户输入 / 外部事件"] --> Flow["单一更新通道\n(单向数据流)"] Flow --> ViewState["数据状态变更"] ViewState --> Render["概念模型:推倒视图并全量重绘\n(由 React 底层消除开销)"] ``` ### [03:44](https://www.youtube.com/watch?v=GW0rj4sNH2w&t=224s) 声明式设计与 JSX 的设计哲学 Jordan Walke 接过麦克风,切入 React 的具体技术实现。他重申 React 组件框架的首要目标是最大限度减少暴露给开发者的突变操作,而达成这一目标的核心手段就是高度的声明式(Declarative)设计。在 React 中,不仅仅是初始渲染,后续的所有界面更新也全都是通过声明式描述完成的。它完全抛弃了内置的 Observable 数据绑定,为开发者提供了一种全新的非突变界面表达途径。 为了配合这种声明式理念,Facebook 随 React 一同推出了一种可嵌入 JavaScript 的 XML 语法——JSX。Jordan 强调,JSX 与过去的某些语言扩展提案不同,它被刻意设计为一个极其轻量的语法转换层(Transform)。在底层,JSX 仅仅是将 XML 标签逐一转换成普通的 JavaScript 函数调用,同时严格保留代码的原始行号。这样一来,开发者现有的 linter 工具依然能精准捕捉作用域错误与未定义变量,并将错误准确定位到正确的代码行。Jordan Walke 明确指出 JSX 的定位:“Low tooling is the key here, but JSX is also optional if you want to build with React.”(工具链的低介入性是这里的关键,但如果你想用 React 开发,JSX 也是完全可选的。) - JSX 中的标签(如 `<div />`)并非原生真实 DOM 节点,而是 React 的虚拟元素实例。 - JSX 对工具链友好,行号一对一对应使得代码在通过静态检查工具时,变量超出作用域等问题能立即被捕获。 - JSX 只是语法糖,开发者完全可以直接书写转换后的 JavaScript 函数调用。 ### [05:53](https://www.youtube.com/watch?v=GW0rj4sNH2w&t=353s) 组件化抽象与 `render` 函数的本质 Jordan 进一步解释道,React 的本质远不仅是操作 DOM 元素,而是一个用于抽象可复用代码、隐藏 UI 实现细节的组件化框架。开发者可以使用类似原生 HTML 标签的 JSX 语法来实例化自定义组件(例如 `<ActionButton />`),并通过属性向其传递文本、回调函数等。这些 XML 属性在组件内部都会映射为 `this.props`。 在组件定义上,React 提供了 `React.createClass` 接口,开发者只需传入配置对象并实现 `render` 函数即可。Jordan 阐述说,虽然 React 使用了少量继承来贴合 JavaScript 的语言特性,但其核心范式绝对不是面向对象的。组件定义后即可直接在 JSX 作用域内被引用。更关键的是,`render` 函数具有唯一的纯粹使命:在任意时间点精确描述界面的外观。它不仅服务于初次挂载,而且覆盖组件生命周期的每一个时间切片。传入的属性不仅限于字符串,还可以是诸如 `onAction` 的事件回调函数,并在内部直接绑定给原生 DOM 按钮的 `onClick`;开发者也可以在花括号插值表达式中执行任意 JavaScript 运算(如将文本转为全写大写 `.toUpperCase()`)。Jordan Walke 总结了 React 组件渲染的核心机制:“As your properties change, the render output changes, and then React satisfies your render function; it'll make sure that the user experience is rendered exactly as you've described at any point in time.”(随着你的属性改变,render 的输出也随之改变,接着 React 会去满足你的 render 函数;它会确保用户体验在任何时间点都完全按照你所描述的那样呈现出来。) - **组件转换本质**:React 组件可以理解为一个将 Props 映射/路由到具体用户体验界面的转换函数。 - **差异化最小更新**:当属性(props)改变时,`render` 会重新计算输出,React 会在底层自动计算出将现有界面更新到新描述所需的最小 DOM 突变集合(Minimal set of DOM mutations),例如仅更新最深层 span 的 `textContent`,从而实现高性能。 - **状态引申**:无状态的纯展示组件(Stateless Component)只是基础,为了构建完整的应用程序,Jordan 随即引出包含内部状态(Internal State)的有状态组件。 ### [10:12](https://www.youtube.com/watch?v=GW0rj4sNH2w&t=612s) 组件作为状态机:getInitialState 与 setState Jordan Walke 继续深入讲解计数器(Counter)示例。该计数器包含递增和递减两个按钮,且复用了前一个示例中构建的 ActionButton 组件。他指出,构建真实应用时往往不能只依赖无状态视图(Stateless Views),开发者需要定义某种状态机,让组件承担起状态机的角色。状态机必须有初始状态,React 提供了专门的 `getInitialState` 方法供开发者定义组件的初始状态;在本例中,状态里的 `count` 字段被赋值为传入的 `initialCount` 属性(示例中传入的值是 4)。 在 `render` 函数中,开发者不仅可以访问 `props`,也可以直接访问 `state`,并且能够完全信赖它们总是最新的。Jordan 强调,无论属性还是状态何时发生变化,React 都会确保 `render` 满足更新需求。例如,组件内部的 `<h3>` 标签会实时反映当前的 `state.count`,开发者可以在此处编写任意 JavaScript 表达式,包括使用三元运算符根据计数值展示特定的文案。 为了触发状态变化,组件将 `addToCount` 方法绑定到按钮的 `onAction` 回调上。当点击递增时,就会传入增量 1 并调用 `setState`。Jordan 将其类比为状态转移:“如果我们的组件是一个状态机,那么 setState 就是你在状态机上执行状态转移的方式。”(“if our components are a state machine then set state is how you perform State transitions on the state machine”)该操作会将下一次的计数值更新为当前计数值加上增量。 - `getInitialState`:组件生命周期内用于声明默认状态的方法。 - `setState`:触发状态转移的核心 API,接收增量更新并重新调度渲染。 - 视图与状态绑定:`render` 内直接读取 `this.state`,数据变动即保证视图同步。 --- ### [12:17](https://www.youtube.com/watch?v=GW0rj4sNH2w&t=737s) React 的两阶段生命周期与 DOM 协调(Reconciliation)机制 随后,Jordan Walke 拆解了 React 组件生命周期的两大核心阶段。第一阶段是初次渲染(Initial Rendering):为了最大程度保证灵活性,React 会递归遍历整个组件层级树,先在内存中计算出一个庞大的标记字符串(Markup string)并注入目标挂载点;紧接着是该阶段的第二步——实例化所有底层对象并挂载事件处理器。Jordan 指出,这两个步骤在物理上甚至可以发生在不同的机器上:服务器端可以先计算并输出纯 HTML 标记字符串,再由客户端接管去实例化视图并绑定事件。 生命周期的第二阶段发生在数据变更时,系统需要保持 UI 的最新状态,这一过程被称为“协调”(Reconciliation)。React 会递归比对前后两次 `render` 调用的输出,计算出能够更新 UI 的最小差异集合(Smallest diff)。即使变更只是一个嵌套在 50 层深度下的 DOM 节点的类名、样式或单行文本,React 也只会将序列化的补丁指令通过逻辑桥传递过去,并精准执行最少量的 DOM 突变。例如把按钮文案从“Book flight”改为“Book hotel”时,系统唯一执行的 DOM 操作就是修改对应 `<span>` 的文本内容,期间甚至完全不会去读取真实 DOM。 ```mermaid flowchart TD accTitle: React 组件生命周期与更新流程 accDescr: 展示初次渲染的两步流程以及数据变更后的协调与最小 DOM 更新机制 subgraph InitialPhase ["阶段一:初次渲染 (Initial Rendering)"] A["递归遍历组件树"] --> B["计算生成 Markup 标记字符串"] B --> C["实例化底层对象并绑定事件监听"] style B stroke-dasharray: 5 5 end subgraph ReconcilePhase ["阶段二:数据变更与协调 (Reconciliation)"] D["状态变更 (setState)"] --> E["执行 render() 生成新虚拟树"] E --> F["递归比对前后输出 (Diffing)"] F --> G["生成最小补丁并精准应用到 DOM"] end InitialPhase --> ReconcilePhase ``` - 初次渲染允许同构解耦:服务端输出 HTML 字符串,客户端挂载行为逻辑。 - 协调算法(Reconciliation):通过最小 Diff 计算,避免全量重绘。 - 性能保障:更新过程中只执行必要的写操作,避免多余的 DOM 属性读取。 --- ### [14:20](https://www.youtube.com/watch?v=GW0rj4sNH2w&t=860s) 逃生通道(Escape Hatches)与性能剪枝 面对技术大会演讲中常见的“玩具示例”质疑,Jordan 坦承演示确实很精简,但强调 React 在架构设计上预留了关键的“逃生通道”(Escape Hatches)。一方面,开发者可以轻松将现有代码库或第三方类库引入 React;另一方面,逃生通道允许开发者介入虚拟比对过程。由于寻找最小 DOM 差异在复杂树形结构下需要计算开销,React 允许开发者手动对搜索空间进行“剪枝”(Pruning),帮助框架更快找到最小变更。 Jordan 特别指出,这种性能优化完全是可选的(Optional),代码即便完全不写优化分支也能正确运行,区别仅仅在于运行速度的快慢。React 提倡将性能优化视为开发流程中完全独立的一个环节:“不要一开始就做优化,而是要定位瓶颈,并在必要的地方进行应用。”(“you don't start with the optimizations but you you identify the bottlenecks and apply them where necessary”)随后,Jordan 将麦克风交还给 Tom Occhino。 - 逃生通道用途一:允许直接访问底层 DOM 节点以集成旧项目代码或第三方插件。 - 逃生通道用途二:提供搜索剪枝机制,手动跳过不必要的虚拟子树比较。 - 架构哲学:优化与功能解耦,防止过度过早优化污染核心业务逻辑。 --- ### [15:26](https://www.youtube.com/watch?v=GW0rj4sNH2w&t=926s) 内部落地演进:从 Facebook 复杂业务到 Instagram 推动开源 Tom Occhino 接过发言,深入解释了逃生通道对渐进式重构的价值。在实际工程中,开发者随时可以通过接口直接获取真实 DOM 节点,并自由书写原生 JavaScript,以此轻松桥接自动伸缩输入框(Auto-growing textarea)或自动补全(Autocomplete)等既有插件。Tom 坦言,这正是 React 能够在 Facebook 取得成功的关键原因:工程团队不必大费周章地全盘重构现有系统,而是可以按业务模块进行零星、渐进的零碎替换(Piecemeal)。 回顾项目发展历程,Tom 透露 React 的研发早在 18 个月到两年前就已经起步。在演讲当时,Facebook 内部已经在海量核心产品线中重度依赖 React,包括点赞、评论组件、聊天窗口(Chat)、好友列表(Buddy list)以及通知中心(Notifications),其稳定性和开发体验都得到了长期验证。 大约一年前 Instagram 加入 Facebook 时,带来了一个前所未有的工程挑战:一个原本不在 Facebook 基础设施体系内的独立团队,希望能采用这套工具。以往在 JSConf 演讲时,团队总以“技术与内部基础设施耦合太深、难以剥离”为由无法对外共享。而 Instagram 团队迫切的使用意愿成为了极佳的“倒逼机制”(Forcing function)。受大会演讲者 Rebecca 提出的“按照开源的标准去写内部代码”这一理念启发,团队对 React 的源码、构建流程和工具链进行了彻底的重构,使之完全脱离 Facebook 专属基础设施。 - 渐进集成:允许混用第三方原生插件,无需推翻现有代码重写。 - 内部验证范围:评论、点赞、网页端聊天框、好友列表与通知。 - Instagram 实践:加入后仅用一两周时间,就使用 React 完全构建了全新的 instagram.com 用户个人主页(Profiles)。 --- ### [17:53](https://www.youtube.com/watch?v=GW0rj4sNH2w&t=1073s) 正式开源发布与社区交流 作为对外部团队迁移实践的总结,Tom 表示 Instagram 团队的全站实践为 React 提供了面对“外部客户”的实战演练,随之而来的逻辑演进方向就是正式面向社区开源。在此刻,Tom 宣布了一个重磅消息:“顺便说一句,我们大约在半小时前刚刚完成了开源发布。”(“incidentally we did that about a half hour ago”)现场随即爆发热烈的掌声。 在演讲尾声,Tom 表现得十分谦逊。他强调 React 只是 Facebook 内部使用且非常顺手的一套方案,绝非宣扬它是唯一的标准答案。团队极其渴望倾听外部开发者的反馈,并宣布已经在 GitHub 上公开了代码库,同时设立了 Google Group 讨论组和 IRC 聊天室,欢迎全球开发者尝试、提 Issue 并参与讨论。 - 开源节点:在本次 JSConfUS 2013 演讲开始前约 30 分钟正式推送到公网。 - 社区渠道:GitHub Issues、Google Group 邮件列表、IRC 在线聊天频道。 - 官方态度:保持谦逊,积极收集社区在多样化复杂场景下的反馈与建议。 ## Notable quotes > [10:12](https://www.youtube.com/watch?v=GW0rj4sNH2w&t=612s) if our components are a state machine then set state is how you perform State transitions on the state machine — Jordan Walke > [00:19](https://www.youtube.com/watch?v=GW0rj4sNH2w&t=19s) This observable model pattern encourages mutation though and mutation is complex. — Tom Occhino > [14:20](https://www.youtube.com/watch?v=GW0rj4sNH2w&t=860s) you don't start with the optimizations but you you identify the bottlenecks and apply them where necessary — Jordan Walke > [02:20](https://www.youtube.com/watch?v=GW0rj4sNH2w&t=140s) We want to build something that gives us the conceptual simplicity of rendering from scratch on every single event, but you know without the overhead because that's prohibitively expensive. — Tom Occhino ## Mentioned Tom Occhino, Jordan Walke, Facebook, React, JSX, Instagram, JSConf, Dojo, AngularJS, Rebecca Murphey, GitHub, Google Groups, IRC [Source](https://www.youtube.com/watch?v=GW0rj4sNH2w)

Source quotes

[10:12] Jordan Walke: if our components are a state machine then set state is how you perform State transitions on the state machine [0:19] Tom Occhino: This observable model pattern encourages mutation though and mutation is complex. [14:20] Jordan Walke: you don't start with the optimizations but you you identify the bottlenecks and apply them where necessary [2:20] Tom Occhino: We want to build something that gives us the conceptual simplicity of rendering from scratch on every single event, but you know without the overhead because that's prohibitively expensive.

Source quotes

[2:37] Tom Occhino: We want to build something that gives us the conceptual simplicity of rendering from scratch on every single event, um, but you know, without the overhead because that's prohibitively expensive.

Original transcript

[0:00] [Music] [0:19] uh I'm Tom okino thank you Chris first [0:21] of all uh I'm going to talk a bit about [0:23] how we do JavaScript application [0:24] development at Facebook we're going to [0:25] do something a little different Jordan [0:27] is actually also going to to present [0:29] he's going to do some of the technical [0:30] stuff I'm going to talk about the fluffy [0:31] I'll get it out of the way quick uh [0:34] first of all we asked ourselves this [0:35] question many many times how should we [0:37] structure JavaScript applications and [0:39] more specifically JavaScript [0:40] applications that run in a web browser [0:42] we're using node and doing all that fun [0:43] stuff too but I'm more interested in the [0:45] user interface side of things so that's [0:46] what we're going to talk about [0:48] today there are a lot of JavaScript [0:50] Frameworks that aim to answer this [0:52] question uh these Frameworks have [0:54] allowed us to move the web forward as a [0:56] real application platform if Peter [0:58] Higgins is the room I want to call out [0:59] that dojo is up here cuz Dojo did it [1:01] first as he would say or Dojo already [1:04] did that um but the consensus is [1:07] basically that an MVC style architecture [1:09] is best and it's not just MVC right it's [1:11] mvvm or as angular calls it model view [1:14] whatever I really like that one I [1:16] actually really like angular in the the [1:17] declarative data binding I think that's [1:19] kind of the Holy Grail in terms of [1:21] apis all of these architectures in [1:23] Frameworks have one thing in common [1:25] though and that's models so typically [1:29] models in these Frameworks Works [1:30] Implement observable objects with an [1:33] advents [1:34] API developers use bir directional data [1:37] binding to bind their views to their [1:40] models as your models change your views [1:42] can be updated and as your views change [1:44] they can update the models which then [1:46] propagates out to other views excuse [1:49] me so this observable model pattern [1:52] encourages mutation [1:54] though and mutation is [1:56] complex so a few years ago starting with [2:00] our chat rewrite we tried to start [2:03] structuring our application a little bit [2:05] differently we wanted to minimize the [2:08] amount of mutation that developers had [2:09] to deal with mutation is a necessary [2:11] evil you have to deal with it but we [2:13] want to minimize the amount of mutation [2:15] that developers have to code themselves [2:17] so let me show you what I mean by that [2:20] here's the structure for a simple [2:22] application in this diagram you'll [2:24] notice that all updates go through a [2:26] single flow so all updates including [2:29] user input they go through a single [2:31] Channel this is one directional data [2:34] binding those updates eventually reach [2:36] the [2:37] views and the simplest way that we have [2:40] found conceptually to structure and and [2:42] render our views is to just try and [2:45] avoid mutation Al together so what I [2:48] mean by that is anytime any of your data [2:51] changes just blow away your view [2:53] completely and render it from [2:55] scratch now I know what you're thinking [2:58] that's not going to work right the Dom [2:59] can be slow you know browsers are going [3:02] to be it's going to be prohibitively [3:03] expensive to try and do this to try and [3:05] just Reiner HTML everything every time [3:07] anything changes especially in a [3:09] real-time app or something that's really [3:11] interactive so you know conceptually [3:14] this is what we're going for though this [3:16] is what we want we want to build [3:17] something that gives us the conceptual [3:19] Simplicity of rendering from scratch on [3:21] every single event um but you know [3:24] without the overhead because that's [3:25] prohibitively expensive so about 18 [3:28] months to 2 years ago and missing the [3:31] exact date uh we started working on a [3:33] JavaScript library that helps us do that [3:34] and we call it react so that was all the [3:36] fluffy stuff uh and I'm going to let [3:38] Jordan come up and talk about some of [3:40] the the more technical details of [3:44] this thanks all right so like Tomo was [3:47] saying uh one of the things that we [3:49] strive for when we when we're um [3:50] building our component framework is that [3:52] we want to minimize the amount of [3:54] developer facing mutations uh that the [3:56] developer exposed to and so react takes [3:59] a approach in a few ways the first is [4:01] that it's highly declarative and so that [4:04] doesn't that means that not just your [4:05] initial rendering but also a lot of your [4:07] updates are implemented via some kind of [4:09] a declaration it's a non- mutative [4:12] description of what your UI should [4:14] be okay so we don't have observable data [4:17] binding inside of react if we don't ship [4:19] with it um I I imagine you could [4:22] actually use it with react but a lot of [4:24] the things that you might reach to obser [4:25] observable data binding for in order to [4:27] implement uh react provides a different [4:29] way to do that and declaration is the [4:30] way that we do [4:31] that so along with react we are shipping [4:35] an embeddable XML syntax and we're [4:37] calling that [4:38] jsx so this is similar to a lot of the [4:41] work and language proposals that might [4:42] have happened in the past but we're [4:45] unique in a few ways and I'll let you [4:47] take a look at what that looks like [4:48] first okay so here we have react used [4:51] with [4:52] jsx and so we are creating a message [4:55] variable and we're assigning it to a [4:57] react div instance [5:00] and that has an a nested inner div there [5:02] too so these divs are not exactly what [5:06] you're used to they're not Dom nodes so [5:09] they're special reactives and I'll get [5:10] to why that's [5:12] important at the bottom there we render [5:16] it so one thing that makes jsx unique is [5:20] that it's a very lightweight transform [5:22] all we do is transform these tags into [5:24] function [5:25] invocations so it's not quite as nice [5:27] looking but you can see it even [5:29] preserves the line the line number so if [5:31] you pass this to your linter it'll catch [5:33] like tags that are out of scope your [5:35] lint messages will appear in the right [5:36] line and everything's low tooling is the [5:39] key here um but jsx is also optional if [5:44] you want to build with react so if you [5:45] like this you can write that but I think [5:48] you guys would agree that this is a lot [5:50] better um so react is more than just [5:53] about Dom nodes and Dom components but [5:57] react is primarily a way for you to [5:58] abstract reusable code and hide [6:00] implementation details of your [6:02] application UI so the primary focus of [6:05] react is allowing you to build custom [6:09] components so the way that you use a [6:11] custom component is a lot like the way [6:12] that you would use a div or a span we [6:14] can use the XML syntax jsx to [6:16] instantiate it and so we have XML [6:19] attributes text and an onaction callback [6:22] provided here and these become the [6:24] properties of your view instance or of [6:26] your react component [6:28] instance and so so you probably want to [6:30] check out how we implement or Define [6:34] this action button a couple things just [6:37] to know is that we we make one call to [6:40] react. create class and then we also [6:42] Supply a render function so what this [6:45] does is creates a class um to model your [6:48] component and it will extend or it will [6:51] mix in all the base functionality of the [6:53] react uh of the react base so react is [6:57] not an objectoriented focused framework [6:59] though uh we use a little bit of [7:01] inheritance just because that lends [7:02] itself naturally to what JavaScript is [7:04] good at um but other than that the core [7:06] Paradigm here isn't objectoriented so I [7:09] thought I would call that out so you can [7:10] supply other methods here and they get [7:12] mixed into your [7:14] prototype and so the moment you define [7:17] the action button you can actually [7:19] reference it it becomes in scope and [7:21] your lyncher won't complain if action [7:23] button wasn't in scope you know your [7:25] lyncher would say you know this is not [7:27] defined so it's a very JavaScript Cent [7:30] language this [7:32] jsx and so render has one purpose and [7:35] that is just is to describe your view at [7:37] any point in [7:39] time so that doesn't mean just the [7:41] initial render but at any point in time [7:43] so let's go through what that might look [7:45] like over a series of [7:47] updates okay so we're starting out with [7:49] a shell here we have an outer button and [7:50] an inner [7:52] span so we want to do something [7:54] interesting with the properties that [7:55] were given we want to be able to [7:57] interpret them into a user experience so [7:59] what we do is we take the property text [8:02] and Route it down into the inner span [8:05] and the way you do that is you can refer [8:06] to these XML attributes that are passed [8:08] in Via this. [8:10] props so you can see there in the center [8:12] we're we're routing the book flight text [8:15] into the in [8:16] Span but these XML attributes don't just [8:19] have to be strings because we're in [8:20] JavaScript in jsx and react is [8:23] Javascript [8:24] focused so we have a function that we're [8:26] being supplied here in the onaction [8:28] Callback and we're routing that to the [8:30] buttons on click so an action buttons on [8:32] action is a Dom buttons on click in this [8:37] case and so it helps to view at least [8:39] initially a react component as a [8:42] transform from these properties into a [8:43] user experience and you can just kind of [8:45] Imagine these being routed to their [8:47] proper [8:50] locations but react is more than just a [8:52] routing tool you can do any kind of [8:54] transform that you or you can do any [8:55] JavaScript expression that you want [8:56] inside of these interpolation braces so [8:59] that means that we can uppercase the [9:00] text and so anytime the text changes [9:02] this will always be [9:03] uppercased so like I mentioned earlier [9:06] render isn't just your initial isn't [9:08] just your initial rendering but it's [9:09] actually your rendering over all points [9:11] in time so as your properties [9:14] change as your properties change the [9:16] render output change changes and then [9:18] react satisfies your render function [9:21] it'll make sure that the user [9:22] experiences render exactly as you've [9:24] described at any point in [9:26] time and so what react will do is it [9:28] will find the minimal set of Dom [9:30] mutations needed to make that happen so [9:32] let's say that your uh [9:35] your let's say that your text had [9:37] changed so react will find that the only [9:40] thing that needs to change is that inner [9:41] innermost spans text content so I'll [9:45] explain a little bit more about how that [9:48] works but uh but the key here is that [9:51] you can [9:52] reabstracted components we already saw [9:55] the action button and that was a simple [9:57] and stateless component but you're [9:59] you'll eventually want to build [10:00] something more complex something more [10:01] like an application it will have its own [10:03] internal [10:05] state so let's do [10:07] that so this is the simplest stateful [10:10] component that you could imagine it's [10:12] the counter has an increment and a [10:13] decrement button and you'll see that [10:15] we're actually reusing our action button [10:18] that we created in the last example and [10:20] so we have two action buttons one that [10:22] increments one that [10:24] decrements so this is [10:27] new if you want to build an application [10:29] and you don't just want simple stateless [10:30] views you want to be able to Define some [10:33] kind of a state machine you want your [10:34] component to be able to assume the role [10:36] of a state machine in some way and so [10:38] State machines need an initial state so [10:40] get initial state is a special function [10:42] that react allows you to implement in [10:44] order for you to Define what your [10:45] initial state is so in this case we're [10:48] setting the count member of our state to [10:51] be equal to the initial count property [10:53] that was passed in and the example there [10:55] passes four so we're going to initialize [10:58] our state to be four [11:03] so this is where it gets really [11:05] interesting the same way that we could [11:06] refer to props in our render [11:09] function and assume that they're always [11:11] up to date because react ensures that we [11:13] can also refer to State inside of a [11:15] render function and assume that they're [11:16] always up to date so anytime your [11:18] Properties or your state [11:20] changes render will render will remain [11:23] um Satisfied by the react [11:26] system so the the content of that H3 [11:29] there will always show the states count [11:31] and you can put any expression there [11:33] again you can put a turn are that [11:34] decides what to render there special [11:36] messaging depending on like certain [11:40] counts okay so to make these State [11:43] changes actually [11:44] happen we bind the add to count method [11:47] to our action buttons on action and so [11:50] when we increment one we're going to add [11:52] to count passing in one because we bound [11:54] it there in the call back and so what we [11:57] do is we set state which is like a State [11:59] transition if our components are a state [12:01] machine then set state is how you [12:03] perform State transitions on the state [12:04] machine and what we do is we set the [12:07] next count to be equal to the current [12:09] count plus the [12:12] Delta all right so how does this work [12:17] um so there's two main passes here or [12:20] there's two main stages to the react [12:22] component life um life cycle there's the [12:25] initial rendering and so what we do for [12:28] the sake of flexibility ility is we [12:30] compute a giant markup [12:33] string and so react will Traverse your [12:35] entire component hierarchy tree and will [12:38] create a markup string that it injects [12:39] into wherever you render it now there's [12:42] a second phase to that initial to that [12:44] initial stage there and that is that we [12:46] instantiate all the backing objects and [12:48] attach all the event handlers what's [12:50] interesting here is that these two [12:51] things can occur in two different places [12:54] on your system two different computers [12:56] um anywhere in the world so you could uh [13:00] you could in theory render the markup on [13:02] a server and then attach all the event [13:05] handlers and instantiate all the backing [13:07] views on the client two completely [13:09] separate [13:10] machines we could talk a lot more about [13:12] that too um but the second uh primary [13:16] phase of a component's life cycle is [13:19] when these changes in data happen so we [13:21] need to make the UI fresh and that's [13:23] what reconciliation is is it's updating [13:26] your U it's making sure that your UI is [13:28] updated anytime any of your data sources [13:31] change and so how this works is that it [13:34] recursively Compares subsequent [13:36] invocations of render and it takes the [13:38] outputs of those and it creates uh the [13:40] smallest diff possible to make sure that [13:42] your UI is is up to date so if only one [13:45] little Dom node changes or if only one [13:47] class name or one style attribute on on [13:49] a Dom node that's possibly 50 levels [13:51] deep in your view hierarchy changes [13:53] we'll will serialize these diff commands [13:55] basically over this logical bridge to [13:57] this other part of the system that will [13:59] apply that smallest [14:03] mutation and so again just to illustrate [14:05] that in more depth here if this text [14:07] changes from book flight to book hotel [14:09] only that span um the only Dom command [14:12] that we'll ever execute is to make sure [14:14] that that span's inner text has changed [14:16] we won't even read from the Dom [14:19] actually okay so anybody that's giving a [14:22] presentation at a conference is going to [14:24] show you the most contrived examples and [14:26] uh so react is no different but uh [14:30] there's also an escape hatch or several [14:31] Escape hatches that you can that you can [14:33] tap into in the react system so some of [14:35] the things you're going to want to do is [14:36] you're going to want to integrate with [14:37] your existing Frameworks or your [14:39] existing code and so we allow plenty of [14:41] ways to let you to let you do that and [14:44] um and another more interesting Escape [14:46] PCH that we have is a way for you to be [14:48] able to help guide this process along so [14:50] react is finding this diff the smallest [14:52] possible set of Dom changes that need to [14:54] be applied and that can take some time [14:57] so what we allow is we allow you to [14:59] prune out a lot of the search space and [15:01] help react um and help guide react along [15:04] to find these smallest comp these [15:05] smallest diffs and so those are [15:08] completely optional you can you can put [15:09] them in your code or you can take them [15:10] out and the same application runs just [15:13] faster or slower so it's treat it's it's [15:16] uh it's treating this optimization step [15:18] as a complet a completely separate part [15:20] of development it's not core you don't [15:21] start with the optimizations but you you [15:24] identify the bottlenecks and apply them [15:25] where [15:26] necessary okay so I'm going to kick it [15:28] back to Tom [15:29] and he's going to tell a little bit [15:30] about the history of react in the [15:33] future thanks Jordan yeah so the [15:36] interesting thing about that escape [15:37] hatch is that's actually pretty [15:39] important to being able to iterate on [15:40] your product over time so another hook [15:42] that you get is basically at any point [15:44] you can say just give me a Dom node and [15:46] let me write JavaScript so one of the [15:48] examples that we had up here was um and [15:50] we just trimmed it for Content but like [15:52] you can basically say okay I have this [15:54] other plug-in from some other framework [15:56] that does like Auto growing text areas [15:58] or that as like an autocompleter and you [16:01] can basically wire those things up via [16:03] that escape hatch really easily and this [16:04] is actually why this framework was able [16:06] to be successful at Facebook we didn't [16:08] have to convert everything we could use [16:09] it kind of peac meal um but yeah I mean [16:13] this started a long time ago this [16:14] started probably 18 months ago somewhere [16:17] between 18 months and two years ago and [16:19] you know at this point we're using this [16:20] all over facebook.com if you've ever [16:22] commented on anything on Facebook or [16:24] liked anything um if you've ever talked [16:26] in chat or integrated or used your buddy [16:28] list [16:29] um or even use your notifications [16:31] there's a bunch of different places [16:32] where we've been using this for a really [16:34] long time and it's been working out [16:36] really really well for us so about a [16:38] year ago when Instagram joined Facebook [16:41] uh we had this this is the first time [16:42] that this ever happened to us we had [16:44] somebody else who wasn't on Facebook's [16:46] infrastructure that wanted to use [16:48] Facebook's infrastructure um and you [16:50] know we've presented a bunch of stuff at [16:51] JSC in the past and you know [16:53] historically our our you know excuse has [16:55] kind of been you know it's pretty tough [16:57] to divorce things from our [16:58] infrastructure [16:59] but Instagram joined us and they really [17:01] wanted to use this and they sort of [17:02] acted like a forcing function for us uh [17:05] Rebecca made a great comment about [17:06] writing your code as if it's open source [17:08] so what we started to do is we just went [17:09] through all of react and all of the [17:11] build steps and all of those tool all [17:13] the tooling behind it and we built it or [17:16] you know refactored it and rebuilt it [17:18] such that it was able to be open sourced [17:20] so that Instagram could use it on [17:23] instagram.com in fact instagram.com was [17:25] their first project that they wanted to [17:26] take on after they joined Facebook and [17:29] in a matter of a week or two they built [17:31] all of instagram.com profiles on this [17:33] system which was amazing for us we were [17:36] really excited about that uh they [17:38] basically again they acted as a forcing [17:40] function for us to kind of get our house [17:41] in order and you know just you know [17:44] divorce our our infrastructure from this [17:46] library and and some other libraries so [17:49] you know all of instagram.com is on [17:51] react now and you know it gave us [17:53] practice with somewhat of an external [17:55] client so The Logical next step for all [17:57] the people that have been working on [17:58] this this is you know you know we want [18:00] to open source this and actually [18:03] incidentally we did that about a half [18:04] hour [18:05] ago so if you check out Facebook thank [18:10] you so again you know I want to be [18:13] really humble here this is just [18:14] something that we're using it's [18:15] something that works really well for us [18:17] we're really interested to get any [18:18] feedback we can you know definitely [18:20] check out the link uh you know we're [18:23] we're really open to feedback there's a [18:24] Google group there's an IRC chat room um [18:27] there's you know all sorts of like on [18:29] GitHub there's a you know issues and [18:31] stuff there so uh you know that's react [18:34] and you know thank you [18:37] [Applause]