本文深入解析React框架的核心原理,对比原生JavaScript DOM操作与React的虚拟DOM机制。通过React.createElement和JSX语法示例,详细阐述组件创建、属性传递及渲染流程,帮助开发者理解React如何简化Web应用开发并提升代码可维护性。
深入解析 React:从原生 JavaScript 起步
作者:Stéphane Bégaudeau
原文链接 https://www.sbegaudeau.com/2018/10/01/from-vanilla-javascript-to-react.html
React 是构建用户界面的 JavaScript 框架,旨在通过动态操作页面内容来开发 Web 应用。尽管浏览器已提供 DOM API 来创建元素,但理解 React 的价值及其与 DOM 的关联至关重要。
原生 JavaScript 与 DOM 交互
JavaScript 提供了全局作用域下的对象和函数以构建应用。在 Web 环境中,开发者可直接访问文档对象模型 (DOM) API;而在 Node.js 等非浏览器环境中,则需导入如 JSDOM 等替代实现。
DOM 提供了操作 HTML 文档的 API,允许以任意方式修改页面结构。通过全局document对象,开发者即可开始使用这些功能。
从document对象出发,开发者可以便捷地创建新元素、修改属性,并将其作为子元素添加到其他节点中。借助 DOM,即便过程繁琐,也能以编程方式生成任何 HTML 文档。
以下示例展示了如何在 HTML 文档中通过编程方式创建一个简单的标题元素。
<!DOCTYPE html>
<html>
<head>
<script src="app.js"></script>
</head>
<body>
<div id="app" />
</body>
</html>具体操作是创建一个 h1 元素,并将其插入到 HTML 页面的 body 中。
// The document object is accessible since it is in the global scope
const h1Element = document.createElement('h1');
h1Element.setAttribute('class', 'title');
const textElement = document.createTextNode('I am Groot');
h1Element.appendChild(textElement);
// document.getElementById('app') will retrieve the div with the identifier app
document.getElementById('app').appendChild(element);上述代码首先创建了一个新元素,随后向该 h1 元素添加了一个名为 class、值为title的属性。接着,代码创建了一个文本节点,内容为 'I am Groot',并将其作为子元素添加到 h1 中。最后,通过 HTML 文档获取 id 为 app 的 div 节点,将 h1 标签追加其中。执行该代码后,生成的 HTML 文档结构如下所示:
<!DOCTYPE html>
<html>
<head>
<script src="app.js"></script>
</head>
<body>
<div id="app">
<h1 class="title">I am Groot</h1>
</div>
</body>
</html>利用 DOM,开发者还可通过 className 属性直接操作元素的类名(因为 class 是 JavaScript 的保留字)。因此,以下代码能产生完全相同的效果。
const h1Element = document.createElement('h1');
// h1Element.setAttribute('class', 'title');
h1Element.className = 'title';
const textElement = document.createTextNode('I am Groot');
h1Element.appendChild(textElement);
document.getElementById('app').appendChild(element);React 核心机制解析
多数 React 教程直接展示其高级用法。本文采取不同策略,从编写通常不再手动维护的 React 代码入手,以深入理解其工作原理。
React 的设计初衷是服务于 Web 场景,其部分 API 设计灵感源自 DOM。为阐明这一点,我们将探讨 React 的核心 API 之一:React.createElement。
要利用 React 操作 DOM,需引入 React 和 ReactDOM 两个依赖库。React.createElement 用于创建轻量且高效的虚拟 DOM 数据结构,以表征用户界面;ReactDOM 则负责将虚拟 DOM 渲染至浏览器真实的 DOM 中。
React.createElement 函数接收三个参数以构建虚拟 DOM 元素:
- 待创建元素的标签名称
- 元素的属性对象
- 元素的子节点
import React from 'react';
const name = 'h1';
const props = { className: 'title' };
const children = 'I am Groot';
const element = React.createElement(name, props, children);React.createElement 同样支持接收一个包含所有子元素的数组作为参数。
import React from 'react';
const name = 'h1';
const props = { className: 'title' };
const children = ['I am Groot'];
const element = React.createElement(name, props, children);children 参数本质上也是元素的一个常规属性,因此它可以作为 props 对象的一部分存在。
import React from 'react';
const props = {
className: 'title',
children: ['I am Groot']
};
const element = React.createElement('h1', props);要在 DOM 中渲染该元素,需指定其挂载点。在本例中,即选取 id 为 app 的 div 元素,并指示 ReactDOM 进行渲染。
import React from 'react';
import ReactDOM from 'react-dom';
const props = {
className: 'title',
children: ['I am Groot']
};
const element = React.createElement('h1', ...props);
ReactDOM.render(element, document.getElementById('app'));文中所有代码示例均可通过引入未压缩版本的 React 和 Babel 进行本地测试。此类配置仅适用于简单测试,因其未经过生产构建的优化。在此特定场景下,应移除 react 和 react-dom 的全局导入(此处均作为全局变量暴露)。
<!DOCTYPE html>
<html>
<head>
<title>React</title>
<script src="https://unpkg.com/react@16/umd/react.development.js"></script>
<script src="https://unpkg.com/react-dom@16/umd/react-dom.development.js"></script>
<script src="https://unpkg.com/babel-standalone@6/babel.min.js"></script>
<script type="text/babel">
const props = {
className: 'title',
children: ['I am Groot']
};
const element = React.createElement('h1', props);
ReactDOM.render(element, document.getElementById('app'));
</script>
</head>
<body>
<div id="app" />
</body>
</html>轻松掌握 JSX 语法
尽管上述方法能构建所有 Web 页面,但其代码冗长。为简化 DOM 操作,React 提供了一种名为 JSX 的简洁且强大的语法扩展。
构建工具中的预处理器会在构建阶段将 JSX 转换为标准 JavaScript。常规 React 项目利用预处理器将 JSX 代码编译为对 React.createElement 的调用。因此,JSX 并非由 React 直接解释,开发者甚至可以在完全不使用 JSX 的情况下使用 React。故以下两段代码功能完全等价:首先,以编程方式使用 React:
import React from 'react';
const props = { className: 'title' };
const children = ['I am Groot'];
const element = React.createElement('h1', props, children);或使用 JSX 语法声明:
import React from 'react';
const element = <h1 className="title">I am Groot</h1>;由于 JSX 代码最终会被编译为对 React.createElement 的调用,因此必须导入 React,即便在 JSX 中看似未直接使用它。
使用 JSX 能以声明方式快速构建大部分 DOM,而 React 底层仅识别对 React.createElement 的调用。鉴于 JSX 元素本质上是对 React.createElement 的封装,children 依然是常规属性。因此,前述示例也可按如下方式编写:
import React from 'react';
const element = <h1 className="title" children="I am Groot"/>;借助 JSX,开发者可通过花括号语法访问变量:
import React from 'react';
const title = 'title';
const text = 'I am Groot';
const element = <h1 className={title} children={text}/>;当然,我们也可以将变量直接命名为期望操作的属性名:
import React from 'react';
const className = 'title';
const children = 'I am Groot';
const element = <h1 className={className} children={children}/>;这使我们能够利用扩展语法(Spread Syntax)来编写更简洁的代码。
import React from 'react';
const props = {
className: 'title',
children: ['I am Groot']
};
const element = <h1 {...props}/>;最后,我们可以像之前一样,在 DOM 中通过 React.createElement 渲染该元素。
import React from 'react';
import ReactDOM from 'react-dom';
const props = {
className: 'title',
children: ['I am Groot']
};
ReactDOM.render(<h1 {...props}/>, document.getElementById('app'));既然已使用 JSX 通过 React 渲染了首个虚拟 DOM 片段,接下来我们将探讨如何利用 React 构建基本应用程序。
立即行动,利用 React 组件开发更多动态交互代码。
以上即为从原生 JavaScript 到 React 深入理解的完整内容,更多关于原生 JavaScript 到 React 的资料请关注本站其它相关文章!