Skip to content

1. React组件命名推荐的方式是哪个?

通过引用而不是使用来命名组件displayName。

使用displayName命名组件:

jsx
export default React.createClass({  displayName: 'TodoApp',  // ...})

React推荐的方法:

jsx
export default class TodoApp extends React.Component {  // ...}

2. react18 最新版本解决了什么问题,增加了哪些东西

React 最新版本(主要指 React 18)在性能、用户体验和开发者体验等方面做了不少改进和扩展,主要包括以下几个方面:

  1. 并发特性(Concurrent Features)

    • 自动批量更新(Automatic Batching):React 18 自动将多个状态更新合并为一次渲染,从而减少不必要的重渲染,提升性能。
    • 并发渲染(Concurrent Rendering):引入并发模式,让 React 能够在后台准备更新,保证界面响应更加流畅,即使在处理大量任务时也能维持高响应性。
  2. 过渡(Transitions)和新 Hook

    • useTransition:允许开发者将某些更新标记为“过渡更新”,这些更新可以被延迟,不会阻塞用户交互。
    • useDeferredValue:用于延迟某些状态值的更新,从而减少不必要的渲染,提升用户体验。
  3. 改进的 Suspense 和服务端渲染(SSR)支持

    • Suspense for Data Fetching:进一步完善 Suspense 的数据获取支持,使得异步数据加载场景(如组件级数据获取)更加直观和高效。
    • Streaming SSR:React 18 支持流式服务端渲染(Streaming SSR),可以边生成边发送 HTML,提高首屏加载速度和性能。
  4. 全新的 Root API

    • React 18 推出了新的 createRoot API,取代了旧的 ReactDOM.render,为并发模式和其他新特性奠定了基础,同时提供了更好的错误边界处理和更新机制。
  5. 其他改进

    • useId:用于生成唯一 ID,方便在无障碍(a11y)和组件实例中避免 ID 冲突。
    • 性能和调试工具的改进:进一步提升调试体验,同时使得开发者可以更好地跟踪和分析应用的性能瓶颈。

3. 对 React 和 Vue 的理解,它们的异同

相似之处:

  • 都将注意力集中保持在核心库,而将其他功能如路由和全局状态管理交给相关的库
  • 都有自己的构建工具,能让你得到一个根据最佳实践设置的项目模板。
  • 都使用了Virtual DOM(虚拟DOM)提高重绘性能
  • 都有props的概念,允许组件间的数据传递
  • 都鼓励组件化应用,将应用分拆成一个个功能明确的模块,提高复用性

不同之处:

1)数据流

  • Vue默认支持数据双向绑定,
  • 而React一直提倡单向数据流

2)虚拟DOM Vue2.x开始引入"Virtual DOM",消除了和React在这方面的差异,但是在具体的细节还是有各自的特点。

  • Vue宣称可以更快地计算出Virtual DOM的差异,这是由于它在渲染过程中,会跟踪每一个组件的依赖关系,不需要重新渲染整个组件树。
  • 对于React而言,每当应用的状态被改变时,全部子组件都会重新渲染。当然,这可以通过 PureComponent/shouldComponentUpdate这个生命周期方法来进行控制,但Vue将此视为默认的优化。

3)组件化 React与Vue最大的不同是模板的编写。

  • Vue鼓励写近似常规HTML的模板。写起来很接近标准 HTML元素,只是多了一些属性。
  • React推荐你所有的模板通用JavaScript的语法扩展——JSX书写。

具体来讲:React中render函数是支持闭包特性的,所以我们import的组件在render中可以直接调用。但是在Vue中,由于模板中使用的数据都必须挂在 this 上进行一次中转,所以 import 完组件之后,还需要在 components 中再声明下。

4)监听数据变化的实现原理不同

  • Vue 通过 getter/setter 以及一些函数的劫持,能精确知道数据变化,不需要特别的优化就能达到很好的性能
  • React 默认是通过比较引用的方式进行的,如果不优化(PureComponent/shouldComponentUpdate)可能导致大量不必要的vDOM的重新渲染。这是因为 Vue 使用的是可变数据,而React更强调数据的不可变。

5)高阶组件 react可以通过高阶组件(Higher Order Components-- HOC)来扩展,而vue需要通过mixins来扩展。

