React:初识React

React是什么?

React是由Meta公司研发,也就是Facebook的公司(马克扎克伯格这个见人)研发的构建Web和原生交互界面的库

不仅可以写网页,还可以写苹果和安卓上面的app

React的优势:

React也是前端里最流行的一个框架

搭建环境

使用cra快速搭建开发环境,create-react-app是一个快速 创建React开发环境的工具,底层由Webpack构建,封装了配置细节,开箱即用

首先安装node.jsNode.js安装及环境配置超详细教程【Windows系统】_windows 安装nodejs-CSDN博客

bash 复制代码
# 安装 nvm(如果尚未安装)
curl -o- https://raw.githubusercontent.com/nvm-sh/nvm/v0.39.3/install.sh | bash
#输入这个命令记得翻墙

# 重新加载 shell 配置
source ~/.bashrc  # 或者 source ~/.zshrc(如果你使用 zsh)

# 安装最新版本的 Node.js
nvm install --lts

# 或者安装特定版本(例如 v16)
nvm install 16

# 设置默认版本
nvm alias default 16
bash 复制代码
node -v  # 应该显示 v14 或更高版本
npm -v   # 确保 npm 也更新到最新版本

安装完node.js创建一个react文件

bash 复制代码
npx create-react-app react-basic

这就代表好了!

好了

启动npm:

bash 复制代码
npm start

出现这个画面说明你的项目创建成功并且可以启动了

介绍项目

package.json是项目的包文件,里面包括了一些核心包

选中部分为本次项目它依赖的一些核心包

下面这俩是最最最核心的包

javascript 复制代码
"react": "^19.0.0",
    "react-dom": "^19.0.0",

下面这段包含的是可执行的命令,我们刚刚启动npm的命令就来自于这里

常用的命令有start和build,build负责打包

这是src,是我们的源码目录

打开index.js,对里面的文件做清理

javascript 复制代码
import React from 'react';
import ReactDOM from 'react-dom/client';
import App from './App';

const root = ReactDOM.createRoot(document.getElementById('root'));
root.render(<App />);

对App.js做清理

javascript 复制代码
function App() {
  return (
    <div className="App">
   this is App
    </div>
  );
}

export default App;

如果出现这个界面说明清理工作一切正常

index.js是整个项目的入口,下面框出来的是react依赖的两个核心包

这个id为root的节点在public里的index.html里

所以index.js做的事就是把这个项目的根组件以React的方式渲染到public文件里的这个位置上👆

App.js是一切项目的根基,组件是从这个地方往下发散的,它被引入到index.js里,然后渲染到了public里的index.html里叫root的div里

App->index.js->public/index.html(root)

后面的基础语法都在App.js里

JSX基础

概念和本质

JSX:JavaScript和XML(HTML)的缩写,表示在JS代码中编写HTML模版结构,它是React中编写
UI模版的方式
下图框出来的地方就是JSX,他书写的地方在函数的内部

优势(经典既要又要):

  1. HTML的声明式模版写法 2. JS的可编程能力
    JSX的本质
    JSX并不是标准的JS语法,它是 JS的语法扩展 ,浏览器本身不能识别,需要通过 解析工具做解析 之后才能在浏览器中运行
    JSX->babel解析->js函数
    使用BABEL转化工具体验一下

高频场景

在JSX中可以通过 大括号语法{} 识别 JavaScript中的表达式,比如常见的变量、函数调用、方法调用等等

javascript 复制代码
const count = 100
function getName() {
  return '荷叶饭'
}
function App() {
  return (
    <div className="App">
      this is App
      {//使用引号传递字符串
        'this is message'
      }
      {//识别JavaScript变量
        count
      }
      {//函数调用
        getName()
      }
      {//方法调用
        new Date().getDate()
      }
      {//使用js对象
        <div style={{ color: 'red' }}>this is div</div>
      }
    </div>
  );
}

export default App;

只能写表达式,不能写语句(if语句、switch语句、变量声明属于语句,不是表达式,不能出现在{}中)

eg:实现列表渲染

循环哪个结构就在App方法里return哪个结构:

key是React里提升框架性能的,独一无二,赋值为字符串或number id

