专业编程教程与实战项目分享平台

网站首页 > 技术文章 正文

React 入门:从 JavaScript 到 React

ins518 2025-05-23 17:41:17 技术文章 1 ℃ 0 评论

JavaScript 是语言,而 React 是工具。

力争用最简洁的方式让你入门 React,前提是你已了解 JavaScript 和 HTML。

0x01 从 JavaScript 到 React

一些必须要懂的 JavaScript 概念

如果你在学习 React 的同时,也在学习 JavaScript,那么这里罗列了一些必须要懂的 JavaScript 概念,来帮助你更好地学习 React。

  • 函数 和 箭头函数
  • 对象
  • 数组及其常用方法
  • 解构赋值
  • 模版字符串
  • 条件运算符
  • JS 模块化和导入导出方法

本文将不会深入讲解关于 JavaScript 方面的知识,你无需非常精通 JavaScript 才能学习 React,但以上的一些概念是最适合初学者掌握的 JavaScript 的重要知识。

当然,你也可以跳过这些基本概念直接进入下面章节的学习,当遇到不理解的问题再回过头来翻阅这里的概念。

渲染 UI

要理解 React 如何工作,首先要搞清楚浏览器是如何解释你的代码并转化成 UI 的。

当用户访问一个网页时,服务器将返回一段 html 代码到浏览器,它可能看起来是这样的:



浏览器阅读了 html 代码,并将它结构化为 DOM。

什么是 DOM

DOM 是一个 html 元素的对象化表达,它是衔接你的代码和 UI 之间的桥梁,它表现为一个父子关系的树形结构。



你可以使用 JavaScript 或 DOM 的内置方法来监听用户事件,操作 DOM 包括,查询、插入、更新、删除界面上特定的元素,DOM 操作不仅允许你定位到特定的元素上,并且允许你修改它的内容及样式。

小问答:你可以通过操作 DOM 来修改页面内容吗?

使用 JavaScript 及 DOM 方法来更新 UI

让我们一起来尝试如何使用 JavaScript 及 DOM 方法来添加一个 h1 标签到你的项目中去。

打开我们的代码编辑软件,然后创建一个新的 index.html 的文件,在文件中加入以下代码:

<!-- index.html -->
<html>
  <body>
    <div></div>
  </body>
</html>

然后给定 div 标签一个特定的 id ,便于后续我们可以定位它。

<!-- index.html -->
<html>
  <body>
    <div id="app"></div>
  </body>
</html>

要在 html 文件中编写 JavaScript 代码,我们需要添加 script 标签

<!-- index.html -->
<html>
  <body>
    <div id="app"></div>
    <script type="text/javascript"></script>
  </body>
</html>

现在,我们可以使用 DOM 提供的 getElementById 方法来通过标签的 ID 定位到指定的元素。

<!-- index.html -->
<html>
  <body>
    <div id="app"></div>

    <script type="text/javascript">
      const app = document.getElementById('app');
    </script>
  </body>
</html>

你可以继续使用 DOM 的一系列方法来创建一个 h1 标签元素,h1 元素中可以包含任何你希望展示的文本。

<!-- index.html -->
<html>
  <body>
    <div id="app"></div>

    <script type="text/javascript">
      // 定位到 id 为 app 的元素
      const app = document.getElementById('app');

      // 创建一个 h1 元素
      const header = document.createElement('h1');

      // 创建一个文本节点
      const headerContent = document.createTextNode(
        'Develop. Preview. Ship.  ',
      );

      // 将文本节点添加到 h1 元素中去
      header.appendChild(headerContent);

      // 将 h1 元素添加到 id 为 app 的元素中去
      app.appendChild(header);
    </script>
  </body>
</html>

至此,你可以打开浏览器来预览一下目前的成果,不出意外的话,你应该可以看到一行使用 h1 标签的大字,写道:Develop. Preview. Ship.

HTML vs DOM

