react

官网 :https://zh-hans.reactjs.org

react 是一个js核心库,如同jQuery 一样,具有大量react生态(围绕‘react’核心开发的库)

1.特点

          1-1 声明式

             (js中的数据决定页面最终渲染结果)

          1-2  组件化

              (包含外观和行为,独立可运行的模块,称为组件)

           1-3 一次学习,跨平台编写

               (`react` 可以开发 桌面web页面,移动端页面,移动app,桌面app)

2.环境的搭建

         

## 使用cdn在html引入react

react可以在现用的html页面中直接使用,通过cdn引入以下几个库:

```html
<!-- react 需要引入 react 核心库 和 react-dom 库 -->
<script crossorigin src="https://unpkg.com/react@18/umd/react.development.js"></script>
<script crossorigin src="https://unpkg.com/react-dom@18/umd/react-dom.development.js"></script>

<!-- 为了更方便书写 react 通常会使用 jsx 语法,为了支持该语法 需要引入 babel -->
<script src="https://unpkg.com/babel-standalone@6/babel.min.js"></script>
```

然后可以在 body 中加入如下代码:

```html
<body>
    <div id="root"></div>

    <!-- 采用 jsx 语法 需要在 type 处加上 text/babel -->
    <script type="text/babel">

        ReactDOM.render(
            <h1>Hello World</h1>,
            document.getElementById('root')
        );

    </script>
</body>
```

这里需要介绍几个概念:

- react.js 这是框架核心库,用于解析 react 语法
- react-dom.js 这个库是用于 react 元素(也就是react的文档对象模型)的创建
- babel.js 这个库用来将 jsx 语法翻译为普通的 js

> 注意:此处为了学习 react 的语法引入了 jsx,在真实项目中不会在页面上直接使用 babel

## 使用create-react-app创建react应用

这种方法需要安装 node.js

首先安装 create-react-app 命令行工具

官网:<https://zh-hans.reactjs.org/docs/create-a-new-react-app.html#create-react-app>

github: <https://github.com/facebook/create-react-app>

create-react-app文档:<https://create-react-app.dev/>

执行以下命令安装 create-react-app 工具

```shell
npm install -g create-react-app
```

然后在任一目录下执行以下命令创建react应用:

```shell
npm init react-app <project-name>
# 或
create-react-app <project-name>
# 其中 <project-name> 是应用的名称
```

进入到项目目录下并运行 `npm run start` 启动项目

## 创建一个使用typescript书写的react项目

<https://create-react-app.dev/docs/adding-typescript>

3.jsx语法

<body>
<div id="app"></div>
</body>
<script type="text/babel">
const root = ReactDOM.createRoot(document.querySelector('#app'));

    // 使用jsx创建一个react-dom对象
    // react-dom 是 react 封装的类似 dom 对象的对象
    // 作用: 用于描述页面元素
    const h1 = <h1>hello world</h1>

    // jsx 使用 圆括号 声明多行的 react-dom 对象
    const box = (
        <div className="box">
            <span>hello box</span>
        </div>
    )

    const element = (
        <div>
            root1
        </div>
        // react-dom 中只能有一个根节点
        // <div>
        //     root2
        // </div>
    )


    let now = new Date()

    // 插值
    const chazhi = (
        <div>
            { /*使用花括号进行插值 插值的内容可以是js表达式*/}
            {'当前时间: ' + now.toLocaleString()}
        </div>
    )

    let id = 'thisIsMyId'

    // 定义样式对象
    let myStyle = {
        width: '100px',
        height: '100px',
        backgroundColor: '#f00'
    }

    // 插值 html 属性
    const htmlAttr = (
        <div>
            {/* 插值id */}
            <div id={id}>
                hello attr
            </div>
            {/* 插值 style */}
            {/* style 属性无法直接赋值一个字符串如下: */}
            {/*<div style="width: 100px; height: 100px; background-color: #f00"></div>*/}
            <div style={myStyle}></div>

            {/* 插值 class */}
            <div className="box active"></div>
        </div>
    )

    // 使用函数创建react-dom对象
    const fnDom = React.createElement(
        // 标签名
        'div',
        // 标签上的属性
        {id: 'myFunctionDom', className: 'my-dom', style: {width: '500px', height: '50px', backgroundColor: '#00f'}},
        // 子元素数组 或 标签体字符串
        [
            // 此处的第三个参数就是一个普通的字符串充当 span 标签的标签体
            React.createElement('span', {key: '1'}, 'hello world'),
            // 若此处 createElement 第三个参数是一个数组的话,需要给元素添加 key 属性
            // key 属性是一个唯一值 不重复即可
            React.createElement('span', {key: '2'}, 'hello world')
        ]
    )

    // root.render 函数可以渲染一个 react-dom 对象
    root.render((
        <div className="container">
            {/* 插值的内容 若是 react-dom 对象 那么就会被页面显示出来 */}
            {h1}
            {box}
            {element}
            {chazhi}
            {htmlAttr}
            {fnDom}
        </div>
    ))

