本章将通过编程世界通用的“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属性
classclassName
forhtmlFor
  • 标签的首字母决定了类型

    • 若首字母小写,则转换为 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>,你会发现只有一个 idrootdiv

  • 【声明容器】第9行:<div id="root"></div>,定义一个容器,最终我们会将 React 渲染完成的 VDOM 转换为真实 DOM,并挂载到这个容器上。
  • 【依赖】第10~12行:通过CDN引入了三个外部的JS文件(注意,这里引入的版本可能不是最新的,不要纠结这个,只要保证大版本号和这里的一致即可。同时,实际开发中不会用这种方式)
    • 第10行:引入 react
    • 第11行:引入react-dom
    • 第12行:引入babel:用于将 JSX 转换成普通的 JS 代码
  • 【书写代码】第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行:调用 rootrender() 方法,将 VDOM 渲染到真实 DOM 中

通过官方的 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
  • 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 主组件。注意闭合
    • 第17行:性能分析的函数调用。对于我们来说,直接忽略——你可以直接删除它(同时删除第5行的引入)
  • 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 组件作为默认导出

运行和构建

# 运行
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
  • 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 主组件,注意闭合
  • 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 组件作为默认导出

运行和构建

# 运行
yarn dev

# 构建
yarn build

注意

使用 create-react-app 工具,可以在普通的 JS/TS 文件中(*.js/*.ts)使用 JSX 语法;但使用 vite 创建的 react 项目,只能在 *.jsx/*.tsx 文件中使用 JSX 语法。

本章源码