javascript 复制代码
const list = [
  { id: 1001, name: 'Vue' },
  { id: 1002, name: 'React' },
  { id: 1003, name: 'Angular' }
]

function App() {
  return (
    <div className="App">
      this is App
      <ul>
        {list.map(item => <li key={item.id}>{item.name}</li>)}
    </ul>
    </div>
  );
}

export default App;

愤怒

又好了嘻嘻

实现基础条件渲染

条件渲染就是根据条件进行渲染,有名字渲染名字,没名字渲染请登录

多种条件渲染的语法:在React中,可以通过逻辑与运算符&&(两种情况)、三元表达式(?:)(多种情况)实现基础的条件渲染

  • 逻辑与&&,短路运算符

  • 如果左侧的值为 true,则返回右侧的值。

  • 如果左侧的值为 false,则直接返回 false,不会计算右侧的值。

javascript 复制代码
const isLogin=true

function App() {
  return (
    <div className="App">
      this is App
    {/* 逻辑与&&,短路运算符 */}
    {isLogin&&<span>this is span</span>}
    </div>
  );
}

export default App;

三元表达式不用多说

javascript 复制代码
const isLogin=false
function App() {
  return (
    <div className="App">
      this is App
    {/* 逻辑与&&,短路运算符 */}
      {isLogin && <span>this is span</span>}
      {/* {三元表达式} */}
      {isLogin?<span>荷叶饭</span>:<span>loading...</span>}
    </div>
  );
}

export default App;

一想到这个玩意在后盾人里的课可能两句话就带过去了我就想笑

实现复杂的条件渲染

复杂的就是当有多种情况需要渲染时,&&和三元表达式不能满足要求

比如新闻渲染的时候有的没有图片,有的一张图片,有的三张图片,单独写一个函数来处理

javascript 复制代码
const articleType = 1//0,1,3
//定义核心函数:根据不同文章类型返回不同JSX模板
function getArticleTem() {
  if (articleType === 0) {
    return <div>我是无图文章</div>
  } else if (articleType === 1) {
    return <div>我是单图文章</div>
  } else {
    return <div>我是多图文章</div>
  }
}

function App() {
  return (
    <div className="App">
      this is App
      {getArticleTem()}
    </div>
  );
}

export default App;

React中的事件绑定

语法: on + 事件名称 = { 事件处理程序 } ,整体上遵循驼峰命名法

javascript 复制代码
function App() {
  const handleClick = () => console.log('button被点击了')
  return (
    <div className="App">
     <button onClick={handleClick}>click me</button>
 </div>
  );
}

export default App;

当事件绑定需要获取事件对象e时, 语法:在事件回调函数中 设置形参e,和原生js是一样的

javascript 复制代码
function App() {
   const handleClick = (e) => console.log('button被点击了e:',e)
  return (
    <div className="App">
     <button onClick={handleClick}>click me</button>
 </div>
  );
}

export default App;

可以获取事件对象捏

传递自定义参数:事件绑定的位置改造成箭头函数的写法,在执行clickHandler实际处理业务函数的时候传递实参

需要传递额外的参数,才需要使用箭头函数(如 onClick={() => handleClick('荷叶饭')})。

javascript 复制代码
function App() {
  const handleClick = (name) => console.log('button被点击了name:',name)
  return (
    <div className="App">
     <button onClick={()=>handleClick('荷叶饭')}>click me</button>    </div>
  );
}
export default App;

这个箭头函数是必须的,如果写成onClick={handleClick('荷叶饭')}handleClick 函数会在渲染时立即执行,而不是在点击时执行,箭头函数可以延迟函数的执行,直到点击事件发生。

箭头函数的等价写法

javascript 复制代码
<button onClick={function() { handleClick('荷叶饭'); }}>click me</button>

但是不简洁,更推荐箭头函数

如果我们既要有要,既要事件对象e,也要额外参数怎么解决?

箭头函数传参数给普通函数 函数再去接收参数

javascript 复制代码
function App() {
  const handleClick = (name,e) => console.log('button被点击了name:',name,'e:',e)
  return (
    <div className="App">
     <button onClick={(e)=>handleClick('荷叶饭',e)}>click me</button>   
 </div>
  );
}

