React

104 阅读6分钟
  • 用于构建用户界面的 JavaScript

  • 脚手架创建项目

  • 指令:npx create-react-app 项目名称

  • 项目目录分析

    • node_modules:依赖包
    • public:公共文件
    • src :核心代码
      • App.css:根组件样式
      • App.js:根组件
      • App.text.js:根组件的测试
      • index.css:全局样式
      • index.js:入口文件(此文件中的代码在项目启动时都会被执行)
      • reportWebVitals.js:兼容浏览器
      • setupTests.js:做测试
  • 分析入口文件index.js

    // index.js
    
    import React from 'react'; // 引入react,得到react的api
    import ReactDOM from 'react-dom/client'; // 将虚拟DOM(vnode)=>真实DOM
    import './index.css'; // 全局样式
    import App from './App'; // 根组件
    
    console.log(ReactDOM);
    // 将虚拟DOM=>真实DOM,并挂载到对应的位置
    const root = ReactDOM.createRoot(document.getElementById('root')); // 挂载到对应的位置
    root.render( // 将虚拟DOM=>真实DOM
        <App />
    );
    
  • 初识函数式组件

    • 就是一个函数
    • 函数名必须首字母大写
    • 有**return**,写页面布局,相当于vue中的template
    • 函数体第一层定义的属性,就是该组件的属性、方法、动态数据,相当于vue中的setup,相当于生命周期created

JSX

  • JSX 是一个 JavaScript 的语法扩展,就是 jshtml 的结合
  • 特点:
    • 可以写js,也就是指令
    • 更加的安全(JSX可以防止注入攻击)
  • JSX相对于html的区别:
    • jsx类名 => className
    • 事件 => on事件类型

指令

动态数据(插值)

  • 语法:{ 表达式 }

    • 表达式:变量、四则运算、三元运算、js...
    function App() {
        let name = '喜羊羊'
        let num = 11
        return (
            <div>
                <h3>你好,{name}!</h3>
                <h4>今年{num + 1}岁啦</h4>
            </div>
        );
    }
    

条件渲染

  • 单个条件渲染

    • 语法:{ } + 三元运算符

      function App() {
          let num = 61
          return (
              // 模板
              <div>
                  {num >= 60 ? <Children /> : <h6>不合格</h6>}
              </div>
          );
      }
      // 子组件
      function Children() {
          return (
              <h1>合格</h1>
          )
      }
      
  • 多个条件渲染

    • 语法:{ } + 处理函数

      function App() {
          let num = 61
          // 处理函数
          function chchangeNum() {
              if (num >= 80) {
                  return (<h3>优秀</h3>)
              } else if (num >= 60 && num < 80) {
                  return (<h4>合格</h4>)
              } else {
                  return (<h5>不合格</h5>)
              }
          }
      	// 模板    
          return (
              <div>
                  {chchangeNum()}
              </div>
          );
      }
      

列表渲染

  • 语法:{ } + map

    • **map**会返回一个新数组
  • 作用:当布局有多个相同的时,可以使用列表渲染

    function App() {
        let list = ['🍉', '🍊', '🍑']
        return (
            <div>
                {
                    list.map((item, index) => {
                        return (<h3 key={index}>{item}</h3>)
                    })
                }
            </div>
        );
    }
    
  • **注意:**列表循环需要添加 key

    #*****
    为什么需要添加key?
    答:
    底层更新(列队的数据)通过key 来判断是否是相同的元素,
    如果是则复用,如果不是则重新创建,
    底层的diff 算法
    

动态样式(属性)

  • 语法:元素属性使用 { }

    import "./App.css"; // 引入样式
    function App() {
        let num = 12
        return (
            <div>
                <h2 className={num > 10 ? 'bindActive' : ''} data-num={num}> 张三</h2 >
            </div >
        );
    }
    

事件处理

  • 语法:on事件类型={()=>事件处理函数}

    #*****
    为什么要写一个嵌套函数?
    答:
    因为react 将模板内容(jsx语法) 变成 虚拟DOMReact.createElement())
    如果发现有事件处理,则将所有事件合成 一个事件,并'自动执行第一层'
    
    function App() {
        function btn() {
            console.log('事件触发');
        }
        return (
            <div>
                <button onClick={() => btn()}>点击</button>
            </div >
        );
    }
    
  • 事件传参

    function App() {
        let btn = (...argument) => {
            console.log('事件传参:', argument);
        }
        return (
            <div>
                <button onClick={() => btn('美羊羊', 18)}>点击</button>
            </div >
        );
    }
    
  • 事件对象

    • 事件对象的嵌套函数第一层的第一个形参,就是**事件对象**

      function App() {
          let btn = (e) => {
              console.log('事件对象:', e);
              console.log('事件目标:', e.target);
          }
          return (
              <div>
                  <button onClick={(e) => btn(e)}>点击</button>
              </div >
          );
      }
      
  • 阻止事件冒泡

    • 语法:事件对象.stopPropagation()

      function App() {
          let btnFather = () => {
              console.log('父元素触发');
          }
          let btnSon = (e) => {
              e.stopPropagation() // 阻止事件冒泡
              console.log('子元素触发');
          }
          return (
              <div>
                  <div onClick={() => { btnFather() }}>
                      父元素
                      <button onClick={(e) => btnSon(e)}>子元素</button>
                  </div>
              </div >
          );
      }
      
  • 阻止默认行为

    • 事件对象.preventDefault()

