React初印象

听过哪些与React相关的名词:


dom5.png

今天主要介绍:React 18.3
以下分享源码在此

1、虚拟 DOM(Virtual DOM):

React 使用虚拟 DOM 渲染流程。


dom1.png
 <body>
  <div id="root"></div>
  <!-- 引入React.js -->
  <script crossorigin src="https://unpkg.com/react@18/umd/react.production.min.js"></script>
  <!-- 引入ReactDOM.js -->
  <script crossorigin src="https://unpkg.com/react-dom@18/umd/react-dom.production.min.js"></script>

  <script src="https://unpkg.com/babel-standalone/babel.min.js"></script>

  <script type="text/babel">

    function handleClick() {
      alert('Div element clicked!');
    }

     //React.createElement是React官方提供创建虚拟dom的一个api.
    const element = React.createElement(
      'div',
      { className: 'redBg', onClick: handleClick },
      'Item 1'
    );

//JSX语法,经过Babel编译器,转化成React.createElement()
   const element2 = (
      <div className="redBg" onClick={handleClick}>
        Item 1
      </div>
    );

    //将虚拟 DOM(React 元素)渲染到真实 DOM 的过程: 1.获取页面元素-->2.创建容器-->3.将虚拟元素渲染到容器内

    const root = ReactDOM.createRoot(document.getElementById('root'));
    root.render(element);

    const realDom = document.getElementById('realDom');

    console.log('virtualDom', element);
    console.log('realDom', realDom);

  </script>
</body>
dom3.png
虚拟dom总结:
- 本质:JS对象,相对真实dom,相对真实dom是一个轻量级的对象。

- 优点:
1、最小化真实dom操作,减少重绘(Repaint)和重排(Reflow)次数。

   重排:受DOM的布局、尺寸、位置等发生变化时影响
   重绘:受DOM的外观(如颜色、边框、背景)发生变化时影响

2、快速开发,组件复用。

- 缺点:多一份虚拟dom内存开销。

2、JSX:

JSX是一种用于描述 UI 的JavaScript 语法扩展,允许在 JavaScript 中编写类似 HTML 的代码。
编译器Babel将jsx代码编译成React.createElement。


dom4.png
  • 单行写法
const element = <h1>Hello, world!</h1>;
  • 多行写法

const element2 = (
    <div className="redBg" onClick={handleClick}>
        muplti line
      </div>
   );
  • 传参,可用插入表达式
const itemName = 'Item 2';
const element3 = (
    <div className="redBg" onClick={handleClick}>
        {itemName}
      </div>
   );
  • 复杂点的写法,多写点JS方法
const items = ['Apple', 'Banana', 'Cherry', 'Date', 'Elderberry'];
const element = (
  <div className="fruit-list">
    <h1>Fruit List</h1>
    <ul>
      {items.map((item, index) => (
        <li key={index} className="fruit-item">
          {item}
        </li>
      ))}
    </ul>
    <footer>
      <p>Total items: {items.length}</p>
    </footer>
  </div>
);
// 渲染到页面
const root = ReactDOM.createRoot(document.getElementById('root'));
root.render(element);
  • 根元素
    如何不增加额外 DOM 节点
错误写法:
const element = (
    <h1>Hello</h1>
    <p>This is a paragraph.</p>
   );
纠正写法:
const element = (
  <React.Fragment>
    <h1>Hello</h1>
    <p>This is a paragraph.</p>
  </React.Fragment>
);
简易写法:
const element = (
  <React.Fragment>
    <h1>Hello</h1>
    <p>This is a paragraph.</p>
  </React.Fragment>
);
总结:
<> ... </> = <React.Fragment>... </React.Fragment>

而在angular中,刚好相反,是在HTML 的代码编写js代码

<div>
  <p>{{ message }}</p>
  <button (click)="updateMessage()">Click me</button>
</div>

3、组件(Component):

React 的核心概念,UI 的构建块。组件可以是函数组件或类组件

  • 类组件
class ClassComponent extends Component {
    constructor(props) {
        super(props);
        // 初始化 state
        this.state = {
            count: 0,
        };
    }

    componentDidMount() {
        // 组件挂载后调用
        console.log('Component did mount');
    }