</script>

4 条件渲染

<script type="text/babel">
    let sex = 'male'

    ReactDOM.createRoot(document.querySelector('#root')).render((
        <div>
            {/* 使用 && 进行短路运算 前一个表达式为true时 就显示后面表达式的内容 */}
            {sex === 'male' && <div style={{color: '#00f'}}>男</div>}
            {sex === 'female' && <div style={{color: 'pink'}}>女</div>}
            {sex === 'other' && <div style={{color: '#ff0'}}>其他</div>}

            {/* 使用三元运算符 按条件显示不同的内容 */}
            {sex === 'male' ? <div style={{color: '#00f'}}>男</div> :
                sex === 'female' ? <div style={{color: 'pink'}}>女</div> :
                    <div style={{color: '#ff0'}}>其他</div>
            }
        </div>
    ))
</script>

5.循环渲染

<script type="text/babel">
    let students = [
        {
            name: '张三',
            sex: 'male',
            age: 17
        },
        {
            name: '李四',
            sex: 'female',
            age: 24
        },
        {
            name: '隔壁老王',
            sex: 'other',
            age: 30
        },
    ]

    ReactDOM.createRoot(document.querySelector('#root')).render((
        <div>
            <ul>
                {/* 循环渲染,使用一个数组的map函数 返回一个由 react-dom 充当成员形成的一个新数组
                    循环渲染一定要添加 key
                */}
                {students.map((item,index) => <li key={index}>姓名: {item.name}; 性别: {item.sex === 'male' ? '男' :
                    item.sex === 'female' ? '女' : '不详'
                }; 年龄: {item.age}</li>)}
            </ul>
        </div>
    ))
</script>

6.受控组件

(被 react 的 state 控制显示和输入的表单元素称为受控组件, 受控组件的数据来自 state 而不是表单元素自身,react 中 所有的事件都不能通过 return false 来屏蔽默认事件)

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta http-equiv="X-UA-Compatible" content="IE=edge">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>Title</title>
    <script src="js/babel.min.js"></script>
    <script src="js/react.development.js"></script>
    <script src="js/react-dom.development.js"></script>
</head>
<body>
<div id="root"></div>
</body>
<script type="text/babel">
    // 知识点
    // 什么是受控组件?
    //      被 react 的 state 控制显示和输入的表单元素称为受控组件
    //      受控组件的数据来自 state 而不是表单元素自身
    // react 中 所有的事件都不能通过 return false 来屏蔽默认事件
    // 声明 input[type=text] 、select 和 textarea 的受控组件
    // 声明 input[type=radio] input[type=checkbox] 的受控组件
    function App() {
        const [name, setName] = React.useState('')
        const [clazz, setClazz] = React.useState('')
        const [desc, setDesc] = React.useState('')
        const [sex, setSex] = React.useState('other')
        const [hobbies, setHobbies] = React.useState(['dlq', 'ymq'])

        function submit(ev) {
            ev.preventDefault()
            console.log(name)
            console.log(clazz)
            console.log(desc)
            console.log(sex)
            console.log(hobbies)
        }

        function onNameInput(ev) {
            console.log(ev)
            console.log(ev.target.value)
            // 由于此处的输入框是受控组件
            // 所以若不改变状态, name 值不发生改变,页面就不会更新
            setName(ev.target.value)
        }

        function onClazzChange(ev) {
            console.log(ev.target.value)
            setClazz(ev.target.value)
        }

        function onDescInput(ev) {
            console.log(ev.target.value)
            setDesc(ev.target.value)
        }

        function onSexChange(ev) {
            console.log(ev.target.value)
            setSex(ev.target.value)
        }

        function onHobbiesChange(ev) {
            console.log(ev.target.value)
            if (hobbies.includes(ev.target.value)) {
                // 若已经包含就了删除
                setHobbies(_hobbies => {
                    let i = _hobbies.findIndex(item => item === ev.target.value)
                    _hobbies.splice(i, 1)
                    return [..._hobbies]
                })
            } else {
                // 不存在时 就添加进数组
                setHobbies(_hobbies => {
                    _hobbies.push(ev.target.value)
                    return [..._hobbies]
                })
            }
        }

        return (
            <div>
                <form>
                    <div>
                        <label>
                            姓名:
                            <input type="text" value={name} onInput={onNameInput}/>
                        </label>
                    </div>
                    <div>
                        <label>
                            班级:
                            <select value={clazz} onChange={onClazzChange}>
                                <option value="" disabled>请选择</option>
                                <option value="1">一班</option>
                                <option value="2">二班</option>
                                <option value="3">三班</option>
                            </select>
                        </label>
                    </div>
                    <div>
                        <label>
                            简介:
                            <textarea rows="4" value={desc} onInput={onDescInput}></textarea>
                        </label>
                    </div>
                    <div>
                        <label>
                            性别:
                            <label><input onChange={onSexChange} checked={sex === 'male'} type="radio" value="male"/>男</label>
                            <label><input onChange={onSexChange} checked={sex === 'female'} type="radio"
                                          value="female"/>女</label>
                            <label><input onChange={onSexChange} checked={sex === 'other'} type="radio" value="other"/>其他</label>
                        </label>
                    </div>
                    <div>
                        <label>
                            爱好:
                            <label><input onChange={onHobbiesChange} checked={hobbies.includes('dlq')} type="checkbox"
                                          value="dlq"/>打篮球</label>
                            <label><input onChange={onHobbiesChange} checked={hobbies.includes('tzq')} type="checkbox"
                                          value="tzq"/>踢足球</label>
                            <label><input onChange={onHobbiesChange} checked={hobbies.includes('ymq')} type="checkbox"
                                          value="ymq"/>羽毛球</label>
                        </label>
                    </div>
                    <div>
                        <button onClick={submit}>提交</button>
                    </div>
                </form>
            </div>
        )
    }

    ReactDOM.createRoot(document.querySelector('#root')).render(<App/>)
