Skip to content

react 基础-函数组件的使用

写在前面

  1. 已经具备了一定的 HTML、CSS、JavaScript、ES6 基础
  2. react 官网地址
  3. 本文档的样例都是直接 html 页面,加载 react 核心库,react-dom 渲染库、和 babel 来呈现,不创建脚手架工程

react 简介

  1. react 是用于构建 Web 和原生交互界面的 js 库。
  2. 为什么要用
  • 原生 JS 操作 DMOM 频繁,效率低(DOM-API 操作 UI)
  • 使用原生 JS 操作 DOM,浏览器会进行大量的重置重绘
  • 原生 JS 没有组件化编码方案,代码复用率低
  1. react 的特点
  • 采用组件化模式、声明式编码,提高开发效率及组件复用率
  • 使用虚拟 DOM+diffing 算法,尽量减少 DOM 操作
  • 在 react native 中可以使用 react 语法进行移动端开发

创建和使用函数组件

js
/* 1.定义函数组件 */
function MyCompont() {
  return <h1>Heollo React</h1>;
}
/* 2.将函数式组件渲染到页面上 */
const root = ReactDOM.createRoot(document.getElementById("root"));
root.render(<MyCompont />);
html
<!DOCTYPE html>
<html lang="en">
  <head>
    <meta charset="UTF-8" />
    <meta http-equiv="X-UA-Compatible" content="IE=edge" />
    <meta name="viewport" content="width=device-width, initial-scale=1.0" />
    <title>hello react</title>
  </head>
  <body>
    <!--容器-->
    <div id="root"></div>
    <!--引入react核心库-->
    <script src="../js/react.development.js"></script>
    <!--引入react-dom,用于支持react操作DOM-->
    <script src="../js/react-dom.development.js"></script>
    <!--引入babel 用于JSX转为JS-->
    <script src="../js/babel.min.js"></script>

    <script type="text/babel">
      /* 此处需要写babel type="text/javascriptt" 因为之前是js代码,但现在写的不是js而是jsx*/
      /* 1.定义函数组件 */
      function MyCompont() {
        return <h1>Heollo React</h1>;
      }
      /* 2.将函数式组件渲染到页面上 */
      const root = ReactDOM.createRoot(document.getElementById("root"));
      root.render(<MyCompont />);
    </script>
  </body>
</html>

组件:用来实现局部功能效果的代码和资源的整合(html/css/js),如一个按钮入口 函数名必须是大写字母开头,因为函数名就是组件名 组件标签要有闭合标签

关于虚拟 DOM

  • 本质是个 Object 类型的对象(一般对象)
  • 虚拟 DOM 比较轻,真实 DOM 比较重,因为虚拟 DOM 只 React 内部使用,无须真实 DOM 上那么多属性
  • 虚拟 DOM 最后会被 React 转换成真实的 DOM 显示在页面上

关于 JSX

jsx 是 javascript xml 的全称,XML 早期用于数据的存储和传输 JSX 的语法规则

  • 定义虚拟 DOM 时,不要写引号

  • 虚拟 DOM 只能有一个根标签

  • 标签必须闭合

  • 标签首字母 (1).若小写字母开头,则将该标签转为 html 的同名标签,找不到对应的同名标签则报错 (2).若大写字母开头,React 则去渲染对应的组件,找不到则报错

  • 标签中混入 JS 表达式,要用{}

  • 指定 css 样式类名时,不用 class,用 className

  • 内联样式要用 style = {{ key: value }} 的样式,key 遵循 js 的驼峰命名的写法

  • 一定要注意区分 JS 表达式和 JS 语句(代码)

    1. 表达式:一个表达式可以产生一个值,可以放在任何一个需要值的地方。下面的都是表达式(有返回值) (1). a (2). a+b (3). demo(1) (4). arr.map() (5). function test(){} (6). a?fun1():fun2() (7). flag && fun()

    2. JS 语句(代码) 下面的都是 JS 语句(代码) (1).if(){} (2).for(){} (3).switch(){}

条件渲染

用 if else 或者是三目运算