export default App;

组件

概念:一个组件就是用户界面的一部分,它可以有自己的逻辑和外观,组件之间 可以互相嵌套,也可以复用多次

root是根组件,header是左边的灰块的上部分,main是左侧在主体区域,aside是右边的

main里重复的两块就是article*2,aside里重复的就是item*3
组件化开发可以让开发者像搭积木一样构建一个完整的庞大的应用

流程:定义组件->使用组件
在React中,一个组件就是首字母大写的函数,内部存放了组件的逻辑和视图UI, 渲染组件只需要把组件当成标签书写

即可

两种使用方法

javascript 复制代码
//定义组件
function Button() {
  //组件内部逻辑
  return <button>click me!</button>
}

function App() {
  // const handleClick = (name,e) => console.log('button被点击了name:',name,'e:',e)
  return (
    <div className="App">
      {/*使用组件(渲染组件)*/}
      {/*自闭和使用*/}
      <Button />
      {/*成对标签使用*/}
      <Button></Button>
    </div>
  );
}

export default App;

组件其实就是个函数,比较灵活,首字母大写,有两种使用方法:自闭和和成对标签,像标签一样灵活

useState基础使用

useState 是一个 React Hook(函数),它允许我们向组件添加一个状态变量(State), 从而控制影响组件的渲染结果
状态变量是 React 组件内部的一个特殊变量,用于存储组件的数据,当状态变量的值发生变化时,React 会自动重新渲染组件,以反映最新的数据
状态变量 和普通JS变量不同的是,状态变量一旦发生变化组件的视图UI 也会跟着变化 (数据驱动视图)
useState是React里的方法,返回的是个数组,数组里有两项:状态变量,修改状态变量的方法
只有用这个专门的修改状态变量的方法修改变量,才会在ui渲染里起效

javascript 复制代码
//useState实现一个计数器按钮
import { useState } from 'react'
function App() {
  // const handleClick = (name,e) => console.log('button被点击了name:',name,'e:',e)

  //1、调用useState添加一个状态变量
  const [count, setCount] = useState(0)
  //2、点击事件回调
  const handleClick = () => { setCount(count+1) }
  return (
    <div className="App">
      <button onClick={handleClick}>{count}</button>
    </div>
  );
}
export default App;

count++ 会直接修改 count 的值,而 React 的状态变量是不可变的(Immutable),不能直接修改。

那我就要问了:const handleClick = () => { setCount(count++) }这么写不也是通过setState 函数更新状态吗?为什么不行?

count++ 会先返回当前的值,再进行自增操作,而 React 的 setState 需要传入新的值,而不是修改原值。

那++count呢?++count 会先对 count 进行自增操作,然后返回自增后的值。这个操作会直接修改当前 count 的值,而 React 需要的是通过 setCount 来触发状态的更新,这就相当于状态变量的修改是你自己改的,不是通过对应的方法改的

正确的写法是应该直接传入 count + 1,而不是 count++,因为 count++ 只是修改了本地变量

对于对象来说也是这样,应该把新的对象写进对应方法,而不是直接修改

javascript 复制代码
import { useState } from 'react'

function App () {
  let [count, setCount] = useState(0)

  const handleClick = () => {
    // 直接修改 无法引发视图更新
    // count++
    // console.log(count)
    setCount(count + 1)
  }

  // 修改对象状态
  const [form, setForm] = useState({ name: 'jack' })

  const changeForm = () => {
    // 错误写法:直接修改
    // form.name = 'john'
    // 正确写法:setFrom 传入一个全新的对象
    setForm({
      ...form,
      name: 'john'
    })
  }

  return (
    <div>
      <button onClick={handleClick}>{count}</button>
      <button onClick={changeForm}>修改form{form.name}</button>
    </div>
  )
}

export default App

组件基础样式方案