受控组件(双向数据绑定)

  • 可以在逻辑层获取到视图层数据,可又以将这个数据传递到视图层

  • 用户可以修改 js层的数据,把这样的组件,叫做 受控组件

  • 步骤:

    1.引入react的useState
    2.表单元素绑定动态属性
    3.给表单元素绑定事件
    4.通过事件对象获取到表单value并赋值
    
  • useState

    import { useState } from 'react' //引入
    let [name, setName] = useState('默认值')
    
// 引入react中的useState
import { useState } from 'react'
function App() {
    // 逻辑层
    let [name, setName] = useState('')
    let changeIpt = (e) => {
        console.log(e.target.value); // 获取表单的值
        setName(e.target.value) //赋值给动态数据
    }
    return (
        <div>
            <input value={name} onInput={(e) => changeIpt(e)}></input>
        </div>
    );
}

组件

创建组件

  • 创建方式:

    • 使用函数创建组件:函数式组件(推荐)
    • 使用class创建组件:类组件(旧方式不推荐,后面再补充)
  • 函数式组件

    • 特点:

      1.函数名首字母必须大写
      2.函数的return,就是组件的内容
      3.函数体就是组件的js,逻辑层
      4.当该函数被调用,相当于触发了组件的created生命周期,初始化完毕组件的属性
      
    • 使用:

      • <函数名></函数名>
      • <函数名 />
    function App() {
        // 组件js,逻辑层
        let name = '喜羊羊'
        console.log(name);
        return (
            <div>{name}</div>
        );
    }
    

父传子

  • 父组件通过子组件的 根标签的属性 传递参数

    <Son money={money} fn={fn} data='200'></Son>
    
  • 子组件通过 组件函数 的形参 props 来接收,接收的是一个对象

    function Son(props) {
        return (<div>子组件</div>)
    }
    
function App() {
    let money = 10
    const fn = () => {
        console.log('传给子组件的方法');
    }
    return (
        <div>
            父组件
            <Son money={money} fn={fn} data='200'></Son>
        </div>
    );
}
// 子组件
function Son(props) {
    // 子组件使用形参props接收父组件传来的数据
    console.log(props);
    return (
        <div>子组件</div>
    )
}

子传父

  • 本质:函数的声明和调用

  • 在子组件中调用父组件中的方法

  • 步骤

    1.父组件中声明方法,形参value用来接收子组件传来的参数
    2.方法传递给子组件
    3.在子组件中调用父组件的方法
    
function App() {
    const fn = (value) => {
        console.log('获取到子组件的数据:', value);
    }
    return (
        <div>
            父组件
            <Son fn={fn}></Son>
        </div>
    );
}
// 子组件
function Son(props) {
    const sonFn = () => {
        props.fn(100)
    }
    return (
        <div>
            <button onClick={() => { sonFn() }}>子组件</button>
        </div>
    )
}

本地存储

  • 使用浏览器的本地存储

provider和inject

  • useContext
  • hooks 中进行详细解释

redux

  • 全局状态管理
  • 在后面做详细解释

组件的组合

  • react 中,只要有自己的功能模块,就需要进行抽离(模块化划分)

    function App() {
        return (
            <div>
                <Swiper></Swiper>
                <CommentS></CommentS>
                <HotPlay></HotPlay>
                <LisBooks></LisBooks>
                <NewSong></NewSong>
            </div>
        );
    }
    

封装组件

  • 思路

    1.完成基本样式
    2.确定动态属性
    3.添加事件、组件通信
    
  • 🌰

    // 封装input组件
    function Zeroinput(props: any) {
        let [type, setType] = useState(props.type) // input类型
        let [isEmpty, setIsEmpty] = useState(false) // 校验是否为空
        let [value, setValue] = useState('') // input表单的值
        let [flag, setFlag] = useState(false) // 控制表单是否开启校验
        // 隐藏密码
        const changehide = () => {
            setType('password')
        }
        // 显示密码
        const changeshow = () => {
            setType('text')
        }
        // 明密文切换
        const eye = () => {
            // 由于ract的更新机制是重新加载组件,所以此处陆毅不使用监听器
            if (type == 'text') {
                return (<i onClick={() => changehide()} className={'iconfont ' + 'icon-xianshi'}></i>)
            } else {
                return (<i onClick={() => changeshow()} className={'iconfont ' + 'icon-yincang'}></i>)
            }
        }
        // 监听
        useEffect(() => {
            if (!value && flag) { // 表单value值为空且已输入过内容
                setIsEmpty(true)
            } else {
                setIsEmpty(false)
            }
        }, [value])
        // 受控组件(事件双向绑定)
        const getValue = (e: any) => {
            setValue(e.target.value)
            setFlag(true) // 此时开启表单非空校验
        }
        return (
            <div className='zeroinput'>
                {/* icon图标 */}
                {props.icon ? <i className={'iconfont ' + props.icon}></i> : ''}
                {/* input */}
                <input onInput={(e) => getValue(e)} type={type} placeholder={props.placeholder} value={value} />
                {/* 明密文 */}
                {props.eye ? eye() : ''}
                {/* 校验提示 */}
                {isEmpty ? <div className='isEmpty'>不能为空哦~</div> : ''}
            </div>
        )
    }
    export default Zeroinput
    
    //使用封装的input组件
    <Zeroinput placeholder='请输入手机号' type='text' icon='icon-shoujihao' />
    <Zeroinput placeholder='请输入密码' type='password' icon='icon-mima' eye />