js
gVar = {
  loginFlag: false,
};
if (gVar.loginFlag) {
  return <Home />;
} else {
  return <Login />;
}
// 三目运算
gVar.loginFlag ? <Home /> : <Login />;

列表渲染

一般用 for 循环 和 array 的 map() 函数 来渲染组件列表。

html
<!DOCTYPE html>
<html lang="en">
  <head>
    <meta charset="UTF-8" />
    <meta http-equiv="X-UA-Compatible" content="IE=edge" />
    <meta name="viewport" content="width=device-width, initial-scale=1.0" />
    <title>hello react</title>
  </head>
  <body>
    <!--容器-->
    <div id="root"></div>
    <!--引入react核心库-->
    <script src="../js/react.development.js"></script>
    <!--引入react-dom,用于支持react操作DOM-->
    <script src="../js/react-dom.development.js"></script>
    <!--引入babel 用于JSX转为JS-->
    <script src="../js/babel.min.js"></script>
    <script type="text/babel">
      /* 此处需要写babel type="text/javascriptt" 因为之前是js代码,但现在写的不是js而是jsx*/
      /* 1.定义函数组件 */
      function MyCompont() {
        const products = [
          { title: "Cabbage", id: 1 },
          { title: "Garlic", id: 2 },
          { title: "Apple", id: 3 },
        ];
        return (
          <div>
            <h1>列表渲染</h1>
            <ul>
              {products.map((item, index) => {
                return <li key={item.title + item.id}>{item.title}</li>;
              })}
            </ul>
          </div>
        );
      }
      const root = ReactDOM.createRoot(document.getElementById("root"));
      /* 2.将函数式组件渲染到页面上 */
      root.render(<MyCompont />);
    </script>
  </body>
</html>
虚拟DOM中key要保证在兄弟结点中是唯一的
虚拟DOM中key的作用:
1.简单的说:key是虚拟DOM对象的标识,在更新显示时key起着极其重要的作用
2.详细的说:当状态的数据发生改变时,react会根据【新数据】生成【新的虚拟DOM】
            随后React进行【新虚拟DOM】与【旧虚拟DOM】的diff比较,比较规则如下:
            a.旧虚拟DOM中找到了与新虚拟DOM相同的key
                (1).若虚拟DOM中内容没变,直接使用之前的真是DOM
                (2).若虚拟DOM中内容变了,则生成新的真实DOM,随后替换页面中之前的真实DOM
            b.旧虚拟DOM中未找到与新虚拟DOM相同的key
                根据数据创建新的真实DOM,随后渲染到页面
用index作为key可能会引发的问题:
    1.若对数据进行:逆序添加、逆序删除等破坏顺序的操作。
        会产生没有必要的真实DOM更新,界面效果没问题,但效率低
    2.如果结构中还包含了输入类的DOM
        会产生错误的DOM更新,界面显示就有问题了
    3.注意!如果不存在对数据的逆序添加、逆序删除等破坏顺序操作
        仅用于渲染列表用于展示,使用index作为key是没有问题的.但一般不推荐用index作为key

事件处理

html
<!DOCTYPE html>
<html lang="en">
  <head>
    <meta charset="UTF-8" />
    <meta http-equiv="X-UA-Compatible" content="IE=edge" />
    <meta name="viewport" content="width=device-width, initial-scale=1.0" />
    <title>hello react</title>
  </head>
  <body>
    <!--容器-->
    <div id="root"></div>
    <!--引入react核心库-->
    <script src="../js/react.development.js"></script>
    <!--引入react-dom,用于支持react操作DOM-->
    <script src="../js/react-dom.development.js"></script>
    <!--引入babel 用于JSX转为JS-->
    <script src="../js/babel.min.js"></script>
    <script type="text/babel">
      /* 此处需要写babel type="text/javascriptt" 因为之前是js代码,但现在写的不是js而是jsx*/
      /* 1.定义函数组件 */
      function MyCompont() {
        function handleClick(e) {
          console.log("handleClick e.target", e.target);
          console.log("handleClick e.target.innerText", e.target.innerText);
          alert("Click me");
        }

        function handleClickParam(param) {
          console.log("handleClickParam param", param);
          return handleClick;
        }

        return (
          <div>
            <h1>事件处理</h1>
            <button onClick={handleClick}>Click me1</button>
            <br />
            <button
              onClick={(e) => {
                console.log("anonymous handleClick e.target", e.target);
                alert("匿名事件处理函数");
              }}
            >
              Click me
            </button>
          </div>
        );
      }
      const root = ReactDOM.createRoot(document.getElementById("root"));
      /* 2.将函数式组件渲染到页面上 */
      root.render(<MyCompont />);
    </script>
  </body>
