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优化类名控制


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

相关推荐
天宇&嘘月2 小时前
web第三次作业
前端·javascript·css
小王不会写code3 小时前
axios
前端·javascript·axios
发呆的薇薇°4 小时前
vue3 配置@根路径
前端·vue.js
luckyext4 小时前
HBuilderX中,VUE生成随机数字,vue调用随机数函数
前端·javascript·vue.js·微信小程序·小程序
小小码农(找工作版)4 小时前
JavaScript 前端面试 4(作用域链、this)
前端·javascript·面试
前端没钱4 小时前
前端需要学习 Docker 吗?
前端·学习·docker
前端郭德纲5 小时前
前端自动化部署的极简方案
运维·前端·自动化
海绵宝宝_5 小时前
【HarmonyOS NEXT】获取正式应用签名证书的签名信息
android·前端·华为·harmonyos·鸿蒙·鸿蒙应用开发
码农土豆5 小时前
chrome V3插件开发,调用 chrome.action.setIcon,提示路径找不到
前端·chrome
鱼樱前端5 小时前
深入JavaScript引擎与模块加载机制:从V8原理到模块化实战
前端·javascript