    shouldComponentUpdate(nextProps, nextState) {
        // 控制组件是否需要重新渲染
        return nextState.count !== this.state.count;
    }

    componentDidUpdate(prevProps, prevState) {
        // 组件更新后调用
        console.log('Component did update');
    }

    componentWillUnmount() {
        // 组件卸载之前调用
        console.log('Component will unmount');
    }

    render() {
        return (
            <div>
                <p>Count: {this.state.count}</p>
                <button onClick={this.handleClick}>Increment</button>
            </div>
        );
    }
}

ClassComponent.propTypes = {
    name: PropTypes.string.isRequired,
    age: PropTypes.number,
    onValueChange: PropTypes.func,
};

export default ClassComponent;
  • 函数组件
import React from 'react'
import './Style.css';
function FCComponent() {
  return (
    <div className='bg-yellow center'>这是函数组件</div>
  )
}

export default FCComponent

3、状态(State):

1.是组件内部的状态。
2.可变,通常在组件内部管理和更新。
3.用于管理组件自身的数据和状态。

  • 类组件的state
constructor(props) {
    super(props);
    // 初始化 state
    this.state = {
          count: 0,
           name: '',
           age: 0
        };
}
  • 函数组件的state
const [count, setCount] = useState(0);
const [name, setName] = useState('');
const [age, setAge] = useState(0);

4、属性(Props):

1.是组件的属性,用于从父组件传递数据和回调函数。
2.不可变,子组件不能直接修改。
3.用于将数据和功能从父组件传递到子组件。

function Welcome(props) {
  return <h1>Hello, {props.name}!</h1>;
}

5、生命周期方法(Lifecycle Methods):

类组件中特定的钩子函数,用于在组件的不同阶段执行代码,如 componentDidMount、componentDidUpdate 和 componentWillUnmount。

class ComponentA extends Component {
  constructor(props) {
    super(props);
    this.state = {
      // 初始化 state
    };
  }

  static getDerivedStateFromProps(nextProps, prevState) {
    // 用于根据新的 props 更新 state
    return null;
  }

  componentDidMount() {
    // 组件挂载后调用
  }

  shouldComponentUpdate(nextProps, nextState) {
    // 控制组件是否需要重新渲染
    return true;
  }

  static getSnapshotBeforeUpdate(prevProps, prevState) {
    // 在更新之前获取快照
    return null;
  }

  componentDidUpdate(prevProps, prevState, snapshot) {
    // 组件更新后调用
  }

  componentWillUnmount() {
    // 组件卸载之前调用
  }

  render() {
    return (
      <div>
        {/* 组件内容 */}
      </div>
    );
  }
}
ComponentA .propTypes = {
  // 定义 prop 类型
};

export default Second;

6、Hooks:

React 16.8 引入的特性,允许在函数组件中使用状态和其他 React 特性,如 useState 和 useEffect。


dom6.png

7、组件通讯:

dom7.png
pubsub使用:
安装
(1) npm install pubsub-js --save
导入
(2) import PubSub from "pubsub-js"

class Publisher extends React.Component {
    constructor(props) {
      super(props);
      this.state = {
        num: 0
      };
    }
    componentDidMount() {  
      // 假设我们在组件挂载后发布一个事件
      setInterval(() => {
        PubSub.publish('someChannel', { message: 'Hello from Publisher!'+ this.state.num }); 
        this.setState({
          num: this.state.num + 1
        });
  
      }, 1000);
    } 
    
    render() {  
      return <div>Publisher Component</div>;  
    }  
  }  
    
  class Subscriber extends React.Component {  
    componentDidMount() {  
      // 订阅事件  
      this.token = PubSub.subscribe('someChannel', (msg, data) => {  
        console.log(data.message, msg); // 输出: Hello from Publisher!  
      });  
    }  
    
    componentWillUnmount() {  
      // 组件卸载时取消订阅,防止内存泄漏  
      PubSub.unsubscribe(this.token);
    }  
    
    render() {  
      return <div>Subscriber Component</div>;  
    }  
  }  

8、上下文(Context):

用于在组件树中传递数据,而不需要通过每层组件手动传递 props

import React, { createContext, Component } from 'react';

