React技术分享

147 阅读1分钟

React技术分享

前置介绍

  • 本次分享的目标:Web前端发展趋势、了解Web开发的基本概念、主流技术栈基本原理、基础开发教程。

多页面应用和单页

E[1XMJ_2B]}{$H~AIQ_{3AC.png

多页面应用(MPA)的概念和特点

  • 页面切换:多页面应用通过链接或导航等方式实现页面之间的切换。当用户点击链接或触发导航时,浏览器会向服务器发送请求,加载新的HTML页面并刷新整个页面。

  • 独立的页面:每个页面都是独立的,包含自己的HTML、CSS和JavaScript代码。它们可以拥有不同的布局、样式和功能,用于展示不同的内容或提供不同的功能模块。

  • 服务器端渲染:在多页面应用中,服务器负责渲染并提供每个页面的内容。服务器接收请求,根据请求的路径返回相应的HTML页面。这样可以使搜索引擎能够更好地抓取和索引页面内容。

  • 传统的页面加载方式:每次页面切换都需要从服务器获取新的页面,并进行完整的加载和渲染。这可能导致页面加载时间较长,用户在切换页面时可能会感受到延迟。

  • 有限的前端交互性:由于每个页面都是独立的,页面之间的交互性相对有限。在页面切换时,可能需要重新加载和初始化JavaScript代码,导致一些状态和数据的丢失。

单页面应用(SPA)的概念和特点

什么是单页面应用?

单页面应用(Single Page Application,SPA)是一种Web应用程序的架构模式。它与传统的多页面应用程序不同,单页面应用在加载初始HTML页面后,通过使用JavaScript动态地更新该页面的内容,而不是通过整个页面的刷新或重新加载来加载新的页面。

为什么会出现单页面应用概念、它有什么优势?

  • 更流畅的用户体验:

SPA通过使用JavaScript动态地更新页面内容,实现了快速响应和无需页面刷新的用户体验。用户可以无缝地与应用程序交互,避免了传统多页面应用中的页面切换延迟。

  • 更快的页面加载:在传统的多页面应用中,每次页面切换都需要重新加载整个页面,包括HTML、CSS和JavaScript等资源。而SPA只在初始加载时获取页面的基本内容和代码,后续页面切换时只需通过异步加载数据进行更新,从而减少了不必要的网络请求和页面加载时间。

  • 更接近原生应用:

SPA的交互方式类似于原生应用,因此用户可以获得更接近原生应用的体验,包括快速响应、平滑的过渡效果和流畅的动画等。

  • 减少服务器负载:

由于SPA只需要在初始加载时获取一次页面资源,之后的页面更新和数据获取通常通过AJAX技术异步进行,减轻了服务器的负载。

  • 简化前后端分离:

SPA常常与后端通过API进行数据交互,前后端可以相对独立地开发和部署。这种前后端分离的方式可以提高团队的协作效率,并允许使用不同的技术栈进行开发。

  • 更好的交互和导航控制:

SPA通过前端路由实现页面之间的导航控制,可以在不刷新页面的情况下进行页面切换和状态管理。这种方式可以提供更好的用户导航体验,同时也减少了页面加载的延迟和不连贯感。

然而,SPA也面临一些挑战:

  • SEO优化问题:

由于SPA的页面内容是通过JavaScript动态生成的,搜索引擎对于抓取和索引SPA页面的能力相对较弱。这可能对需要良好SEO的应用程序产生影响,需要采取额外的措施来优化SEO。

  • 初次加载时间较长:

尽管SPA在后续交互中可以提供快速加载速度,但初始加载时间可能较长,因为需要下载并解析初始HTML、CSS和JavaScript等资源。这可能会对用户体验产生一定的影响。

  • 内存占用较高:

SPA通常会在初始加载时将所有必需的代码和资源下载到客户端,并在运行时保留在内存中。对于较大型的SPA,这可能导致较高的内存占用,特别是在较低配置的设备上可能会有性能问题。

  • 前端路由管理复杂:

SPA通常使用前端路由来管理不同页面之间的导航和状态。对于较大型的SPA,前端路由管理可能变得复杂,需要合理地设计和维护路由结构。

实现单页面(SPA)需要用到哪些技术

虚拟DOM

虚拟DOM(Virtual DOM)是SPA中重要的概念,它是一个轻量级的JavaScript对象树,与真实的DOM元素一一对应。通过使用虚拟DOM,可以将页面渲染逻辑抽象为操作虚拟DOM对象,最终再将变化部分更新到真实的DOM中,从而提高渲染性能。

前端路由

前端路由用于管理SPA中不同页面的切换和导航。通过前端路由,可以根据URL的变化动态加载对应的组件或页面内容,实现无刷新的页面切换。常见的前端路由库包括React Router、Vue Router等。

基于哈希(hash)的路由

基于哈希的路由是一种简单且兼容性较好的路由实现方式。它通过改变URL的哈希部分来实现页面切换,不会触发浏览器的页面刷新。

在JavaScript中,可以使用location.hash来获取或设置URL的哈希部分。通过监听hashchange事件,可以检测哈希的变化。

// 设置哈希路由
location.hash = '#home'; // 设置URL的哈希部分为"#home"

// 监听哈希变化事件
window.addEventListener('hashchange', function() {
  const hash = location.hash.slice(1); // 获取不包含#的哈希部分
  // 根据哈希值切换路由
  if (hash === 'home') {
    // 显示首页内容
  } else if (hash === 'about') {
    // 显示关于页面内容
  } else {
    // 处理未匹配的路由
  }
});

基于历史记录(history)的路由

基于历史记录的路由使用浏览器提供的History API,它允许修改浏览器历史记录中的URL,并通过pushStatereplaceState方法实现页面切换,不会刷新整个页面。

// 设置历史路由
history.pushState(null, null, '/home'); // 修改URL为"/home"

// 监听浏览器前进/后退事件
window.addEventListener('popstate', function(event) {
  const path = location.pathname; // 获取当前路径
  // 根据路径切换路由
  if (path === '/home') {
    // 显示首页内容
  } else if (path === '/about') {
    // 显示关于页面内容
  } else {
    // 处理未匹配的路由
  }
});

状态管理

SPA中涉及到的数据和状态通常较为复杂,需要进行统一管理。状态管理工具(如Redux、Vuex等)可以帮助开发者在不同组件之间共享和管理状态,确保应用的一致性和可维护性。

AJAX/异步数据请求

SPA通常通过异步数据请求获取数据,并将数据渲染到页面中。AJAX技术(Asynchronous JavaScript and XML)可以实现无需刷新整个页面的异步数据请求,常见的AJAX库包括axios、fetch等。

打包工具

在SPA开发中,使用打包工具可以将多个前端资源(如HTML、CSS、JavaScript、图片等)打包为一个或多个静态文件,以减少文件大小和提高加载速度。常见的打包工具包括Webpack、Parcel、Rollup等。

真实DOM和虚拟DOM

数据结构:

真实DOM:浏览器提供的实际页面结构,由浏览器解析HTML并构建而成。它是由DOM节点组成的树形结构,表示了页面的实际层次关系。

虚拟DOM:通过JavaScript对象模拟的页面结构,是一个轻量级的、与平台无关的数据结构。它是由虚拟DOM节点(Virtual DOM Node)组成的树形结构,表示了页面的逻辑结构。

更新过程:

真实DOM:当页面状态发生变化时,真实DOM直接进行更新,触发浏览器的重排(reflow)和重绘(repaint)操作。这些操作比较耗费性能,特别是在大型或频繁更新的应用中。

虚拟DOM:通过比较新旧虚拟DOM树的差异,找出需要更新的部分,然后只对这部分进行真实DOM的更新。这种优化可以减少对真实DOM的操作,提高性能和效率。

性能:

真实DOM:由于直接操作真实DOM会触发浏览器的布局计算和渲染操作,性能开销相对较大。

虚拟DOM:通过在JavaScript层面进行比较和更新,可以减少对真实DOM的操作,从而提高性能。虽然虚拟DOM的比较过程也会带来一定的开销,但通常通过智能的差异计算算法进行优化。

开发体验:

真实DOM:直接操作真实DOM需要编写大量的DOM操作代码,涉及到元素创建、属性设置、事件绑定等操作,开发过程相对繁琐。

虚拟DOM:通过使用虚拟DOM,开发者可以使用更高级、更抽象的API来描述页面结构和更新逻辑,简化了DOM操作,提升了开发体验。


React 基本介绍

React 简介

什么是 React?

React 是一个用于构建用户界面的 JavaScript 库。它由 Facebook 开发并于2013年首次发布。React 的目标是创建可重用的 UI 组件,这些组件可以构建大型、高性能的 Web 应用程序。

React 的特点

  • 组件化:

React 的核心思想是将用户界面分解为独立的、可重用的组件。每个组件负责管理自己的状态和逻辑,并可以嵌套和组合形成复杂的界面。这种组件化的开发方式使得代码更具可维护性和可重用性。

  • 虚拟 DOM:

React 使用虚拟 DOM(Virtual DOM)来表示真实 DOM 的轻量级副本。通过对比前后两个虚拟 DOM 的差异,React 可以高效地更新实际发生变化的部分,而不需要重新渲染整个页面。这种优化使得 React 在性能方面表现出色。

  • 单向数据流:

React 推崇单向数据流的设计模式,即数据从父组件向子组件进行传递,子组件不能直接修改父组件的数据。这种数据流的设计使得状态管理更加可控和可预测,降低了出现数据混乱的可能性,也便于调试和维护。

  • 声明式:

React 使用 JSX 语法来描述用户界面,它是一种类似于 HTML 的语法扩展。通过 JSX,开发人员可以以声明式的方式编写代码,描述界面应该呈现的样子,而不必关注底层的 DOM 操作。这种声明式的编程风格使得代码更易读、易理解。

  • 生态丰富:

React 生态系统非常丰富,有大量的工具和库可以与 React 配合使用。例如,React Router 用于处理应用程序的路由,Redux 用于状态管理,Axios 用于处理网络请求等。这些工具和库可以帮助开发人员更高效地构建和管理 React 应用。

React 的核心概念

组件化开发思想(Components)

React将用户界面拆分成独立可复用的组件。组件是构建用户界面的基本单元,可以是一个按钮、一个输入框,或者更复杂的元素。每个组件都有自己的状态和属性,并且可以通过定义render方法来描述组件的外观。

// 定义一个简单的函数式组件
function Greeting(props) {
  return <h1>Hello, {props.name}!</h1>;
}

// 在父组件中使用Greeting组件
function App() {
  return (
    <div>
      <Greeting name="Alice" />
      <Greeting name="Bob" />
    </div>
  );
}

ReactDOM.render(<App />, document.getElementById('root'));

虚拟DOM(Virtual DOM)

React使用虚拟DOM来表示用户界面的状态。虚拟DOM是React自己实现的一种轻量级的DOM表示,它将用户界面表示为一个JavaScript对象树。当组件的状态发生变化时,React会通过比较前后两个虚拟DOM树的差异,并将变化的部分高效地更新到实际的DOM上,以保持界面与状态的同步。

// 创建虚拟DOM元素
const virtualElement = React.createElement('h1', { className: 'title' }, 'Hello, Virtual DOM!');

// 渲染虚拟DOM到真实DOM
ReactDOM.render(virtualElement, document.getElementById('root'));

JSX(Javascript XML)

JSX是一种JavaScript的扩展语法,它允许在JavaScript代码中直接编写类似HTML的标记语言。使用JSX,你可以在组件的render方法中描述组件的外观,并在其中嵌入JavaScript逻辑。React会将JSX转换为对应的虚拟DOM表示。

  1. 渲染HTML元素:
  const element = <h1>Hello, world!</h1>;
  ReactDOM.render(element, document.getElementById('root'));
  1. 嵌入JavaScript表达式:
const name = 'Alice';
const element = <h1>Hello, {name}!</h1>;
ReactDOM.render(element, document.getElementById('root'));

  1. 使用条件语句:
const isLoggedIn = true;
const element = (
  <div>
    {isLoggedIn ? <p>Welcome back!</p> : <p>Please log in.</p>}
  </div>
);
ReactDOM.render(element, document.getElementById('root'));

  1. 使用循环生成列表:
const numbers = [1, 2, 3, 4, 5];
const listItems = numbers.map((number) => <li>{number}</li>);
const element = <ul>{listItems}</ul>;
ReactDOM.render(element, document.getElementById('root'));

  1. 使用组件:
function Greeting(props) {
  return <h1>Hello, {props.name}!</h1>;
}

const element = <Greeting name="Alice" />;
ReactDOM.render(element, document.getElementById('root'));

单向数据流(One-Way Data Flow)

是React中的一个重要概念,它指的是数据在应用程序中的流动方向是单向的,从父组件向子组件传递数据,子组件不能直接修改父组件的数据。

在React中,数据流动的过程如下:

  1. 父组件通过props将数据传递给子组件。父组件可以将自身的状态(state)或其他数据作为属性传递给子组件。
  2. 子组件接收到父组件传递的属性(props),并根据这些属性来渲染自己的内容。
  3. 子组件可以通过调用父组件传递的回调函数来通知父组件发生了某个事件或状态的变化。
  4. 如果需要修改数据,子组件通常会调用父组件传递的回调函数,并将新的数据作为参数传递给父组件。
  5. 父组件接收到子组件传递的数据后,可以更新自身的状态,并通过重新渲染来传递更新后的数据给子组件。

这种单向数据流的设计模式使得数据的流动变得可预测和可追踪,更易于理解和调试应用程序的行为。它也有助于保持应用程序的一致性,因为只有父组件可以修改数据,子组件只负责展示和接收数据。

需要注意的是,虽然数据的流动是单向的,但并不意味着React应用程序的整个数据流都必须通过层级组件树自上而下进行。React提供了一些解决方案,如Context API和Redux等,用于在组件之间共享状态或进行更复杂的数据管理。但无论如何,单向数据流的概念仍然是React中的核心原则之一。


React Hook

  • 什么是 Hook?

    Hook 是 React 16.8 版本引入的一种特性,它是一组函数,可以让你在函数组件中使用状态和其他 React 特性。使用 Hook,你无需编写类组件,就能在函数组件中使用 React 的特性,如状态管理、生命周期方法等。

  • Hook 的背景和动机

    难以重用状态逻辑:当有多个组件需要共享某些状态逻辑时,你需要使用 render props 或高阶组件等模式来解决这个问题。这种方式会导致组件层级的嵌套增加,代码变得复杂。

    类组件的复杂性:使用类组件需要理解和管理生命周期方法,包括处理副作用、订阅和取消订阅等操作。这增加了学习成本,尤其是对于初学者而言。

常用的 Hook

  • useState Hook:管理组件的状态
  • useEffect Hook:处理副作用(如数据获取、订阅等)
  • useContext Hook:在组件树中共享状态
  • useMemo 和 useCallback Hook:性能优化
  • useRef Hook:
  • 获取 DOM 节点或保存变量
  • 自定义 Hook:封装可复用的逻辑

Hook 相比类组件的好处

  • 更简洁的代码:Hook 使组件逻辑更易读和组织
  • 更好的状态管理:使用 useState 管理组件的状态
  • 更灵活的副作用处理:使用 useEffect 处理异步操作和副作用
  • 更好的可复用性:自定义 Hook 提供了逻辑的复用和抽象

示例代码演示

类组件

import React, { Component } from 'react';

class Counter extends Component {
  constructor(props) {
    super(props);
    this.state = {
      count: 0,
    };
  }

  componentDidMount() {
    document.title = `Count: ${this.state.count}`;
  }

  componentDidUpdate() {
    document.title = `Count: ${this.state.count}`;
  }

  increment = () => {
    this.setState((prevState) => ({
      count: prevState.count + 1,
    }));
  };

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

export default Counter;

使用Hooks的函数组件

import React, { useState, useEffect } from 'react';

function Counter() {
  const [count, setCount] = useState(0);

  useEffect(() => {
    document.title = `Count: ${count}`;
  }, [count]);

  const increment = () => {
    setCount((prevCount) => prevCount + 1);
  };

  return (
    <div>
      <p>Count: {count}</p>
      <button onClick={increment}>Increment</button>
    </div>
  );
}

export default Counter;