原因高阶组件就是高阶函数,而React的组件本身就是纯粹的函数,所以高阶函数对React来说易如反掌。相反Vue.js使用HTML模板创建视图组件,这时模板无法有效的编译,因此Vue不采用HOC来实现。

6)构建工具 两者都有自己的构建工具

  • React ==> Create React APP
  • Vue ==> Vite

7)跨平台

  • React ==> React Native
  • Vue ==> Weex

4. 可以使用TypeScript写React应用吗?怎么操作?

可以,完全可以使用 TypeScript 来开发 React 应用。TypeScript 能够为你的代码提供类型检查、自动补全和更好的维护性,特别适合大型项目。下面是一些常见的操作方式和步骤:


1. 使用 Create React App 创建 TypeScript 项目

你可以使用 Create React App (CRA) 提供的 TypeScript 模板,快速创建一个包含 TypeScript 配置的 React 应用:

bash
npx create-react-app my-app --template typescript
# 或者使用 Yarn
yarn create react-app my-app --template typescript

这会生成一个包含 .tsx.ts 文件的项目,并且预先配置好 tsconfig.json。


2. 在已有的 React 项目中添加 TypeScript

如果你有一个已有的 React 项目,可以按以下步骤添加 TypeScript 支持:

  1. 安装依赖
    安装 TypeScript 以及 React 的类型定义:
bash
npm install --save typescript @types/react @types/react-dom
# 或使用 Yarn
yarn add typescript @types/react @types/react-dom
  1. 添加 tsconfig.json
    在项目根目录创建一个 tsconfig.json 文件,配置你的 TypeScript 选项。一个简单的配置如下:
json
{
  "compilerOptions": {
    "target": "es5",
    "lib": ["dom", "dom.iterable", "esnext"],
    "allowJs": true,
    "skipLibCheck": true,
    "esModuleInterop": true,
    "allowSyntheticDefaultImports": true,
    "strict": true,
    "forceConsistentCasingInFileNames": true,
    "module": "esnext",
    "moduleResolution": "node",
    "resolveJsonModule": true,
    "isolatedModules": true,
    "noEmit": true,
    "jsx": "react-jsx"
  },
  "include": ["src"]
}
  1. 重命名文件
    .js.jsx 文件重命名为 .ts.tsx。一般来说,包含 JSX 的文件需要使用 .tsx 扩展名。

  2. 修复类型错误
    启动项目后,TypeScript 可能会检测到一些类型错误,你可以根据提示进行调整,逐步完善类型定义。


3. 使用 Next.js、Vite 等工具

除了 CRA,你还可以使用 Next.js 或 Vite 来搭建 React + TypeScript 项目,它们同样提供了开箱即用的 TypeScript 支持。

  • Next.js
    使用以下命令创建一个 Next.js + TypeScript 项目:
bash
npx create-next-app@latest --typescript
  • Vite
    使用 Vite 创建 React + TypeScript 项目:
bash
npm create vite@latest my-app -- --template react-ts

4. 类型定义和最佳实践

  • 组件类型
    使用 React.FC(或 FC) 定义函数组件的类型,或者直接指定 props 的类型:
tsx
import React from 'react';

interface MyComponentProps {
  title: string;
}

const MyComponent: React.FC<MyComponentProps> = ({ title }) => {
  return <h1>{title}</h1>;
};

export default MyComponent;
  • Hooks 类型
    当使用自定义 Hook 时,可以为返回值和参数定义具体类型,确保类型安全。

  • 第三方库的类型定义
    如果使用的第三方库没有内置类型定义,可以安装相关的 @types/ 包,例如 @types/react-router-dom

5. React 设计思路,它的理念是什么?

