React 学习笔记

React

React.js是一个开源的JavaScript库,由Facebook于2013年开发,用于构建用户界面或UI组件。它主要用于构建单页应用程序或SPA,并且可以在Web和移动应用程序中使用。React.js使用虚拟DOM来提高性能,并使用组件化的方式来实现代码的重用和组织。

特点

  1. 高效性能:React.js使用虚拟DOM来减少直接DOM操作,从而提高性能。
  2. 组件化:React.js使用组件来构建应用程序,使得代码更易于维护、重用和组织。
  3. 声明式编程:React.js使用声明式编程方式来描述界面和状态的变化,使得代码更易于理解和预测。
  4. 跨平台:React.js可以用于构建Web和移动应用程序,并且具有跨平台的兼容性。
  5. 开源:React.js是开源的JavaScript库,具有广泛的社区支持和资源。

一、创建React项目

  1. create-react-app创建
    在终端中输入以下命令来创建一个新的React项目:

    npx create-react-app my-app
    

    其中,"my-app"是要创建的项目的名称。

  2. Vite
    使用 NPM:

    npm create vite@latest
    

    使用 Yarn:

    yarn create vite
    

    使用 PNPM:

    pnpm create vite
    

    然后按照提示操作即可!

  3. 目录结构

    my-app
    ├─ .eslintrc.cjs
    ├─ .gitignore
    ├─ index.html
    ├─ package.json
    ├─ public
    │  └─ vite.svg
    ├─ README.md
    ├─ src
    │  ├─ App.css
    │  ├─ App.jsx
    │  ├─ assets
    │  │  └─ react.svg
    │  ├─ index.css
    │  └─ main.jsx
    └─ vite.config.js
    
    

二、React组件创建和嵌套组件

React 应用程序是由组件组成的。一个组件是 UI(用户界面)的一部分,它拥有自己的逻辑和外观。组件可以小到一个按钮,也可以大到整个页面。

React 组件是返回标签的 JavaScript 函数

React组件的类型

  1. 函数组件和类组件:这是根据定义组件的方法来划分的。

    • 函数组件使用函数来定义组件,只返回React元素。
    • 类组件使用ES6 class来定义组件,可以包含更多的功能,比如有状态、生命周期方法等。
  2. 无状态组件和有状态组件:这是根据组件内部是否维护state来划分的。

    • 无状态组件内部不使用state,只根据外部组件传入的props返回待渲染的React元素。
    • 有状态组件则维护了自己的state,并能主动触发更新。
  3. 展示型组件和容器型组件:这是根据组件的功能来划分的。

    • 展示型组件专门写结构,里面没有逻辑,逻辑都是从容器组件传过来的,俗称傻瓜组件。
    • 容器型组件专门写逻辑,里面没有UI结构,俗称聪明组件。
函数组件
// button.jsx
function MyButton() {
  return (
    <button>button</button>
  );
}

export default MyButton;
类组件
// button.jsx
// import React from 'react';
import React, { Component } from 'react';

// class MyButton extends React.Component {
class MyButton extends Component {
    constructor() {}
    render() {
        return (
            <button>button</button>
        );
    }
}

export default MyButton;
嵌套组件
import MyButton from './button';
function MyComponent() {
  return (  
    <div>  
      <p>这是一个按钮</p>  
      <MyButton /> 
    </div>  
  );  
}

上面所使用的标签语法被称为 JSX。它是可选的,但大多数 React 项目会使用 JSX,主要是它很方便。

JSX 比 HTML 更加严格。你必须闭合标签,如<br />。你的组件也不能返回多个 JSX 标签。你必须将它们包裹到一个共享的父级中,比如 <div>...</div> 或使用空的 <>...</> 包裹。

添加样式

在 React 中,你可以使用 className 来指定一个 CSS 的 class。它与 HTML 的 class 属性的工作方式相同:

<img className="avatar" />

然后,你可以在一个单独的 CSS 文件中为它编写 CSS 规则:

/* In your CSS */
.avatar {
  border-radius: 50%;
}

显示数据

JSX 会让你把标签放到 JavaScript 中。而大括号会让你 “回到” JavaScript 中,这样你就可以从你的代码中嵌入一些变量并展示给用户。例如:

function MyComponent(props) {  
  const count = 0; 
  
  return (  
    <div>  
      <p>Count: {count}</p>  
      <button>Button</button>  
    </div>  
  );  
}