</script>
</html>

7.状态提升

组件向上级组件汇报自己状态的过程叫做状态提升

<script type="text/babel">
    // 文档:https://zh-hans.reactjs.org/docs/lifting-state-up.html

    // 知识点
    // 什么是状态提升?
    //      组件向上级组件汇报自己状态的过程叫做状态提升
    // 应用场景
    //      1. 在父组件中需要读取子组件状态时,可以让子组件状态提升给父组件
    //      2. 子组件需要发出某个事件,且事件将携带参数时
    // 如何实现?
    //      理论原理:
    //          组件可以利用自身的 props 属性将自身状态提升到上级组件
    //          表现形式类似于触发事件
    //          上级组件只要绑定事件(本质是提供一个回调函数)接收参数即可
    //      操作方法:
    //          1. 父组件中,给子组件添加 props 属性,并分配一个函数值,该函数接收子组件的状态值为参数
    //          2. 子组件中,在适当时机调用父组件提供的 props,并将自身状态作为参数传入父组件提供的函数

    function Child(props) {
        const [count, setCount] = React.useState(0);

        function increase() {
            setCount(_c => _c + 1)
        }

        // 监视 count 的变化
        React.useEffect(() => {
            // count 发生变化
            // 通知父组件
            // 将状态值作为参数传入
            props.countChange(count)
        }, [count])

        return (
            <div>
                <div>count: {count}</div>
                <div>
                    <button onClick={increase}>+</button>
                </div>
            </div>
        )
    }

    function App() {

        // 此函数的参数用于接收子组件状态
        function onCountChange(args) {
            console.log(args)
        }

        return (
            <div>
                {/* 给子组件分配一个函数 用于接收组件状态 */}
                <Child countChange={onCountChange}></Child>
            </div>
        )
    }

    ReactDOM.createRoot(document.querySelector('#root')).render(<App/>)
</script>

8.元素分组 React.Fragment

 React.Fragment 内置组件起到一个给元素分组的作用,最终不会显示到页面上 作用类似小程序中的 <block> 标签

9.组件通信

什么是组件通信: 父组件和子组件相互传递参数的过程,就是组件间的通信

 应用场景

    //      子组件的状态依赖于父组件传入的 props

    //      由于 props 是只读的

    //      所以要修改 props 子组件只能通知父组件更新数据

    //      这个过程就会用到组件间的通信方法

通信方法:

    //      1. 父组件将自身 state 作为参数传入子组件 props

    //      2. 子组件依赖 props 显示内容

    //      3. 子组件内希望修改 props 中的值,则发出一个事件

    //      4. 父组件绑定子组件发出的事件并接收参数

    //      5. 父组件接收事件后更新自身 state,此时 react 会自动更新子组件 props

10.错误边界

什么是错误边界

    //      react 的错误边界类似 js 的 catch

    //      错误边界是一个 react 组件,其内部的子组件一旦发生异常就会触发错误边界

  应用场景

    //      需要对页面异常做出更友好的提示的时候,可以使用错误边界