React 的设计思路和理念主要围绕以下几个核心原则:

  1. 声明式编程

    • React 鼓励开发者用声明式的方式来描述 UI。你只需声明“当状态为某个值时,界面应该是什么样的”,而不是手动管理 DOM 更新。这种方式可以简化代码逻辑,提高可读性和维护性。
  2. 组件化

    • React 强调将 UI 拆分为独立、可复用的组件。每个组件管理自己的状态和渲染逻辑,通过组合组件来构建复杂的界面。组件化不仅提升了代码的复用性,还让开发、测试和维护变得更容易。
  3. 单向数据流(Unidirectional Data Flow)

    • 数据在 React 应用中是自上而下流动的。父组件通过 props 向子组件传递数据,子组件不能直接修改父组件的状态。单向数据流让数据的变化更加可控,状态管理和调试也更简单。
  4. 虚拟 DOM(Virtual DOM)

    • React 使用虚拟 DOM 作为 UI 的中间抽象层,通过对比新旧虚拟 DOM 之间的差异来高效地更新真实 DOM。这样可以减少不必要的 DOM 操作,提升性能,同时使得跨平台渲染变得更容易。
  5. 函数式编程思想

    • React 的组件和状态更新方式鼓励使用纯函数和不可变数据。通过这种方式,组件的渲染和更新变得可预测,更容易调试和维护。尤其在引入 Hooks 后,函数式编程风格得到了进一步强化。
  6. 灵活与渐进式采用

    • React 设计为库而非框架,它只关注 UI 层,易于与其他库或现有项目集成。开发者可以根据项目需要,逐步引入状态管理、路由、数据请求等功能,保持技术栈的灵活性。

总结

React 的设计理念是:

  • 声明式和组件化:用简洁、可组合的方式构建 UI。
  • 单向数据流和不可变性:确保数据更新过程可控、易于调试。
  • 虚拟 DOM 优化:提高性能,简化跨平台渲染。
  • 函数式编程:强调纯函数和状态管理的可预测性。

这些理念使得 React 成为一个灵活、高效且易于维护的 UI 构建库,适用于各种规模的应用开发。

6. React中props.children和React.Children的区别

在 React 中,props.childrenReact.Children 虽然都与组件的子元素相关,但它们的作用和用途有所不同:

  1. props.children
    • 定义
      它是组件接收的子节点,通过组件的 props 自动传入。
    • 用途
      用于直接在组件中访问、渲染或操作传入的子元素。
    • 示例
jsx
function MyComponent({ children }) {
  return <div className="wrapper">{children}</div>;
}

<MyComponent> 时,所有在其标签内部的元素都会作为 props.children 传入。

  1. React.Children
  • 定义
    它是 React 提供的一个实用工具对象,包含了若干方法,用于对 props.children(可能是数组、对象或者单个元素)进行遍历、映射、计数和转换。
  • 常用方法
    • React.Children.map(children, function):对所有子元素进行遍历,并返回一个新的数组。
    • React.Children.forEach(children, function):对所有子元素进行遍历,但不返回新数组。
    • React.Children.count(children):返回子元素的数量。
    • React.Children.toArray(children):将 props.children 转换成一个扁平数组,并自动添加 key。
  • 用途
    当你需要对传入的子元素进行复杂操作或确保子元素始终以数组形式处理时,React.Children 提供了一些标准化方法,避免直接对 props.children 进行类型判断或遍历时遇到的问题。

总结

  • props.children
    直接表示传入组件的子节点,可以是单个元素、数组或其他数据类型。

  • React.Children
    一组辅助工具方法,用于对 props.children 进行安全、统一的遍历和操作,解决了直接处理 props.children 时可能遇到的类型不一致问题。

这种设计使得我们在开发中既可以方便地访问传入的子元素,也可以利用 React.Children 提供的方法来高效、安全地处理这些子元素。

7. React的状态提升是什么?使用场景有哪些?

状态提升(Lifting State Up)是指将多个组件共享的状态从它们各自的内部“提取”到它们的最近公共父组件中,由这个父组件来管理状态,并通过 props 将状态和修改状态的回调函数传递给子组件。这样做可以避免状态在多个组件中各自维护而产生不一致的问题,从而实现数据的统一管理和同步更新。

使用状态提升的场景

  1. 兄弟组件共享数据
    当多个兄弟组件需要依赖同一份数据时,可以将数据提升到它们共同的父组件中。例如,一个组件用来输入数据,另一个组件显示输入的结果。

  2. 表单数据的集中管理
    在一个包含多个输入控件的表单中,各个输入控件可能需要共享或相互影响数据。将这些数据提升到表单的父组件,可以统一管理表单状态,便于验证、提交和重置。

  3. 协调组件间的交互
    当组件之间存在交互需求,比如一个组件的操作需要影响另一个组件的展示内容,将状态提升到它们的公共父组件后,父组件可以通过传递回调函数来协调这些交互逻辑。

  4. 避免重复状态
    如果多个组件独立管理相同的数据,容易造成数据不一致。通过状态提升,将状态集中在父组件中,从而避免数据冗余和同步问题。

