react_1

  • 组件中使用了 jsx 语法,即在 js 中直接使用 html 标签或组件标签

  • 函数式组件必须返回标签片段

在 index.js 引入组件

import React from 'react'
import ReactDOM from 'react-dom/client'
import './index.css'
import reportWebVitals from './reportWebVitals'
// 1. 引入组件
import Hello from './pages/Hello'

const root = ReactDOM.createRoot(document.getElementById('root') as HTMLElement)
root.render(
  <React.StrictMode>
    {/* 2. 将原来的 <App/> 改为 <Hello></Hello> */}
    <Hello></Hello>
  </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()

将欢迎词作为属性传递给组件

<Hello msg='你好'></Hello>
  • 字符串值,可以直接使用双引号赋值

  • 其它类型的值,用 {值}

而组件修改为

export default function Hello(props: { msg: string }) {
  return <h3>{props.msg}</h3>
}
jsx 原理
export default function Hello(props: { msg: string }) {
  return <h3>{props.msg}</h3>
}

在 v17 之前,其实相当于

import { createElement } from "react";
export default function Hello(props: {msg: string}) {
  return createElement('h3', null, `${props.msg}`)
}

3) 人物卡片案例

样式已经准备好 /src/css/P1.css

#root {
  display: flex;
  width: 100vw;
  height: 100vh;
  justify-content: center;
  align-items: center;
}

div.student {
  flex-shrink: 0;
  flex-grow: 0;
  position: relative;
  width: 128px;
  height: 330px;
  /* font-family: '华文行楷'; */
  font-size: 14px;
  text-align: center;
  margin: 20px;
  display: flex;
  justify-content: flex-start;
  background-color: #7591AD;
  align-items: center;
  flex-direction: column;
  border-radius: 5px;
  box-shadow: 0 0 8px #2c2c2c;
  color: #e8f6fd;
}

.photo {
  position: absolute;
  width: 100%;
  height: 100%;
  top: 0;
  border-radius: 0%;
  overflow: hidden;
  transition: 0.3s;
  border-radius: 5px;
}

.photo img {
  width: 100%;
  height: 100%;
  /* object-fit: scale-down; */
  object-fit: cover;
}