</html>
1.通过onXx指定事件处理函数(注意X要大写)
    a.react使用的是自定义(合成)事件,而不是使用的原生DOM事件--为了进一步的兼容
    b.React中的事件是通过事件委托方式处理的(委托给组件最外层的元素)--为了高效
2.通过event.target得到发生事件的DOM元素对象

Fragment 和空标签<></>

当做组件的根节点,而无需向 DOM 添加额外节点。解决jsx的要求只能有一个根节点而引入额外标签的问题
用法 <React.Fragment></React.Fragment>,react在生成真实DOM的时候,会丢掉这个标签
还有一种做法是写空标签<></>
但区别是Fragment可以写key属性,用于提供给React Diff用,但空标签不能写key属性

例子

case 下载

作业

html
<!DOCTYPE html>
<html lang="en">
  <head>
    <meta charset="UTF-8" />
    <meta http-equiv="X-UA-Compatible" content="IE=edge" />
    <meta name="viewport" content="width=device-width, initial-scale=1.0" />
    <title>作业</title>
    <style>
      @charset "utf-8";
      /* 样式重置 */
      h1,
      h2,
      h3,
      h4,
      h5,
      h6,
      header,
      hgroup,
      hr,
      input,
      li,
      ol,
      p,
      pre,
      td,
      textarea,
      th,
      ul {
        margin: 0;
        padding: 0;
      }
      ul,
      oll,
      li {
        list-style: none;
      }
      a {
        text-decoration: none;
        color: #333;
      }
      i,
      em {
        font-style: normal;
      }
      input,
      textarea,
      button,
      select,
      a {
        outline: none;
        border: none;
      }
      table {
        border-collapse: collapse;
        border-spacing: 0;
      }

      .todo-container {
        position: static;
      }
      .todo-wrap {
        width: 520px;
        margin: 20px auto;
        border: 1px solid #999;
        border-radius: 5px;
      }
      .todo-main {
        margin: 10px;
        border: 1px solid #999;
        border-bottom-width: 0;
        width: 500px;
      }
      .todo-main li {
        position: relative;
        border-bottom: 1px solid #999;
        height: 40px;
        width: 500px;
        line-height: 40px;
      }
      .todo-main li:hover {
        background-color: #ddd;
      }
      .todo-main .hot {
        position: relative;
        top: -10px;
        color: red;
        font-size: 12px;
        background-color: #666;
        padding: 2px 5px;
        border-radius: 15px;
      }
    </style>
    <script>
      const datas = new Array(20).fill("").map((item, index) => {
        return {
          title: `标题${index + 1}`,
          content: `标题${index + 1}的详细内容`,
          key: new Date().getTime(),
          isHot: index % 2,
        };
      });

      function showContent() {
        alert("弹出对应消息的内容");
      }
    </script>
  </head>
  <body>
    <div class="todo-wrap">
      第十三讲作业说明:<br />
      1.把生成的datas数组按照如下界面的界面样式显示出来<br />
      2.数组的每个元素对象格式为{title:"xxx",content:"xxx",isHot:true,key:xxx}当isHot为真时,右上角显示一个new的标志<br />
      2.点击列表时用alert弹出当前消息的内容(content字段)<br />
    </div>
    <div class="todo-container">
      <div class="todo-wrap">
        <!-- todo列表 -->
        <ul class="todo-main">
          <li onclick="showContent()">标题一<span class="hot">new</span></li>
          <li onclick="showContent()">标题二</li>
          <li onclick="showContent()">标题三</li>
        </ul>
      </div>
    </div>
  </body>
</html>