8. 在React中页面重新加载时怎样保留数据?

这个问题就设计到了数据持久化, 主要的实现方式有以下几种:

  • Redux: 将页面的数据存储在redux中,在重新加载页面时,获取Redux中的数据;
  • data.js: 使用webpack构建的项目,可以建一个文件,data.js,将数据保存data.js中,跳转页面后获取;
  • sessionStorge: 在进入选择地址页面之前,componentWillUnMount的时候,将数据存储到sessionStorage中,每次进入页面判断sessionStorage中有没有存储的那个值,有,则读取渲染数据;没有,则说明数据是初始化的状态。返回或进入除了选择地址以外的页面,清掉存储的sessionStorage,保证下次进入是初始化的数据
  • history API: History API 的 pushState 函数可以给历史记录关联一个任意的可序列化 state,所以可以在路由 push 的时候将当前页面的一些信息存到 state 中,下次返回到这个页面的时候就能从 state 里面取出离开前的数据重新渲染。react-router 直接可以支持。这个方法适合一些需要临时存储的场景。

9. 同时引用这三个库react.js、react-dom.js和babel.js它们都有什么作用?

  • react:包含react所必须的核心代码
  • react-dom:react渲染在不同平台所需要的核心代码
  • babel:将jsx转换成React代码的工具

10. React必须使用JSX吗?

React 并不强制要求使用 JSX。当不想在构建环境中配置有关 JSX 编译时,不在 React 中使用 JSX 会更加方便。

每个 JSX 元素只是调用 React.createElement(component, props, ...children) 的语法糖。因此,使用 JSX 可以完成的任何事情都可以通过纯 JavaScript 完成。

例如,用 JSX 编写的代码:

jsx
class Hello extends React.Component {
  render() {
    return <div>Hello {this.props.toWhat}</div>;
  }
}
ReactDOM.render(
  <Hello toWhat="World" />,
  document.getElementById('root')
);

可以编写为不使用 JSX 的代码:

js
class Hello extends React.Component {
  render() {
    return React.createElement('div', null, `Hello ${this.props.toWhat}`);
  }
}
ReactDOM.render(
  React.createElement(Hello, {toWhat: 'World'}, null),
  document.getElementById('root')
);

11. 为什么使用jsx的组件中没有看到使用react却需要引入react?

本质上来说JSX是React.createElement(component, props, ...children)方法的语法糖。在React 17之前,如果使用了JSX,其实就是在使用React, babel 会把组件转换为 CreateElement 形式。在React 17之后,就不再需要引入,因为 babel 已经可以帮我们自动引入react。

12. 在React中怎么使用async/await?

async/await是ES7标准中的新特性。如果是使用React官方的脚手架创建的项目,就可以直接使用。如果是在自己搭建的webpack配置的项目中使用,可能会遇到 regeneratorRuntime is not defined 的异常错误。那么我们就需要引入babel,并在babel中配置使用async/await。可以利用babel的 transform-async-to-module-method 插件来转换其成为浏览器支持的语法,虽然没有性能的提升,但对于代码编写体验要更好。

13. 对React SSR的理解

服务端渲染是数据与模版组成的html,即 HTML = 数据 + 模版。将组件或页面通过服务器生成html字符串,再发送到浏览器,最后将静态标记"混合"为客户端上完全交互的应用程序。页面没使用服务渲染,当请求页面时,返回的body里为空,之后执行js将html结构注入到body里,结合css显示出来;

SSR的优势:

  • 对SEO友好
  • 所有的模版、图片等资源都存在服务器端
  • 一个html返回所有数据
  • 减少HTTP请求
  • 响应快、用户体验好、首屏渲染快

1)更利于SEO

不同爬虫工作原理类似,只会爬取源码,不会执行网站的任何脚本。使用了React或者其它MVVM框架之后,页面大多数DOM元素都是在客户端根据js动态生成,可供爬虫抓取分析的内容大大减少。另外,浏览器爬虫不会等待我们的数据完成之后再去抓取页面数据。服务端渲染返回给客户端的是已经获取了异步数据并执行JavaScript脚本的最终HTML,网络爬中就可以抓取到完整页面的信息。