javascript 复制代码
//useState实现一个计数器按钮
import { useState } from 'react'
function App() {
  // const handleClick = (name,e) => console.log('button被点击了name:',name,'e:',e)

  //1、调用useState添加一个状态变量
  const [count, setCount] = useState(0)
  //2、点击事件回调
  const handleClick = () => { setCount(count+1) }
  return (

    <div className="App">
       {/* //行内控制样式 */}
      <button onClick={handleClick} style={{color:'red',fontSize:'50px'}}>{count}</button>
    </div>
  );
}

export default App;

行内控制样式

也可以封装出来:

javascript 复制代码
const style = {
  color: 'red',
  fontSize:'50px'
}
//useState实现一个计数器按钮
import { useState } from 'react'
function App() {
  // const handleClick = (name,e) => console.log('button被点击了name:',name,'e:',e)

  //1、调用useState添加一个状态变量
  const [count, setCount] = useState(0)
  //2、点击事件回调
  const handleClick = () => { setCount(count+1) }
  return (

    <div className="App">
       {/* //行内控制样式 */}
           <button onClick={handleClick} style={style}>{count}</button>

    </div>
  );
}

export default App;

也可以使用类名外部引入

javascript 复制代码
//导入样式
import './index.css'
function App() {
  // const handleClick = (name,e) => console.log('button被点击了name:',name,'e:',e)

  //1、调用useState添加一个状态变量
  const [count, setCount] = useState(0)
  //2、点击事件回调
  const handleClick = () => { setCount(count + 1) }
  return (

    <div className="App">
      <button onClick={handleClick} className='foo'>{count}</button>
    </div>
  );
}

export default App;

//index.css
.foo{
  color:blue
}

案例:B站评论

javascript 复制代码
import { useEffect, useRef, useState } from 'react'
import './App.scss'
import avatar from './images/bozai.png'
import _ from 'lodash'
import classNames from 'classnames'
import { v4 as uuidV4 } from 'uuid'
import dayjs from 'dayjs'
import axios from 'axios'

// 当前登录用户信息
const user = {
  // 用户id
  uid: '30009257',
  // 用户头像
  avatar,
  // 用户昵称
  uname: '黑马前端',
}
// 导航 Tab 数组
const tabs = [
  { type: 'hot', text: '最热' },
  { type: 'time', text: '最新' },
]

// 封装请求数据的Hook

function useGetList () {
  // 获取接口数据渲染
  const [commentList, setCommentList] = useState([])

  useEffect(() => {
    // 请求数据
    async function getList () {
      // axios请求数据
      const res = await axios.get(' http://localhost:3004/list')
      setCommentList(res.data)
    }
    getList()
  }, [])

  return {
    commentList,
    setCommentList
  }
}


// 封装Item组件

function Item ({ item, onDel }) {
  return (
    <div className="reply-item">
      {/* 头像 */}
      <div className="root-reply-avatar">
        <div className="bili-avatar">
          <img
            className="bili-avatar-img"
            alt=""
            src={item.user.avatar}
          />
        </div>
      </div>

      <div className="content-wrap">
        {/* 用户名 */}
        <div className="user-info">
          <div className="user-name">{item.user.uname}</div>
        </div>
        {/* 评论内容 */}
        <div className="root-reply">
          <span className="reply-content">{item.content}</span>
          <div className="reply-info">
            {/* 评论时间 */}
            <span className="reply-time">{item.ctime}</span>
            {/* 评论数量 */}
            <span className="reply-time">点赞数:{item.like}</span>
            {/* 条件:user.id === item.user.id 的情况,才显示删除按键*/}
            {user.uid === item.user.uid &&
              <span className="delete-btn" onClick={() => onDel(item.rpid)}>
                删除
              </span>}
          </div>
        </div>
      </div>
    </div>
  )
}