如何使用?

    //      1. 错误边界需要是一个 class 组件(函数组件不能充当错误边界)

    //      2. 错误边界需要声明一个 hasError 状态

    //      3. 错误边界需要实现生命周期钩子函数: static getDerivedStateFromError(error) 该函数返回要修改的状态,通常是{hasError: true} ; componentDidCatch(error, errorInfo) [该函数非必要]

    //      4. 错误边界的渲染函数的内容,根据 hasError 状态进行调整

    //      5. 使用错误边界的时候,在错误边界的标签体中嵌入其他子组件

    //      6. 子组件生命周期任何阶段发生异常,都会被错误边界捕获到,从而显示错误的提示

// 使用 类组件声明错误边界
    class ErrorBoundary extends React.Component {
        state = {
            // hasError 状态值用于指示 错误边界 内的子组件是否有错误
            hasError: false
        }

        // 当组件发生异常时,在调用 render 函数前先会调用此函数
        // error 当前正被触发的异常对象
        static getDerivedStateFromError(error) {
            console.log('getDerivedStateFromError')
            console.log(error)
            // 此处在渲染之前修改 hasError 状态为 true 代表产生了异常
            return {hasError: true}
        }

        // 此为非必填函数
        // 该生命周期将在捕获异常完成并渲染完成后触发
        componentDidCatch(error, errorInfo) {
            console.log('componentDidCatch')
            console.log(error)
            console.log(errorInfo)
        }

        render() {
            // render 函数通过 hasError 判断应该显示什么内容
            // 没有异常就显示 ErrorBoundary 的标签体里的内容 否则显示一个友好的错误提示
            return this.state.hasError ? <div>错误时候看到的内容</div> :
                <div>{this.props.children}</div>
        }
    }

11,context 上下文

 什么是 context?

    //      context 是一对 组件,用于向后代组件提供参数

    //      注意: context 的数据是只读的

    // 应用场景

    //      在一个典型的 React 应用中,数据是通过 props 属性自上而下(由父及子)进行传递的

    //      但此种用法对于某些类型的属性而言是极其繁琐的

    //      (例如:地区偏好,UI 主题,因为他们是全局属性,每个和ui相关组件都应该读取其值)

    //      所以:很多不同层级的组件需要访问一些同样的数据,可以考虑使用 context

    // 概念

    //      context 提供了一对组件 如下:

    //      Provider(供应商) 提供数据的组件

    //      Consumer(消费者) 消费数据的组件

    // 使用方法

   1. 创建 context

    //          语法:const MyContext = React.createContext(defaultValue);

    //          defaultValue 若组件找不到对应 context 时会获取默认值

    //          context 的值可以是一个对象

      2. 页面中将 MyContext.Provider 作为父节点使用,并传入 value 参数作为 Provider 提供的数据

   3. 子组件接收 Provider 提供的数据

    //          3.1 类组件接收方法:

    //              1. 给类名的 contextType 赋值 context 对象,例如:

    //                  MyClassComponent.contextType = MyContext

    //              2. 在组件内使用 this.context 访问 Provider 提供的数据

      //    3.2 函数组件接收方法:

    //              1. 在页面中,给要使用数据的函数组件套上一个 Consumer 父组件

    //              2. 在 Consumer 内 通过工厂函数返回要接收 Provider 数据的函数组件,工厂函数接收一个 value 参数,该参数即为 Provider 提供的数据

    //              3. 函数组件通过 props 接收 value 参数

    //                  例如:

    //                  <MyContext.Consumer>

    //                      {value => <FunctionComponent theme={value}></FunctionComponent>}

    //                  </MyContext.Consumer>

    // Provider 提供一个动态可变的值

绑定多个 Context

    //      官网:https://zh-hans.reactjs.org/docs/context.html#consuming-multiple-contexts

    //      重点在于利用多重嵌套的 Context.Consumer,来实现绑定多个 Context

12.转发(ref)

           类组件使用 React.createRef()

          函数组件使用 React.useRef()

         通用方法 使用箭头函数 el => { temp = el }

13.传送门

什么是传送门

    //      传送门是 react 通过 ReactDOM.createPortal 方法创建的一个特殊的 react-dom

    //      传送门的内容可以显示到 html 文档的任何位置 甚至是 react 根节点外面

     应用场景

    //      制作模态等脱离自身组件结构的内容

    // 注意:传送门的使用必须配合无状态的函数组件,class组件是无法使用的

     使用方法:

    //      1. 创建一个函数组件充当传送门组件(该函数组件不能有状态)

    //      2. 函数组件内使用 ReactDOM.createProtal 方法创建传送门

    //          ReactDOM.createPortal 函数将返回一个可以被渲染到页面的一组html元素

    //          第一个参数:要传送的 react-dom

    //          第二个参数:传送门的目标节点,最后元素将渲染到该节点

    // 传送门组件

    // 传送门组件必须是函数组件 且不能包含状态

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值