2)更利于首屏渲染 首屏的渲染是node发送过来的html字符串,并不依赖于js文件了,这就会使用户更快的看到页面的内容。尤其是针对大型单页应用,打包后文件体积比较大,普通客户端渲染加载所有所需文件时间较长,首页就会有一个很长的白屏等待时间。

SSR的局限:

1)服务端压力较大 本来是通过客户端完成渲染,现在统一到服务端node服务去做。尤其是高并发访问的情况,会大量占用服务端CPU资源;

2)开发条件受限 在服务端渲染中,只会执行到componentDidMount之前的生命周期钩子,因此项目引用的第三方的库也不可用其它生命周期钩子,这对引用库的选择产生了很大的限制;

3)学习成本相对较高 除了对webpack、MVVM框架要熟悉,还需要掌握node、 Koa2等相关技术。相对于客户端渲染,项目构建、部署过程更加复杂。

时间耗时比较:

1)数据请求

由服务端请求首屏数据,而不是客户端请求首屏数据,这是"快"的一个主要原因。服务端在内网进行请求,数据响应速度快。客户端在不同网络环境进行数据请求,且外网http请求开销大,导致时间差

  • 客户端数据请求 51612599334_.pic.jpg

  • 服务端数据请求 61612599340_.pic.jpg

2)html渲染

服务端渲染是先向后端服务器请求数据,然后生成完整首屏 html返回给浏览器;而客户端渲染是等js代码下载、加载、解析完成后再请求数据渲染,等待的过程页面是什么都没有的,就是用户看到的白屏。就是服务端渲染不需要等待js代码下载完成并请求数据,就可以返回一个已有完整数据的首屏页面。

  • 非ssr html渲染

31612599269_.pic.jpg

  • ssr html渲染

41612599275_.pic.jpg

14. 为什么 React 要用 JSX?

JSX 是一个 JavaScript 的语法扩展,或者说是一个类似于 XML 的 ECMAScript 语法扩展。它本身没有太多的语法定义,也不期望引入更多的标准。

其实 React 本身并不强制使用 JSX。在没有 JSX 的时候,React 实现一个组件依赖于使用 React.createElement 函数。代码如下:

js
class Hello extends React.Component {
  render() {
    return React.createElement(
        'div',
        null, 
        `Hello ${this.props.toWhat}`
      );
  }
}
ReactDOM.render(
  React.createElement(Hello, {toWhat: 'World'}, null),
  document.getElementById('root')
);

而 JSX 更像是一种语法糖,通过类似 XML 的描述方式,描写函数对象。在采用 JSX 之后,这段代码会这样写:

jsx
class Hello extends React.Component {
  render() {
    return <div>Hello {this.props.toWhat}</div>;
  }
}
ReactDOM.render(
  <Hello toWhat="World" />,
  document.getElementById('root')
);

通过对比,可以清晰地发现,代码变得更为简洁,而且代码结构层次更为清晰。

因为 React 需要将组件转化为虚拟 DOM 树,所以在编写代码时,实际上是在手写一棵结构树。而XML 在树结构的描述上天生具有可读性强的优势。

但这样可读性强的代码仅仅是给写程序的同学看的,实际上在运行的时候,会使用 Babel 插件将 JSX 语法的代码还原为 React.createElement 的代码。

总结: JSX 是一个 JavaScript 的语法扩展,结构类似 XML。JSX 主要用于声明 React 元素,但 React 中并不强制使用 JSX。即使使用了 JSX,也会在构建过程中,通过 Babel 插件编译为 React.createElement。所以 JSX 更像是 React.createElement 的一种语法糖。

React 团队并不想引入 JavaScript 本身以外的开发体系。而是希望通过合理的关注点分离保持组件开发的纯粹性。

15. React 中的高阶组件运用了什么设计模式?

使用了装饰模式,高阶组件的运用:

js
function withWindowWidth(BaseComponent) {
  class DerivedClass extends React.Component {
    state = {
      windowWidth: window.innerWidth,
    }
    onResize = () => {
      this.setState({
        windowWidth: window.innerWidth,
      })
    }
    componentDidMount() {
      window.addEventListener('resize', this.onResize)
    }
    componentWillUnmount() {
      window.removeEventListener('resize', this.onResize);
    }
    render() {
      return <BaseComponent {...this.props} {...this.state}/>
    }
  }
  return DerivedClass;
}
const MyComponent = (props) => {
  return <div>Window width is: {props.windowWidth}</div>
};
export default withWindowWidth(MyComponent);

