本章将通过编程世界通用的“Hello World”程序,告诉你:创建 React 应用的几种方法、虚拟DOM、JSX以及将 React 挂载到真实 DOM 的步骤。
本专题是快速入门级别的,如果你想深入、完整的学习 React,这里推荐尚硅谷的免费 React 视频教程(为了不分散你的注意力,该教程的链接地址将在专题结束之后放出),以及 React官方文档(中文)。
DOM 与虚拟DOM
你应该知道什么是 DOM,如果感觉不是很能清楚描述,可以看看这个文档的描述。我们重点关注虚拟DOM(Virtual DOM,VDOM)。
React 官网的描述是:
Virtual DOM 是一种编程概念。在这个概念里, UI 以一种理想化的,或者说“虚拟的”表现形式被保存于内存中,并通过如 ReactDOM 等类库使之与“真实的” DOM 同步。这一过程叫做协调。
这种方式赋予了 React 声明式的 API:您告诉 React 希望让 UI 是什么状态,React 就确保 DOM 匹配该状态。这使您可以从属性操作、事件处理和手动 DOM 更新这些在构建应用程序时必要的操作中解放出来。
与其将 “Virtual DOM” 视为一种技术,不如说它是一种模式,人们提到它时经常是要表达不同的东西。在 React 的世界里,术语 “Virtual DOM” 通常与 React 元素关联在一起,因为它们都是代表了用户界面的对象。而 React 也使用一个名为 “fibers” 的内部对象来存放组件树的附加信息。上述二者也被认为是 React 中 “Virtual DOM” 实现的一部分。
叭啦叭啦一大堆,说了又好像没完全说,这就是官网的统一气质。我们对这段晦涩难懂的文字进行归纳:
- VDOM是一种概念,是一种模式
- 正如其名,VDOM是虚拟,它是被保存在内存中的
- VDOM最终会与真实DOM同步,也就是说,最终会变成真实DOM呈现在页面上
- VDOM通过状态改变UI的呈现
- VDOM 本质是
Object
类型的对象(一般对象) - 虚拟 DOM 比较“轻”,真实 DOM 比较“重”。因为虚拟 DOM 是 React 内部在用,无需真实 DOM 上那么多属性。
- 虚拟 DOM 最终会被 React 转化为真实 DOM,呈现在页面上。
JSX
我们先来看一下 JSX 长什么样:
const element = <h1>Hello, world!</h1>;
- 它不是字符串,因为没有被引号包裹
- 它更像是 HTML 标签,但它直接放在了 JavaScript 代码里
React 官网的描述是:
JSX 是一个 JavaScript 的语法扩展。我们建议在 React 中配合使用 JSX,JSX 可以很好地描述 UI 应该呈现出它应有交互的本质形式。JSX 可能会使人联想到模板语言,但它具有 JavaScript 的全部功能。
-
JSX 是看起来很像 HTML 的东西
-
它允许直接作为 Javascript 的表达式
-
它可以直接嵌入Javascript表达式,如下:
const name = 'axum.rs'; const element = <h1>你好,{name}</h1>;
-
它其实是语法糖——
React.createElement()
的语法糖
规范
JSX 虽然看起来像 HTML (其实和 XHTML/XML 的表现更像),但它有一些规范需要遵守:
-
必须闭合。比如,在 HTML 中,
<img>
是没有闭合标签的,JSX 要求所有标签必须闭合,所以可以在标签最后加上/
,变成<img />
,比如:(这个规则适用于所有没有或者不需要闭合的标签,包括 HTML 中的<br />
,<hr />
等,也包括自定义组件)const img = <img src="axum-rs-logo.png" alt="AXUM中文网" />;
-
在 VDOM 中,必须有且只有一个根标签
// 错误 const element = <h1>标题</h1><p>内容</p>; // 正确 const element = <div><h1>标题</h1><p>内容</p></div>;
<h1>标题</h1><p>内容</p>
是两个标签,它们不能直接作为根标签放在 VDOM。而是需要将它们包裹在一个标签里。 -
通过
{}
可以在 JSX 里插入 JavaScript 表达式。 -
属性名使用小驼峰命名。HTML 里有许多属性是带中线分割的,比如:
data-site="axum.rs"
,在 JSX 中,需要变成:dataSite="axum.rs"
-
部分重命名的属性。由于 JSX 的本质是语法糖,它其实调用的是一个 Javascript 函数,所以它自身最终也会被转换成 Javascript 代码。对于有些和Javascript关键字同名的 HTML 属性,JSX对它们进行了重新命名:
HTML属性 | JSX属性 |
---|---|
class | className |
for | htmlFor |
-
标签的首字母决定了类型
- 若首字母小写,则转换为 HTML 中同名元素。若 HTML 中没有对应的同名元素则报错
- 若首字母大写,则视为组件, React 会去渲染对应的组件。如果没有对应的组件,则报错
// 将视为 HTML 的 <h1> 标签 const element = <h1>你好,axum.rs</h1>; // 将视为渲染 <H1> 组件 const element = <H1>你好,axum.rs</H1>;
-
内联样式的写法。在 HTML 中,可以通过
style
来定义内联样式,它的值是一堆键值对。在JSX中,需要使用style={{key1:value1,key2:value2}}
的写法。-
虽然本质很简单,但这么多括号难免让人头晕。这里解释一下:
-
style={}
表示style
属性的值是一个JAVASCRIPT表达式 -
{key1:value1,key2:value2}
这是一个典型的 Javascript 语法——简单对象:const obj = {key1:value1,key2:value2};
-
所以,整个写法的意思是,需要把一个JAVASCRIPT 的 Object 作为
style
属性的值
-
你好,React
讲了这么多理论,终于可以来体验一把了。
有很多创建 React 应用的方法,下面一一进行介绍。
通过CDN的方式
这是最简单的方法,通过这个方法可以直接在页面中使用 React。
但是这是最不推荐的方法,请不要在正式环境使用此方法。
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title>axum中文网</title>
</head>
<body>
<div id="root"></div>
<script src="https://cdn.jsdelivr.net/npm/[email protected]/umd/react.production.min.js"></script>
<script src="https://cdn.jsdelivr.net/npm/[email protected]/umd/react-dom.production.min.js"></script>
<script src="https://cdn.jsdelivr.net/npm/@babel/[email protected]/babel.min.js"></script>
<script type="text/babel">
const VDOM = <h1>你好,axum.rs!</h1>;
const root = ReactDOM.createRoot(document.getElementById("root"));
root.render(VDOM);
</script>
</body>
</html>
除了一堆 <script>
,你会发现只有一个 id
为 root
的 div
。
- 【声明容器】第9行:
<div id="root"></div>
,定义一个容器,最终我们会将 React 渲染完成的 VDOM 转换为真实 DOM,并挂载到这个容器上。 - 【依赖】第10~12行:通过CDN引入了三个外部的JS文件(注意,这里引入的版本可能不是最新的,不要纠结这个,只要保证大版本号和这里的一致即可。同时,实际开发中不会用这种方式)
- 第10行:引入
react
- 第11行:引入
react-dom
- 第12行:引入
babel
:用于将 JSX 转换成普通的 JS 代码
- 第10行:引入
- 【书写代码】第13~17行:通过
<script>
嵌入我们的代码- 第13行:注意,
type="text/babel"
,这样才能正确编写 JSX 代码,并让babel
转换成普通 JS 代码 - 【定义入口VDOM】第14行:声明一个变量
VDOM
,它的值是一个 JSX - 【定义React根元素】第15行:声明一个变量
root
,它通过ReactDOM.createRoot()
来创建 React 根元素;它的参数是一个真实 DOM,即我们在第9行的 HTML 中的id="root"
的div
- 【渲染VDOM】第16行:调用
root
的render()
方法,将VDOM
渲染到真实 DOM 中
- 第13行:注意,
通过官方的 create-react-app
React 提供了一个名为 create-react-app
的工具,用于创建 React 应用的脚手架。它使用 webpack
作为打包工具,所以就——很慢、很笨重。
方式一:全局安装
在使用这个脚手架工具之前,你需要首先全局安装这个工具:
npm install -g create-react-app
然后创建我们的应用:
create-react-app 应用名字
比如:
create-react-app hello-react
它需要很长的一段时间,耐心等待吧。
创建完成之后,进入到新创建项目所在文件夹:
cd hello-react
方式二:yarn create
相较于全局安装,我更喜欢使用 yarn create
的方式。这是 yarn
提供的功能,它本质还是调用 create-react-app
,只不过它不需要手动去全局安装。
yarn create react-app hello-react
等待创建完成,然后进入所在目录:
cd hello-react
目录结构
├── README.md
├── package-lock.json
├── package.json
├── public
│ ├── favicon.ico
│ ├── index.html -- 静态页面
│ ├── logo192.png
│ ├── logo512.png
│ ├── manifest.json
│ └── robots.txt
└── src
├── App.css
├── App.js -- 主组件
├── App.test.js
├── index.css
├── index.js -- 入口文件
├── logo.svg
├── reportWebVitals.js
└── setupTests.js
-
public/index.html
:静态页面,提供 React 挂载容器。这个文件很多内容,我们把它精减一下:<!DOCTYPE html> <html lang="en"> <head> <meta charset="utf-8" /> <link rel="icon" href="%PUBLIC_URL%/favicon.ico" /> <meta name="viewport" content="width=device-width, initial-scale=1" /> <title>React App</title> </head> <body> <div id="root"></div> </body> </html>
- 第10行:
<div id="root"></div>
,我们的 React 虚拟 DOM 最终将挂载在这个元素上,并渲染为真实 DOM
- 第10行:
-
src/index.js
:项目入口文件import React from 'react'; import ReactDOM from 'react-dom/client'; import './index.css'; import App from './App'; import reportWebVitals from './reportWebVitals'; const root = ReactDOM.createRoot(document.getElementById('root')); root.render( <React.StrictMode> <App /> </React.StrictMode> ); // If you want to start measuring performance in your app, pass a function // to log results (for example: reportWebVitals(console.log)) // or send to an analytics endpoint. Learn more: https://bit.ly/CRA-vitals reportWebVitals();
- 第1行:引入
React
- 第2行:引入
ReactDOM
- 第3行:引入样式
- 第4行:引入主组件
App
- 第5行:引入
reportWebVitals
,用于性能分析。对于我们来说,直接忽略——你可以直接删除它(同时删除第17行的调用) - 第7行:声明一个变量
root
,它通过ReactDOM.createRoot()
来创建 React 根元素;它的参数是一个真实 DOM,即我们在 HTML 中的id="root"
的div
- 第8~12行:渲染虚拟DOM
- 第9~11行:使用
React.StrictMode
组件。它是 React 内置的组件,用于开启严格模式 - 第10行:使用
App
主组件。注意闭合
- 第9~11行:使用
- 第17行:性能分析的函数调用。对于我们来说,直接忽略——你可以直接删除它(同时删除第5行的引入)
- 第1行:引入
-
src/App.js
:主组件。定义主组件App
。它生成的代码对于现在你来说可能过于复杂,让我们把它改成以下内容:import React from "react"; function App() { return <h1>你好,AXUM中文网</h1>; } export default App;
- 第1行:引入
React
- 第3~5行:声明一个函数,它的名字叫
App
- 第4行:它的返回值是一个 JSX
- 注意,它其实是一个 React 组件:
- 首字母大小
- 返回 JSX
- 第7行:将
App
组件作为默认导出
- 第1行:引入
运行和构建
# 运行
yarn start
# 构建
yarn build
通过 vite
虽然 create-react-app
是官方的,但它实在是太笨重了,而 vite 提供了更轻量的解决方案。【在线体验】👉本教程将使用 vite 来创建 React 项目。
yarn create vite hello-react --template react
目录结构
├── index.html -- 静态页面
├── package.json
├── public
│ └── vite.svg
├── src
│ ├── App.css
│ ├── App.jsx -- 主组件
│ ├── assets
│ │ └── react.svg
│ ├── index.css
│ └── main.jsx -- 入口
└── vite.config.js
-
index.html
:静态页面,提供 React 挂载容器。<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8" /> <link rel="icon" type="image/svg+xml" href="/vite.svg" /> <meta name="viewport" content="width=device-width, initial-scale=1.0" /> <title>Vite + React</title> </head> <body> <div id="root"></div> <script type="module" src="/src/main.jsx"></script> </body> </html>
- 第10行:
<div id="root"></div>
,我们的 React 虚拟 DOM 最终将挂载在这个元素上,并渲染为真实 DOM - 第11行:引入
src/main.jsx
- 第10行:
-
src/main.jsx
:入口文件import React from 'react' import ReactDOM from 'react-dom/client' import App from './App' import './index.css' ReactDOM.createRoot(document.getElementById('root')).render( <React.StrictMode> <App /> </React.StrictMode> )
- 第1行:引入
React
- 第2行:引入
ReactDOM
- 第3行:引入主组件
App
- 第4行:引入样式文件
- 第6~10行:创建React根元素,并渲染
- 第7行和第9行:使用
React.StrictMode
组件。它是由React
内置的组件,用于开启严格模式 - 第8行:使用
App
主组件,注意闭合
- 第7行和第9行:使用
- 第1行:引入
-
src/App.jsx
:主组件:定义主组件App
,它生成的代码对于现阶段的你来说可能太复杂了,让我们把它简化成以下内容:import React from 'react' function App() { return <h1>你好,axum.rs!</h1> } export default App
- 第1行:引入
React
- 第3~5行:声明一个函数,它的名字叫
App
- 第4行:它的返回值是一个 JSX
- 注意,它其实是一个 React 组件:
- 首字母大小
- 返回 JSX
- 第7行:将
App
组件作为默认导出
- 第1行:引入
运行和构建
# 运行
yarn dev
# 构建
yarn build
注意
使用 create-react-app
工具,可以在普通的 JS/TS 文件中(*.js/*.ts
)使用 JSX 语法;但使用 vite 创建的 react 项目,只能在 *.jsx/*.tsx
文件中使用 JSX 语法。