.photo::before {
  position: absolute;
  content: '';
  width: 100%;
  height: 100%;
  background-image: linear-gradient(to top, #333, transparent);
}

div.student h2 {
  position: absolute;
  font-size: 20px;
  width: 100%;
  height: 68px;
  font-weight: normal;
  text-align: center;
  margin: 0;
  line-height: 68px;
  visibility: hidden;
}

h2::before {
  position: absolute;
  top: 0;
  left: 0;
  content: '';
  width: 100%;
  height: 68px;
  background-color: rgba(0, 0, 0, 0.3);
  border-top-left-radius: 5px;
  border-top-right-radius: 5px;  
}

div.student h1 {
  position: absolute;
  top: 250px;
  font-size: 22px;
  margin: 0;
  transition: 0.3s;
  font-weight: normal;
}

div.student p {
  margin-top: 300px;
  width: 80%;
  font-weight: normal;
  text-align: center;
  padding-bottom: 5px;
  border-bottom: 1px solid #8ea2b8;
}

.student:hover .photo::before {
  display: none;
}

.student:hover .photo {
  width: 90px;
  height: 90px;
  top: 90px;
  border-radius: 50%;
  box-shadow: 0 0 15px #111;
}

.student:hover img {
  object-position: 50% 0%;
}

.student:hover h1 {
  position: absolute;
  top: 190px;
  width: 40px;
}

div.student:hover h2 {
  visibility: visible;
}

类型 /src/model/Student.ts

export interface Student {
  id: number,
  name: string,
  sex: string,
  age: number,
  photo: string
}

组件 /src/pages/P1.tsx

import { Student } from '../model/Student'
import '../css/P1.css'
export default function P1(props: { student: Student }) {
  return (
    <div className='student'>
      <div className='photo'>
        <img src={props.student.photo}/>
      </div>
      <h1>{props.student.name}</h1>
      <h2>{props.student.id}</h2>      
      <p>性别 {props.student.sex} 年龄 {props.student.age}</p>
    </div>
  )
}

使用组件

const stu1 = { id: 1, name: '张三', sex: '男', age: 99, photo: '/imgs/1.png' }
const stu2 = { id: 2, name: '李四', sex: '男', age: 20, photo: '/imgs/2.png' }
const stu3 = { id: 3, name: '王五', sex: '男', age: 30, photo: '/imgs/3.png'}

<P1 student={stu1}></P1>
<P1 student={stu2}></P1>
<P1 student={stu3}></P1>
路径
  • src 下的资源,要用相对路径引入

  • public 下的资源,记得 / 代表路径的起点

标签命名
  • 组件标签必须用大驼峰命名

  • 普通 html 标签必须用小写命名

事件处理
import { Student } from '../model/Student'
import '../css/P1.css'
export default function P1(props: { student: Student }) {
    
  function handleClick(e : React.MouseEvent){
    console.log(student)
    console.log(e)
  }
  
  return (
    <div className='student'>
      <div className='photo' onClick={handleClick}>
        <img src={props.student.photo}/>
      </div>
      <h1>{props.student.name}</h1>
      <h2>{props.student.id}</h2>
      <p>性别 {props.student.sex} 年龄 {props.student.age}</p>
    </div>
  )
}

  • 事件以小驼峰命名

  • 事件处理函数可以有一个事件对象参数,可以获取事件相关信息

  • 列表 & Key
    import { Student } from '../model/Student'
    import P1 from './P1'
    
    export default function P2(props: { students: Student[] }) {
      return (
        <>
          {props.students.map((s) => ( <P1 student={s} key={s.id}></P1> ))}
        </>
      )
    }

  • key 在循环时是必须的,否则会有 warning

  • 也可以这么做

  • import { Student } from '../model/Student'
    import P1 from './P1'
    
    export default function P2(props: { students: Student[] }) {
      const list = props.students.map((s) => <P1 student={s} key={s.id}></P1>)
      return <>{list}</>
    }

    使用组件

  • const stu1 = { id: 1, name: '张三', sex: '男', age: 99, photo: '/imgs/1.png' }
    const stu2 = { id: 2, name: '李四', sex: '男', age: 20, photo: '/imgs/2.png' }
    const stu3 = { id: 3, name: '王五', sex: '男', age: 30, photo: '/imgs/3.png'}
    <P2 students={[stu1,stu2,stu3]}></P2>
    条件渲染

    P1 修改为

    import { Student } from '../model/Student'
    import '../css/P1.css'
    export default function P1(props: { student: Student; hideAge?: boolean }) {
      function handleClick() {
        console.log(props.student)
      }
    
      const ageFragment = !props.hideAge && <span>年龄 {props.student.age}</span>
    
      return (
        <div className='student'>
          <div className='photo' onClick={handleClick}>
            <img src={props.student.photo} />
          </div>
          <h1>{props.student.name}</h1>
          <h2>{props.student.id}</h2>
          <p>
            性别 {props.student.sex} {ageFragment}
          </p>
        </div>
      )
    }

  • 子元素如果是布尔值,nullish,不会渲染

  • P2 修改为

  • import { Student } from '../model/Student'
    import P1 from './P1'
    
    export default function P2(props: { students: Student[]; hideAge?: boolean }) {
      const list = props.students.map((s) => (
        <P1 student={s} hideAge={props.hideAge} key={s.id}></P1>
      ))
      return <>{list}</>

    使用组件

  • const stu1 = { id: 1, name: '张三', sex: '男', age: 99, photo: '/1.png' }
    const stu2 = { id: 2, name: '李四', sex: '男', age: 45, photo: '/2.png' }
    const stu3 = { id: 3, name: '王五', sex: '男', age: 45, photo: '/3.png'}
    
    <P2 students={[stu1,stu2,stu3]} hideAge={true}></P2>
    参数解构

    以 P1 组件为例

    import { Student } from '../model/Student'
    import '../css/P1.css'
    export default function P1
    ({ student, hideAge = false }: { student: Student, hideAge?: boolean }) {
      
      function handleClick() {
        console.log(student)
      }
    
      const ageFragment = !hideAge && <span>年龄 {student.age}</span>
    
      return (
        <div className='student'>
          <div className='photo' onClick={handleClick}>
            <img src={student.photo} />
          </div>
          <h1>{student.name}</h1>
          <h2>{student.id}</h2>
          <p>
            性别 {student.sex} {ageFragment}
          </p>
        </div>
      )
    }

  • 可以利用解构赋值语句,让 props 的使用更为简单

  • 对象解构赋值还有一个额外的好处,给属性赋默认值

    const stu1 = { id: 1, name: '张三', sex: '男', age: 99, photo: '/1.png' }
    
    <P1 student={stu1}></P1>

    4) 处理变化的数据

    入门案例侧重的是数据展示,并未涉及到数据的变动,接下来我们开始学习 react 如何处理数据变化

    axios

    首先来学习 axios,作用是发送请求、接收响应,从服务器获取真实数据

    安装

    npm install axios

    定义组件

  • import axios from 'axios'
    export default function P4({ id }: { id: number }) {
      async function updateStudent() {
        const resp = await axios.get(`http://localhost:8080/api/students/${id}`)
        console.log(resp.data.data)
      }
    
      updateStudent()
    
      return <></>
    }

  • 其中 /api/students/${id} 是提前准备好的后端服务 api,会延迟 2s 返回结果

  • 使用组件

  • <P4 id={1}></P4>

    在控制台上打印

  • {
        "id": 1,
        "name": "宋远桥",
        "sex": "男",
        "age": 40
    }

    当属性变化时,会重新触发 P4 组件执行,例如将 id 从 1 修改为 2

    执行流程

  • 首次调用函数组件,返回的 jsx 代码会被渲染成【虚拟 dom 节点】(也称 Fiber 节点)

    • 根据【虚拟 dom 节点】会生成【真实 dom 节点】,由浏览器显示出来

  • 当函数组件的 props 或 state 发生变化时,才会重新调用函数组件,返回 jsx

    • jsx 与上次的【虚拟 dom 节点】对比

      • 如果没变化,复用上次的节点

      • 有变化,创建新的【虚拟 dom 节点】替换掉上次的节点

  • 由于严格模式会触发两次渲染,为了避免干扰,请先注释掉 index.tsx 中的 <React.StrictMode>

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

打赏作者

敲代码的翠花

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

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

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

打赏作者

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

抵扣说明:

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

余额充值