装饰模式的特点是不需要改变 被装饰对象 本身,而只是在外面套一个外壳接口。JavaScript 目前已经有了原生装饰器的提案,其用法如下:

js
@testable
   class MyTestableClass {
}

16.React19新特性

React 19 于 2024 年 12 月 5 日正式发布,带来了多项新特性和改进,旨在提升开发者体验和应用性能。以下是 React 19 的主要更新: 参考资料:

1. Actions

在 React 应用中,处理数据变更通常需要手动管理状态、错误处理和乐观更新。React 19 引入了 Actions,简化了这些操作。通过 useOptimistic Hook,可以轻松实现乐观更新。

示例:

javascript
import { useOptimistic } from 'react';

function UpdateName() {
  const [optimisticName, setOptimisticName] = useOptimistic('', async (newName) => {
    await updateNameOnServer(newName);
  });

  return (
    <div>
      <input
        value={optimisticName}
        onChange={(e) => setOptimisticName(e.target.value)}
      />
    </div>
  );
}

在上述示例中,useOptimistic 在等待服务器响应时立即更新 UI,提升用户体验。

2. 新的 use API

React 19 引入了新的 use API,用于在渲染期间读取资源,例如读取 Promise 的结果。use 可以在渲染时挂起组件,直到 Promise 解析完成。

示例:

javascript
import { use } from 'react';

function Comments({ commentsPromise }) {
  const comments = use(commentsPromise);

  return (
    <ul>
      {comments.map((comment) => (
        <li key={comment.id}>{comment.text}</li>
      ))}
    </ul>
  );
}

需要注意的是,use 不能用于在渲染期间创建的 Promise,否则会引发警告。

3. 改进的 Suspense

React 19 对 Suspense 进行了改进,支持预热挂起的组件树,提高应用性能。通过预加载数据或代码,减少用户等待时间。

4. React DOM 静态 API

新增的 React DOM 静态 API 允许在不附加事件处理程序的情况下渲染静态内容,适用于渲染大型静态列表或内容,提高渲染性能。

5. 表单状态管理

React 19 引入了 useFormStatus Hook,帮助开发者更好地控制表单状态。该 Hook 提供关于表单提交状态的信息,如是否正在提交、提交的数据等。

示例:

javascript
import { useFormStatus } from 'react-dom';

function MyForm() {
  const { pending, data } = useFormStatus();

  return (
    <form>
      {pending && <p>Submitting...</p>}
      {/* 表单内容 */}
    </form>
  );
}

6. 资源预加载支持

React 19 增加了对资源预加载的支持,包括 prefetchDNSpreconnectpreloadpreinit 等 API,优化资源加载性能,提升页面加载速度。

示例:

javascript
import { prefetchDNS, preconnect, preload, preinit } from 'react-dom';

function MyComponent() {
  prefetchDNS('https://example.com');
  preconnect('https://example.com');
  preload('https://example.com/style.css', { as: 'style' });
  preinit('https://example.com/script.js', { as: 'script' });
}

7. 更好的错误报告

React 19 优化了错误处理机制,减少错误日志的重复,提供更清晰的错误信息,并新增了 onCaughtErroronUncaughtError 等根选项,方便开发者捕获和处理错误。

8. 自定义元素支持

React 19 完全支持自定义元素,确保在客户端和服务器端渲染环境下属性的正确处理,提高与自定义元素的兼容性。

示例:

javascript
const ThemeContext = createContext('');

function App({ children }) {
  return (
    <ThemeContext.Provider value="dark">
      {children}
    </ThemeContext.Provider>
  );
}

在服务器端渲染时,React 会根据属性类型决定是将其渲染为属性还是忽略。

9. React 编译器

React 19 引入了新的 React 编译器,自动处理性能优化,使开发者能够编写更简洁、高效的代码。

总结:

React 19 的发布为开发者提供了更强大、灵活的工具,简化了数据处理、状态管理和性能优化等方面的工作。通过引入 Actions、新的 use API、改进的 Suspense 等特性,React 19 进一步提升了开发体验和应用性能。

评论区

欢迎留言、补充或勘误。

xiaoba.blog