此时,如果你打开浏览器的代码审查功能,你会注意到在 DOM 中已经包含了刚才创建的 h1 标签,但源代码的 html 中却并没有。换言之,你所创建的 html 代码中与实际展示的内容是不同的。



这是因为 HTML 代码中展示的是初始化的页面内容,而 DOM 展示的是更新后的页面内容,这里尤指你通过 JavaScript 代码对 HTML 所改变后的内容。

使用 JavaScript 来更新 DOM,是非常有用的,但也往往比较繁琐。你写了如下那么多内容,仅仅用来添加一行 h1 标签。如果要编写一个大一些的项目,或者团队开发,就感觉有些杯水车薪了。

<!-- index.html -->
<script type="text/javascript">
  const app = document.getElementById('app');
  const header = document.createElement('h1');
  const headerContent = document.createTextNode('Develop. Preview. Ship.  ');
  header.appendChild(headerContent);
  app.appendChild(header);
</script>

以上这个例子中,开发者花了大力气来“指导”计算机该如何做事,但这似乎并不太友好,或者有没有更友好的方式让计算机迅速理解我们希望达到的样子呢?

命令式 vs 声明式编程

以上就是一个很典型的命令式编程,你一步一步的告诉计算机该如何更新用户界面。但对于创建用户界面,更好的方式是使用声明式,因为那样可以大大加快开发效率。相较于编写 DOM 方法,最好有种方法能声明开发者想要展示的内容(本例中就是那个 h1 标签以及它包含的文本内容)。

换句话说就是,命令式编程就像你要吃一个披萨,但你得告诉厨师该如何一步一步做出那个披萨;而声明式编程就是你告诉厨师你要吃什么样的披萨,而无需考虑怎么做。

React 正是那个“懂你”的厨师!

React:一个声明式的 UI 库

作为一个 React 开发者,你只需告诉 React 你希望展示什么样的页面,而它会自己找到方法来处理 DOM 并指导它正确地展示出你所要的效果。

小问答:你觉得以下哪句话更像声明式?
A:我要吃一盘菜,它要先放花生,然后放点鸡肉丁,接着炒一下...
B:来份宫保鸡丁


0x02 快速入门 React