你还可以将 JSX 属性 “转义到 JavaScript”,但你必须使用大括号 而非 引号。例如,className=“avatar” 是将 “avatar” 字符串传递给 className,作为 CSS 的 class。但 src={user.imageUrl} 会读取 JavaScript 的 user.imageUrl 变量,然后将该值作为 src 属性传递:

const user = {
  name: 'Hedy Lamarr',
  imageUrl: 'https://i.imgur.com/yXOvdOSs.jpg',
  imageSize: 90,
};
export default function Profile() {
  return (
    <>
      <h1>{user.name}</h1>
      <img
        className="avatar"
        src={user.imageUrl}
        alt={'Photo of ' + user.name}
        style={{
          width: user.imageSize,
          height: user.imageSize
        }}
      />
    </>
  );
}

在上面示例中,style={{}} 并不是一个特殊的语法,而是 style={ } JSX 大括号内的一个普通 {} 对象。当你的样式依赖于 JavaScript 变量时,你可以使用 style 属性。

条件渲染

React 没有特殊的语法来编写条件语句,因此使用的就是普通的 JavaScript 代码。例如,if 语句、&&? : 运算符来选择性地渲染 JSX:

let content;
if (isLoggedIn) {
  content = <AdminPanel />;
} else {
  content = <LoginForm />;
}
return (
  <div>
    {content}

    {isLoggedIn ? (<AdminPanel />) : (<LoginForm />)}

    {/* 切勿将数字放在 && 左侧 */}
    {isLoggedIn && <AdminPanel />}
  </div>
);

切勿将数字放在 && 左侧.

JavaScript 会自动将左侧的值转换成布尔类型以判断条件成立与否。然而,如果左侧是 0,整个表达式将变成左侧的值(0),React 此时则会渲染 0 而不是不进行渲染。

渲染列表

依赖 JavaScript 的特性,例如 for 循环array 的 map() 函数 来渲染组件列表。

在你的组件中,使用 map() 函数将这个数组转换为 <li> 标签构成的列表:

function CustomUl() {
    const products = [
        { title: 'Cabbage', id: 1 },
        { title: 'Garlic', id: 2 },
        { title: 'Apple', id: 3 },
    ];

    const listItems = products.map(product =>
        <li key={product.id}>
            {product.title}
        </li>
    );

    return (
        <ul>{listItems}</ul>
    );
}

直接放在 map() 方法里的 JSX 元素一般都需要指定 key 值!

响应事件

使用 React 可以在 JSX 中添加 事件处理函数。其中事件处理函数为自定义函数,它将在响应交互(如点击、悬停、表单输入框获得焦点等)时触发。

如需添加一个事件处理函数,需要先定义一个函数,然后 将其作为 prop 传入 合适的 JSX 标签。

function MyButton() {
  function handleClick() {
    alert('You clicked me!');
  }

  return (
    <>
        <button onClick={handleClick}>
            Click me
        </button>
        <button onClick={() => {
            alert('You clicked me!')
        }}>
            Click me
        </button>
    </>
  );
}

注意,onClick={handleClick} 的结尾没有小括号!不要 调用 事件处理函数:你只需 把函数传递给事件 即可。当用户点击按钮时 React 会调用你传递的事件处理函数。

更新界面

通常你会希望你的组件 “记住” 一些信息并展示出来,比如一个按钮被点击的次数。要做到这一点,你需要在你的组件中添加 state。

首先,从 React 引入 useState

import { useState } from 'react';

现在可以在组件中声明一个 state 变量:

function MyButton() {
  const [count, setCount] = useState(0);
  // ...

将从 useState 中获得两样东西:当前的 state(count),以及用于更新它的函数(setCount)。可以给它们起任何名字,但按照惯例会像 [something, setSomething] 这样为它们命名。

这里的 [] 语法称为数组解构,它允许你从数组中读取值。 useState 返回的数组总是正好有两项。

第一次显示按钮时,count 的值为 0,因为你把 0 传给了 useState()。当你想改变 state 时,调用 setCount() 并将新的值传递给它。点击该按钮计数器将递增:

function MyButton() {
  const [count, setCount] = useState(0);

  function handleClick() {
    setCount(count + 1);
  }

  return (
    <button onClick={handleClick}>
      Clicked {count} times
    </button>
  );
}

React 将再次调用你的组件函数。第一次 count 变成 1。接着点击会变成 2。继续点击会逐步递增。

如果你多次渲染同一个组件,每个组件都会拥有自己的 state。

注意,每个按钮会 “记住” 自己的 count,而不影响其他按钮。

更新 React state 中的对象
import { useState } from 'react';
export default function MovingDot() {
  const [userInfo, setUserInfo] = useState({
    age: 14,
    height: 150
  });
  const addAge = () => {
    // userInfo.age += 1; 数据改变了,但是不会触发视图更新
    setUserInfo({
        ...userInfo,
        age: age + 1,
    })
  }
  const addHeight = () => {
    // userInfo.height += 1; 数据改变了,但是不会触发视图更新
    setUserInfo({
        ...userInfo,
        height: height + 1,
    })
  }
  return (
    <div>
        <p>
            <span>年龄</span>: {userInfo.age}
        </p>
        <p>
            <span>身高</span>: {userInfo.height}
        </p>
        <button onClick={addAge}>年龄 + 1</button>
        <button onClick={addHeight}>身高 + 1</button>
    </div>
  );
}
更新 React state 中的数组
避免使用 (会改变原始数组)推荐使用 (会返回一个新数组)
添加元素pushunshiftconcat[...arr] 展开语法
删除元素popshiftsplicefilterslice
替换元素splicearr[i] = ... 赋值map
排序reversesort先将数组复制一份
const [artists, setArtists] = useState([]);

setArtists(
    [...artists, '张杰', '周杰伦', '李荣浩']
)

使用 Hook

use 开头的函数被称为 Hook。useState 是 React 提供的一个内置 Hook。你可以在 React API 参考 中找到其他内置的 Hook。你也可以通过组合现有的 Hook 来编写属于你自己的 Hook。

Hook 比普通函数更为严格。你只能在你的组件(或其他 Hook)的 顶层 调用 Hook。如果你想在一个条件或循环中使用 useState,请提取一个新的组件并在组件内部使用它。

将 Props 传递给组件

向组件传递 props
export default function Profile() {
  return (
    <Avatar
        person={{ name: 'Lin Lanying', imageId: '1bX5QH6' }}
        size={100}
    />
  );
}
在子组件中读取 props
// 给 prop 指定一个默认值 
function Avatar({ person, size = 100 }) {
  // ...
}

这种语法被称为 “解构”,等价于于从函数参数中读取属性:

function Avatar(props) {
  let person = props.person;
  let size = props.size;
  // ...
}
组件共享数据
import { useState } from 'react';

export default function MyApp() {
  const [count, setCount] = useState(0);

  function handleClick() {
    setCount(count + 1);
  }

  return (
    <div>
      <h1>Counters that update together</h1>
      <MyButton count={count} onClick={handleClick} />
      <MyButton count={count} onClick={handleClick} />
    </div>
  );
}

function MyButton({ count, onClick }) {
  return (
    <button onClick={onClick}>
      Clicked {count} times
    </button>
  );
}
使用 JSX 展开语法传递 props

有时候,传递 props 会变得非常重复:

function Profile({ person, size, isSepia, thickBorder }) {
  return (
    <div className="card">
      <Avatar
        person={person}
        size={size}
        isSepia={isSepia}
        thickBorder={thickBorder}
      />
    </div>
  );
}

重复代码没有错(它可以更清晰)。但有时你可能会重视简洁。一些组件将它们所有的 props 转发给子组件,正如 Profile 转给 Avatar 那样。因为这些组件不直接使用他们本身的任何 props,所以使用更简洁的“展开”语法是有意义的:

function Profile(props) {
  return (
    <div className="card">
      <Avatar {...props} />
    </div>
  );
}

将 JSX 作为子组件传递 等价于 slot 概念

将内容嵌套在 JSX 标签中时,父组件将在名为 children 的 prop 中接收到该内容。

function Card({ children }) {
  return (
    <div className="card">
      {children}
    </div>
  );
}

export default function Profile() {
  return (
    <Card>
      <Avatar
        size={100}
        person={{ 
          name: 'Katsuko Saruhashi',
          imageId: 'YfeOqp2'
        }}
      />
    </Card>
  );
}
  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

DevilAngelia

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值