const App = () => {
  // 渲染评论列表
  // 1. 使用useState维护list
  // const [commentList, setCommentList] = useState(_.orderBy(list, 'like', 'desc'))
  const { commentList, setCommentList } = useGetList()

  // 删除功能
  const handleDel = (id) => {
    console.log(id)
    // 对commentList做过滤处理
    setCommentList(commentList.filter(item => item.rpid !== id))
  }

  // tab切换功能
  // 1. 点击谁就把谁的type记录下来
  // 2. 通过记录的type和每一项遍历时的type做匹配 控制激活类名的显示
  const [type, setType] = useState('hot')
  const handleTabChange = (type) => {
    console.log(type)
    setType(type)
    // 基于列表的排序
    if (type === 'hot') {
      // 根据点赞数量排序 
      // lodash
      setCommentList(_.orderBy(commentList, 'like', 'desc'))
    } else {
      // 根据创建时间排序
      setCommentList(_.orderBy(commentList, 'ctime', 'desc'))
    }
  }

  // 发表评论
  const [content, setContent] = useState('')
  const inputRef = useRef(null)
  const handlPublish = () => {
    setCommentList([
      ...commentList,
      {
        rpid: uuidV4(), // 随机id
        user: {
          uid: '30009257',
          avatar,
          uname: '黑马前端',
        },
        content: content,
        ctime: dayjs(new Date()).format('MM-DD hh:mm'), // 格式化 月-日 时:分
        like: 66,
      }
    ])
    // 1. 清空输入框的内容
    setContent('')
    // 2. 重新聚焦  dom(useRef) - focus
    inputRef.current.focus()
  }

  return (
    <div className="app">
      {/* 导航 Tab */}
      <div className="reply-navigation">
        <ul className="nav-bar">
          <li className="nav-title">
            <span className="nav-title-text">评论</span>
            {/* 评论数量 */}
            <span className="total-reply">{10}</span>
          </li>
          <li className="nav-sort">
            {/* 高亮类名: active */}
            {tabs.map(item =>
              <span
                key={item.type}
                onClick={() => handleTabChange(item.type)}
                className={classNames('nav-item', { active: type === item.type })}>
                {item.text}
              </span>)}
          </li>
        </ul>
      </div>

      <div className="reply-wrap">
        {/* 发表评论 */}
        <div className="box-normal">
          {/* 当前用户头像 */}
          <div className="reply-box-avatar">
            <div className="bili-avatar">
              <img className="bili-avatar-img" src={avatar} alt="用户头像" />
            </div>
          </div>
          <div className="reply-box-wrap">
            {/* 评论框 */}
            <textarea
              className="reply-box-textarea"
              placeholder="发一条友善的评论"
              ref={inputRef}
              value={content}
              onChange={(e) => setContent(e.target.value)}
            />
            {/* 发布按钮 */}
            <div className="reply-box-send">
              <div className="send-text" onClick={handlPublish}>发布</div>
            </div>
          </div>
        </div>
        {/* 评论列表 */}
        <div className="reply-list">
          {/* 评论项 */}
          {commentList.map(item => <Item key={item.id} item={item} onDel={handleDel} />)}
        </div>
      </div>
    </div>
  )
}

export default App

classnames优化类名控制


一直存在的类名叫静态类名,根据不同情况肯存在肯不存在的类名叫动态类名
卧槽这个老师讲的也是依托

相关推荐
mldong6 小时前
你的 Vue3 项目也能有钉钉同款审批流设计器:npm 装包,10 分钟画出第一条审批流
前端·vue.js
2分钟速写快排6 小时前
什么是 RAG?如何用 RAG 实现一个用户记忆?
前端·后端·ai编程
passerby60617 小时前
如何自己造一个时间处理库
前端·javascript·github
走到天涯海角8 小时前
react里面的长列表渲染优化
前端·react.js·前端框架
小羊没烦恼!8 小时前
Hello Web API系列教程——Web API与国际化
java·服务器·前端·javascript·php
北岛贰8 小时前
迷茫焦虑期,我做了一个带支付带官网的 AI 聊天虚拟恋人 App
前端·人工智能·后端
mayaairi10 小时前
Vue2 组件通讯(三):全局事件总线、PubSub、插槽与组件实例属性
前端·javascript·vue.js
kyriewen10 小时前
面试官问我:AI 都能写代码了,前端凭什么还值 25K
前端·javascript·人工智能
风骏时光牛马11 小时前
AI源码分析:拆解模型底层实现逻辑
前端
BillKu11 小时前
全局样式变量:CSS 自定义属性(--border-color)和SCSS 变量($border-color)的说明
javascript·css·scss