// 创建一个 Context 对象
const MyContext = createContext();

// 创建一个提供 Context 的组件
class MyProvider extends Component {
    state = {
        user: {
            name: 'Alice',
            age: 30
        }
    };

    setUser = (name, age) => {
        this.setState({
            user: { name, age }
        });
    };

    render() {
        return (
            <MyContext.Provider value={{ 
                user: this.state.user,
                setUser: this.setUser
            }}>
                {this.props.children}
            </MyContext.Provider>
        );
    }
}
import React, { Component } from 'react';

// 创建一个消费 Context 的组件
class ChildComponent extends Component {
    static contextType = MyContext;

    handleChangeUser = () => {
        const { setUser } = this.context;
        setUser('Bob', 25);
    };

    render() {
        const { user } = this.context;
        return (
            <div>
                <p>User Name: {user.name}</p>
                <p>User Age: {user.age}</p>
                <button onClick={this.handleChangeUser}>Change User</button>
            </div>
        );
    }
}

// 创建一个消费 Context 的组件
class Child2Component extends Component {
    static contextType = MyContext;

    handleChangeUser = () => {
        const { setUser } = this.context;
        setUser('Bob--22', 250);
    };

    render() {
        const { user } = this.context;
        return (
            <div>
                <p>User Name: {user.name}</p>
                <p>User Age: {user.age}</p>
                <button onClick={this.handleChangeUser}>Change User</button>
            </div>
        );
    }
}

// 创建一个顶层父组件
class ParentContextComponent extends Component {
    render() {
        return (
            <MyProvider>
                <div className='center'>
                    <h1>上下文Context Component</h1>
                    <ChildComponent />
                    <br/>
                    <Child2Component />
                </div>
            </MyProvider>
        );
    }
}

export default ParentContextComponent;

9、Redux:

一个流行的状态管理库,常与 React 一起使用,帮助管理应用的全局状态。
由于内容比较多,下次再专门做个分享会。

10、插槽:

先来看一段angular的代码

先定义一个包含插槽的组件container.component:

<div class="parent">
  <h1>Parent Component</h1>
  <!-- 第一个插槽 -->
  <ng-content select="[header]"></ng-content>
  <hr/>
  <!-- 第二个插槽 -->
  <ng-content select="[body]"></ng-content>
  <hr/>
  <!-- 第三个插槽 -->
  <ng-content select="[footer]"></ng-content>
</div>

调用container.component,插入需要不同位置的代码

<!-- app.component.html -->
<app-parent>
  <div header>
    <h2>This is the Header Content</h2>
  </div>
  <div body>
    <p>This is the Body Content</p>
  </div>
  <div footer>
    <p>This is the Footer Content</p>
  </div>
</app-parent>
下面再看一下React的代码

先定义一个包含插槽的组件container.component:

import React, { Component } from 'react';

class ParentComponent extends Component {
  render() {
    const { header, body, footer } = this.props;
    return (
      <div className="parent">
        <h1>Parent Component</h1>
        {/* 渲染不同的插槽内容 */}
        <div className="header">{header}</div>
        <hr/>
        <div className="body">{body}</div>
        <hr/>
        <div className="footer">{footer}</div>
      </div>
    );
  }
}

export default ParentComponent;

将内容作为 props 传递给 ParentComponent 的不同插槽

import React, { Component } from 'react';
import ParentComponent from './ParentComponent';

class App extends Component {
  render() {
    return (
      <ParentComponent
        header={<h2>This is the Header Content</h2>}
        body={<p>This is the Body Content</p>}
        footer={<p>This is the Footer Content</p>}
      />
    );
  }
}

export default App;

以上是本次分享,谢谢大家。

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 206,602评论 6 481
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 88,442评论 2 382
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 152,878评论 0 344
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 55,306评论 1 279
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 64,330评论 5 373
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 49,071评论 1 285
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 38,382评论 3 400
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 37,006评论 0 259
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 43,512评论 1 300
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 35,965评论 2 325
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 38,094评论 1 333
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,732评论 4 323
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 39,283评论 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 30,286评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,512评论 1 262
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 45,536评论 2 354
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 42,828评论 2 345

推荐阅读更多精彩内容