要想在项目中使用 React,最简单的方法就是从外部 CDN(如:http://unpkg.com)引入两个 React 的包:

  • react:React 核心库
  • react-dom:提供用于操作 DOM 特定方法的库
<!-- index.html -->
<html>
  <body>
    <div id="app"></div>

    <script src="https://unpkg.com/react@17/umd/react.development.js"></script>
    <script src="https://unpkg.com/react-dom@17/umd/react-dom.development.js"></script>

    <script type="text/javascript">
      const app = document.getElementById('app');
    </script>
  </body>
</html>

这样就无需使用纯 JavaScript 来直接操作 DOM 了,而是使用来自 react-dom 中的 ReactDOM.render() 方法来告诉 React 在 app 标签中直接渲染 h1 标签及其文本内容。

<!-- index.html -->
<html>
  <body>
    <div id="app"></div>

    <script src="https://unpkg.com/react@17/umd/react.development.js"></script>
    <script src="https://unpkg.com/react-dom@17/umd/react-dom.development.js"></script>

    <script type="text/javascript">
      const app = document.getElementById('app');
      ReactDOM.render(<h1>Develop. Preview. Ship.  </h1>, app);
    </script>
  </body>
</html>

但当你在浏览器中运行的时候,它会报一个语法错误:

因为代码中的 <h1>Develop. Preview. Ship. </h1&gt; 并不是 JavaScript 代码,而是 JSX。

什么是 JSX

JSX 是一种 JS 的语法扩展,它使得你可以用类 HTML 的方式来描述界面。你无需学习 HTML 和 JavaScript 之外的新的符号和语法等,只需要遵守以下三条规则即可:

  • 返回一个单根节点的元素,如:
<!-- 你可以使用 div 标签 -->
<div>
  <h1>Hedy Lamarr's Todos</h1>
  <img 
    src="https://i.imgur.com/yXOvdOSs.jpg" 
    alt="Hedy Lamarr" 
    className="photo"
  />
  <ul>
    ...
  </ul>
</div>

<!-- 你也可以使用空标签 -->
<>
  <h1>Hedy Lamarr's Todos</h1>
  <img 
    src="https://i.imgur.com/yXOvdOSs.jpg" 
    alt="Hedy Lamarr" 
    className="photo"
  />
  <ul>
    ...
  </ul>
</>
  • 关闭所有标签
<!-- 诸如 img 必须自关闭 <img />,而包围类标签必须成对出现 <li></li> -->
<>
  <img 
    src="https://i.imgur.com/yXOvdOSs.jpg" 
    alt="Hedy Lamarr" 
    className="photo"
   />
  <ul>
    <li>Invent new traffic lights</li>
    <li>Rehearse a movie scene</li>
    <li>Improve the spectrum technology</li>
  </ul>
</>
  • 使用驼峰命名(camalCase)方式
<!-- 如 stroke-width 必须写成 strokeWidth,而 class 由于是 react 的关键字,因此替换为 className
<img 
  src="https://i.imgur.com/yXOvdOSs.jpg" 
  alt="Hedy Lamarr" 
  className="photo"
/>

JSX 并不是开箱即用的,浏览器默认情况下是无法解释 JSX 的,所以你需要一个编译器(compiler),诸如 Babel,来将 JSX 代码转换为普通的浏览器能理解的 JavaScript。

在项目中添加 Babel

复制粘贴以下脚本到 index.html 文件中:

<script src="https://unpkg.com/@babel/standalone/babel.min.js"></script>

另外,你还需要告诉 Babel 需要转换哪些代码,为需要转换的代码添加类型 type="text/jsx"

<html>
  <body>
    <div id="app"></div>
    <script src="https://unpkg.com/react@17/umd/react.development.js"></script>
    <script src="https://unpkg.com/react-dom@17/umd/react-dom.development.js"></script>
    <!-- Babel Script -->
    <script src="https://unpkg.com/@babel/standalone/babel.min.js"></script>
    <script type="text/jsx">
      const app = document.getElementById('app');
      ReactDOM.render(<h1>Develop. Preview. Ship.  </h1>, app);
    </script>
  </body>
</html>

现在可以再次回到浏览器中刷新页面来确认是否能成功展示了。



使用声明式的 React,你只编写了以下代码:

<script type="text/jsx">
  const app = document.getElementById("app")
  ReactDOM.render(<h1>Develop. Preview. Ship.  </h1>, app)
</script>

而命令式代码如此前编写的:

<script type="text/javascript">
  const app = document.getElementById('app');
  const header = document.createElement('h1');
  const headerContent = document.createTextNode('Develop. Preview. Ship.  ');
  header.appendChild(headerContent);
  app.appendChild(header);
</script>

相比较后不难发现,你节省了很多重复冗余的工作。

这就是 React,一款富含可重用代码,为你节省时间和提高效率的工具。

目前,你还无需过多关注究竟 React 用了什么神奇的魔法实现这样的功能。当然如果你感兴趣的话,可以参考 React 的官方文档中的 UI Tree 和 render 两个章节。

React 核心概念

在真正上手 React 项目之前,还有三个最重要的 React 核心概念需要理解

  • 组件(Components)
  • 参数(Props)
  • 状态(State)

在后续的章节中我们将逐一学习以上三个核心概念。


0x03 使用组件(Components)来建立界面

一个用户界面可以被分割成更小的部分,我们称之为“组件”。它是自包含、可重用的代码块,你可以把它想象成乐高玩具,独立的砖块可以拼装成更大的组合结构。如果你要更新界面的某一部分,你可以仅更新特定的组件或“砖块”。



模块化让你的代码更具有可维护性,因为你可以轻易地添加、修改、删除特定的组件而无需改动程序的其他部分。React 组件其实就是用 JavaScript 编写的,接下来我们将学习如何编写一个从 JavaScript 原生到 React 的组件。

创建组件

在 React 中,组件就是函数,我们在 script 中插入一个 header 方法:

<script type="text/jsx">
  const app = document.getElementById("app")
  function header() {}
  ReactDOM.render(<h1>Develop. Preview. Ship.  </h1>, app)
</script>

组件函数返回一个界面元素(即我们前面所提到过的单根节点的元素),可以使用 JSX 语法,如:

<script type="text/jsx">
  const app = document.getElementById("app")

  function header() {
     return (<h1>Develop. Preview. Ship.  </h1>)
   }

  ReactDOM.render(, app)
</script>

然后将 header 传入 ReactDOM.render 的第一个参数中去:

ReactDOM.render(header, app)

但如果你现在刷新浏览器预览效果的话,将会报错,因为还需要做两件事。

首先,React 组件必须以大写字母开头:

// 首字母大写
function Header() {
  return <h1>Develop. Preview. Ship.  </h1>;
}

ReactDOM.render(Header, app);

其次,你在使用 React 组件时,也需要使用 JSX 的语法格式,将组件名称用 <> 扩起来:

function Header() {
  return <h1>Develop. Preview. Ship.  </h1>;
}
<br/>ReactDOM.render(<Header />, app);

嵌套组件

一个应用程序通常包含多个组件,有的甚至是组件嵌套的组件。例如我们来创建一个 HomePage 组件:

function Header() {
  return <h1>Develop. Preview. Ship.  </h1>;
}

function HomePage() {
  return <div></div>;
}

ReactDOM.render(<Header />, app);

然后将 Header 组件放入 HomePage 组件中:

function Header() {
  return <h1>Develop. Preview. Ship.  </h1>;
}

function HomePage() {
  return (
    <div>
      {/* 嵌套的 Header 组件 */}
      <Header />
    </div>
  );
}

ReactDOM.render(<HomePage />, app);

组件树

你可以继续以这种方式嵌套 React 组件,以形成一个更大的组件。



比如上图中,你的顶层组件是 HomePage,它下面包含了一个 Header,一个 ARTICLE 和一个 FOOTER。然后 HEADER 组件下又包含了它的子组件等等。

这样的模块化使得你可以在项目的许多其他地方重用组件。


0x04 参数(Props)与数据展示

如果你重用 Header 组件,你将显示相同的内容两次。

function Header() {
  return <h1>Develop. Preview. Ship.  </h1>;
}

function HomePage() {
  return (
    <div><br/>      <Header />
      <Header />
    </div>
  );
}

但如果你希望在标题中传入不同的文本,或者你需要从外部源获取数据再进行文本的设置时,该怎么办呢?

普通的 HTML 元素允许你通过设置对应标签的某些重要属性来修改其实际的展示内容,比如修改 <img>src 属性就能修改图片展示,修改 <a>href 就能改变超文本链接的目标地址。

同样的,你可以通过传入某些属性值来改变 React 的组件,这被称为参数(Props)



与 JavaScript 函数类似,你可以设计一个组件接收一些自定义的参数或者属性来改变组件的行为或展示效果,并且还允许通过父组件传递给子组件。

注意:React 中,数据流是顺着组件数传递的。这被称为单向数据流

使用参数

HomePage 组件中,你可以传入一个自定义的 title 属性给 Header 组件,就如同你传入了一个 HTML 属性一样。

// function Header() {
//   return <h1>Develop. Preview. Ship.  </h1>
// }

function HomePage() {
  return (
    <div>
      <Header title="Hello React" />
    </div>
  );
}

// ReactDOM.render(<HomePage />, app)

然后,Header 作为子组件可以接收这些传入的参数,可在组件函数的第一个入参中获得。

function Header(props) {
  return <h1>Develop. Preview. Ship.  </h1>
}

你可以尝试打印 props 来查看它具体是什么东西。

function Header(props) {
    console.log(props) // { title: "Hello React" }
    return <h1>Hello React</h1>
}

由于 props 是一个 JS 对象,因此你可以使用对象解构来展开获得对象中的具体键值。

function Header({ title }) {
    console.log(title) // "Hello React"
    return <h1>Hello React</h1>
}

现在你就能使用 title 变量来替换 h1 标题中的文本了。

function Header({ title }) {
    console.log(title) // "Hello React"
    return <h1>title</h1>
}

但当你打开浏览器刷新页面时,你会发现页面上展示的是标题文本是 title,而不是 title 变量的值。这是因为 React 不能对纯文本进行解析,这就需要你额外地对文本展示做一些处理。

在 JSX 中使用变量

要在 JSX 中使用你定义的变量,你需要使用花括号 {} ,它允许你在其中编写 JavaScript 表达式

function Header({ title }) {
    console.log(title) // "Hello React"
    return <h1>{ title }</h1>
}

通常,它支持如下几种方式:

  • 输出对象属性 { props.title }
  • 模板字符串 {`Hello ${title}`}
  • 函数返回值 { getTitle() }
  • 三元表达式 { title ? title : "Hello" }

这样你就能根据参数输出不同的标题文本了:

function Header({ title }) {
  return <h1>{title ? title : 'Hello React!'}</h1>;
}

function Page() {
  return (
    <div>
      <Header title="Hello JavaScript!" />
      <Header title="Hello World!" />
    </div>
  );
}

通过列表进行迭代

通常我们会有一组数据需要展示,它以列表形式呈现,你可以使用数组方法来操作数据,并生成在样式上统一的不同内容。

例如,在 HomePage 中添加一组名字,然后依次展示它们。

function HomePage() {
  const names = ['Mike', 'Grace', 'Margaret'];

  return (
    <div>
      <Header title="Develop. Preview. Ship.  " />
    </div>
  );
}

然后你可以使用 Arraymap 方法对数据进行迭代输出,并使用箭头函数来将数据映射到每个迭代项目上。

function HomePage() {
  const names = ['Mike', 'Grace', 'Margaret'];

  return (
    <div>
      <Header title="Develop. Preview. Ship.  " />
      <ul>
        {names.map((name) => (
          <li>{name}</li>
        ))}
      </ul>
    </div>
  );
}

现在如果你打开浏览器查看,会看到一个关于缺少 key 属性的警告。这是因为 React 需要通过 key 属性来唯一识别数组上的元素来确定最终需要在 DOM 上更新的项目。通常我们会使用 id,但本例子中你可以直接使用 name,因为它们的值也是唯一不同的。

function HomePage() {
  const names = ['Mike', 'Grace', 'Margaret'];

  return (
    <div>
      <Header title="Develop. Preview. Ship.  " />
      <ul>
        {names.map((name) => (
          <li key={name}>{name}</li>
        ))}
      </ul>
    </div>
  );
}

0x05 使用状态(State)来增加交互性

首先,我们看下 React 是如何通过状态和事件处理来帮助我们增加交互性的。

我们在 HomePage 组件中添加一个“喜欢”按钮:

function HomePage() {
  const names = ['Mike', 'Grace', 'Margaret'];

  return (
    <div>
      <Header title="Develop. Preview. Ship.  " />
      <ul>
        {names.map((name) => (
          <li key={name}>{name}</li>
        ))}
      </ul>

      <button>Like</button>
    </div>
  );
}

监听事件

要让按钮在被点击的时候做些什么时,你可以在按钮上添加 onClick 事件属性:

function HomePage() {
  // ...
  return (
    <div>
      {/* ... */}
      <button onClick={}>Like</button>
    </div>
  );
}

在 React 中,属性名称都是驼峰命名式的,onClick 是许多事件属性中的一种,还有一些其他的事件属性,如:输入框会有 onChange ,表单会有 onSubmit 等。

处理事件

你可以定义一个函数来处理以上一些事件,当它被触发的时候。事件处理函数可以在返回语句之前定义,如:

function HomePage() {
  // ...

  function handleClick() {
    console.log("I like it.")
  }

  return (
    <div>
      {/* ... */}
      <button onClick={}>Like</button>
    </div>
  )
}

接着你就可以在 onClick 中调用 handleClick 方法了。

function HomePage() {
  //    ...
  function handleClick() {
    console.log('I like it.');
  }

  return (
    <div>
      {/* ... */}
      <button onClick={handleClick}>Like</button>
    </div>
  );
}

状态和钩子

React 里有一系列钩子函数(Hooks),你可以利用钩子函数在组件中创建状态,你可以把状态理解为在界面上随时间或者行为变化的一些逻辑信息,通常情况下是由用户触发的。



你可以通过状态来存储和增加用户点击喜欢按钮的次数,在这里我们可以使用 React 的 useState 钩子函数。

function HomePage() {
  React.useState();
}

useState 返回一个数组,你可以使用数组解构来使用它。

function HomePage() {
  const [] = React.useState();

  // ...
}

该数组的第一个值是状态值,你可以定义为任何变量名称:

function HomePage() {
  const [likes] = React.useState();

  // ...
}

该数组的第二个值是状态修改函数,你可以定义为以 set 为前缀的函数名,如 setLikes

function HomePage() {
  const [likes, setLikes] = React.useState(); 
  // likes 存储了喜欢被点击的次数;setLikes 则是用来修改该次数的函数

  // ...
}

同时,你可以在定义的时候给出 likes 的初始值

function HomePage() {
  const [likes, setLikes] = React.useState(0);
}

然后你可以尝试查看你设置的初始值是否生效

function HomePage() {
  // ...
  const [likes, setLikes] = React.useState(0);

  return (
    // ...
    <button onClick={handleClick}>Like({likes})</button>
  );
}

最后,你可以在每次按钮被点击后调用 setLikes 方法来更新 likes 变量的值。

function HomePage() {
  // ...
  const [likes, setLikes] = React.useState(0);

  function handleClick() {
    setLikes(likes + 1);
  }

  return (
    <div>
      {/* ... */}
      <button onClick={handleClick}>Likes ({likes})</button>
    </div>
  );
}

点击喜欢按钮将会调用 handleClick 方法, 然后调用 setLikes 方法将更新后的新值传入该函数的第一个入参中。这样 likes 变量的值就变成了新值

状态管理

本章节仅对状态做了简单的介绍,举例了 useState 的用法,你可以在后续的学习中了解到更多的状态管理和数据流处理的方法,更多的内容可以参考官网的 添加交互性 和 状态管理 两个章节进行更深入的学习。

小问答:请说出参数(Props)和状态(State)的区别?

0x06 长路漫漫,继续前行

到此为止,你已了解了 React 的三大核心概念:组件、参数和状态。对这些概念的理解越深刻,对今后开发 React 应用就越有帮助。学习的旅程还很漫长,途中若有困惑可以随时回看本文,或阅读以下主题文章进行更深入的学习:

  • Render and Commit (reactjs.org)
  • Referencing Values with Refs (reactjs.org)
  • Managing State (reactjs.org)
  • Passing Data Deeply with Context (reactjs.org)
  • React APIs (reactjs.org)

React 学习资源

React 的学习资源层出不穷,你可以在互联网上搜索 React 来获取无穷无尽的资源,但在我看来最好的仍是官方提供的《React 文档》,它涵盖了所有你需要学习的主题。

最好的学习方法就是实践。

本文暂时没有评论,来添加一个吧(●'◡'●)

欢迎 发表评论:

最近发表
标签列表