IT数码 购物 网址 头条 软件 日历 阅读 图书馆
TxT小说阅读器
↓语音阅读,小说下载,古典文学↓
图片批量下载器
↓批量下载图片,美女图库↓
图片自动播放器
↓图片自动播放器↓
一键清除垃圾
↓轻轻一点,清除系统垃圾↓
开发: C++知识库 Java知识库 JavaScript Python PHP知识库 人工智能 区块链 大数据 移动开发 嵌入式 开发工具 数据结构与算法 开发测试 游戏开发 网络协议 系统运维
教程: HTML教程 CSS教程 JavaScript教程 Go语言教程 JQuery教程 VUE教程 VUE3教程 Bootstrap教程 SQL数据库教程 C语言教程 C++教程 Java教程 Python教程 Python3教程 C#教程
数码: 电脑 笔记本 显卡 显示器 固态硬盘 硬盘 耳机 手机 iphone vivo oppo 小米 华为 单反 装机 图拉丁
 
   -> JavaScript知识库 -> React进阶学习 -> 正文阅读

[JavaScript知识库]React进阶学习

高级指引

代码分割

  1. 打包

    打包:是一个将文件引入并合并到一个单独文件的过程,最终形成一个“bundle”,然后在页面上引入该bundle,整个应用即可一次性加载。

    打包工具:Webpack、Rollup、Browserify。

  2. 代码分割

    随着代码的增多,形成的bundle也会增大,因此对bundle进行分割,使用Webpack等打包器,来创建多个包并在运行时动态加载,可以实现“懒加载”,提高应用的性能。

  3. import()

    代码分割后,需要通过动态的import()语法来引入代码。

    代码分割和babel同时使用时,需要确保babel能解析动态import语法而不是将其转换,需要安装 @babel/plugin-syntax-dynamic-import 插件。

  4. React.lazy:React.lazy不适用于服务端渲染。

  5. 异常捕获边界

  6. 在哪个地方进行代码分割:路由。

  7. 命名导出:React.lazy目前只支持默认导出(default exports)

Context

通常的做法

在一个应用中,一个变量需要被多个不同层级的子元素都使用时,通常的做法是在每层的组件上都使用props来获取和向下传递这个值。


/* theme作为一个应用的主题,每个按钮都需要知道theme的值,然后根据theme的值作出改变 */
class App extends React.Component {
  render() {
    /* theme作为一个应用的主题,每个按钮都需要知道theme的值,然后根据theme的值作出改变 */
    // 1. 在父元素中指明theme的值
    return <Toolbar theme="dark" />;
  }
}

function Toolbar(props) {
  return (
    <div>
        // 2. 在中间层级的元素上使用props获取并传递theme值,尽管它并不需要知道
      <ThemedButton theme={props.theme} />
    </div>
  );
}

class ThemedButton extends React.Component {
  render() {
    // 3. 在子元素中获取这个theme值
    return <button>{this.props.theme}</button>;
  }
}

Context的用法

React提供一个context对象,可以在不使用props传递的情况下,让所有需要的子元素都能获取到这个值。

/* 只需创建一个context对象,然后在父元素应用它,就可以在任意的子元素中获取到这个值 */
// 1. 创建一个context对象
const ThemeContext = React.createContext("light");

class AppContext extends React.Component {
 
  render() {
    return (
        // 2. 在父元素中使用context对象,通过value属性改变这个值
      <ThemeContext.Provider value="dark">
        <ToolBar />
      </ThemeContext.Provider>
    );
  }
}

function ToolBar() {
  return (
    <div>
        // 不需要知道theme值的中间件,无任何操作
      <ThemeButton />
    </div>
  );
}

class ThemeButton extends React.Component {
  // 3. 在子元素中,取到context对象,通过this.context来获取它的value值
  // React会找到最近的theme Provider,并使用它的值
  static contextType = ThemeContext;
  render() {
    return <button>{this.context}</button>;
  }
}

Context的主要应用场景是在很多不同层级的组件需要访问同样的数据(获取它的值和改变),但是中间的组件并不需要这些内容的时候。
Context的缺点是会让组件的复用性变得很差。

Context的替代方案

另一种不使用Context的方法,是低层级需要使用数据的组件作为一个值来进行传递,中间组件就不需要去知道这些数据了。

这种方法虽然减少了要传递的props的数量,但是它是将父组件与和它密切相关的子组件分开了,组件的层级、逻辑变得更复杂了。

使用context的通用场景:管理当前的locale、theme、一些缓存数据。
这些场景使用context会比替代方案要简单很多。

API

  1. React.creatContext
const MyContext = React.createContext(defaultValue);

上面的语句创建了一个Context对象,并传入了一个defaultValue作为默认值。

当子组件使用Context对象的时候,React会找到离子组件最近的Context.Provider中的value值,如果没有找到则会使用defaultValue

  1. Context.Provider
<MyContext.Provider value="" />

每个Context对象都会返回一个Provider React组件,它允许消费组件(使用数据的组件)订阅context的变化。

Provider接收一个value值,并传递给它的消费组件,当value值发生改变时,它内部的所有消费组件都会重新渲染,且不受其他组件是否更新的影响。

这里的更新是通过新旧值的检测来实现的,使用了与Object.is相同的算法。

  1. Class.contextType
MyClass.contextType = MyContext;

组件上的contextType属性被赋值了一个context对象后,就可以在组件中通过this.context属性来获取context对象的value值了,并且可以在任意的生命周期中访问到它。

也可以通过static来声明类属性(不过它现在还是一个实验性的语法)

static contextType = MyContext;
  1. Context.Consumer
<MyContext.Consumer>
  {(value) => <button>{value}</button>}
</MyContext.Consumer>

Context.Consumer组件,可以让你在函数式组件中使用context对象。
因为函数式组件中不能添加属性,所以无法获取到context对象,所以使用.Consumer API,通过(value)=>{}的方式来使用context对象的value值。

  1. Context.displyName
MyContext.displayName = "myContext";

context对象接收一个displayName属性,可以在React devTools工具中看到当前组件的value是从哪一个context对象获取的。

动态Context

  1. 创建一个context对象
const themes = {
  light: {
    foreground: "#000000",
    background: "#eeeeee",
  },
  dark: {
    foreground: "#ffffff",
    background: "#222222",
  },
};

const ThemeContext = React.createContext(themes.dark);
  1. 一个使用context对象的button
class ThemedButton extends React.Component {
  render() {
    let props = this.props;
    let theme = this.context;
    return (
      <button {...props} style={{ backgroundColor: theme.background }}></button>
    );
  }
}
ThemedButton.contextType = ThemeContext;
  1. 一个中间件:给button添加方法
function ToolBar(props) {
  return <ThemedButton onClick={props.changeTheme}>Change Theme</ThemedButton>;
}
  1. 一个父组件:提供context对象的数据和方法
class AppDinamic extends React.Component {
  constructor(props) {
    super(props);
    this.state = {
      theme: themes.light,
    };

    this.toggleTheme = () => {
      this.setState((state) => ({
        theme: state.theme === themes.dark ? themes.light : themes.dark,
      }));
    };
  }
  render() {
    return (
      <ThemeContext.Provider value={this.state.theme}>
        <ToolBar changeTheme={this.toggleTheme} />
      </ThemeContext.Provider>
    );
  }
}

实现:点击button按钮,进行主题的切换。

多个context

当一个组件需要使用多个context时,可以将他们嵌套传递,然后在子组件中使用.Consumer组件嵌套获取。

父组件:

<ThemeContext.Provider value="dark">
    <UserContext.Provider value="Guest">
      <ToolBar />
    </UserContext.Provider>
</ThemeContext.Provider>

子组件:

 <ThemeContext.Consumer>
      {(theme) => (
        <UserContext.Consumer>
          {(user) => (
            <div>
              <button>{theme}</button>
              <button>{user}</button>
            </div>
          )}
        </UserContext.Consumer>
      )}
    </ThemeContext.Consumer>

context对象使用的注意事项

context对象的数据在Provider组件中通过value属性来传递,当value的值改变时,消费组件会重新渲染。
但是当Provider组件被重新,而value的值是一个引用数据类型的数据时,React会因为检测到value值的变化而去重新渲染消费组件。

解决这个问题的办法:将value属性的值,放在组件的state中,然后通过调用this.state来给value赋值

<MyContext.Provider value={this.state.value}>
    <Toolbar />
</MyContext.Provider>

错误边界

错误边界是一种React组件,这种组件可以捕获发生在其子组件树任何位置的JavaScript错误,并打印这些错误,同时展示降级UI,而不展示那些发生崩溃的子组件树。

错误边界组件需要自己包装。

错误边界在渲染期间、生命周期方法和整个组件树的构造函数中捕获错误。

错误边界无法捕获错误的场景:事件处理、异步代码、服务端渲染、它自身(非它的子组件)抛出的错误。

refs转发

refs转发:将ref自动地通过组件传递到其子组件。

转发refs到DOM组件

  1. 使用``React.forwardRef`方法来创建组件,传入ref参数,在button元素中进行转发。
const FancyButton = React.forwardRef((props, ref) => (
  <button ref={ref} className="FancyButton">
    {props.children}
  </button>
));
  1. 通过React.createRef()获取ref,在组件中应用ref,向button元素传入内容
const ref = React.createRef();
return <FancyButton ref={ref}>Click me!</FancyButton>;

Ref转发既可以在DOM组件上使用,也可以在class组件中使用。

Fragments

Fragments的作用类似的div(不需要但在React规范中又必须存在)。

一个简单的例子:

// 父组件
render() {
    return (
      <table>
        <tr>
          <Columns />
        </tr>
      </table>
    );
}
// 子组件
render() {
    return (
      <div>
        <td>Hello</td>
        <td>World</td>
      </div>
    );
}

最终构成的代码将会是这样:

<table>
    <tr>
        <div>
            <td>Hello<td>
            <td>World</td>
        </div>
    <tr>
</table>

但是我们知道<tr>标签中是不可以包裹<td><th>之外的其他标签的,<div>的存在是一个错误。

然而子组件的最外层又必须要有一个容器标签来进行包裹。
这种情况下,我们就可以使用Fragments

render() {
    return (
      <React.Fragment>
        <td>Hello</td>
        <td>World</td>
      </React.Fragment>
    );
}

这样运行的代码就不会报错了。

另一种简洁的写法是使用<></>(短语法)来进行包裹,也可以达到同样的效果。

短语法的缺点是,不支持key和各种属性,因此无法在一个循环中使用,而Fragments则支持key和属性的存在。

高阶组件

与第三方库协同

深入JSX

JSX:React.createElement(component, props, …children)函数的语法糖,下面介绍JSX语法使用过程中需要注意的一些问题和规范。

  1. 要求React必须在作用域内
<myButton color="blue" shadowSize={2}>
    Click Me
</myButton>

在上面的JSX语句中,我们首先指定了React元素。
它要求React必须在作用域内,因为JSX语句会被编译为React.createElement的形式。

  1. JSX语法中可以通过点语法使用组件

创建MyComponents变量:

const MyComponents = {
  DataPicker: function DataPicker(props) {
    return <div>Imagine a {props.color} datapicker here.</div>;
  },
};

通过点语法使用MyComponents变量下的DataPicker组件:

return <MyComponents.DataPicker color="blue"></MyComponents.DataPicker>;
  1. 用户定义的组件必须以大写字母开头

如果我们创建了一个不以大写字母开头的组件:

function hello(props) {
  return <div>Hello {props.toWhat}</div>;
}

那么我们在使用它时,将会这么引入:

<hello toWhat="World"></hello>

因为标签以小写字母开头,它会被React识别为HTML元素(例如<div>),而不能实现想要的效果。

  1. JSX类型不能是一个表达式

如果JSX组件需要通过一个表达式来获取,不可以直接将表达式作为JSX写在代码里,你需要先将表达式的值赋给一个大写字母开头的变量,然后再使用。

错误写法:

<components[props.storyType] story={props.story} />;

正确写法:

const SpecificStory = components[props.storyType];
return <SpecificStory story={props.story} />;
  1. JSX中传递的props可以是一个JavaScript表达式
<ComponentOne foo={1 + 2 + 3 + 4}></ComponentOne>

但是像if、for循环这样不属于表达式的内容不可以在prop的{}中使用。

  1. Props的默认值为true

当你没有给props赋值时,它的默认值为true。

<MyComponent autocomplete /> <==> <MyComponent autocomplete={true} />
  1. props可以使用扩展运算符

可以通过{…props}来给所有prop赋值:

const props = { firstName: "Ben", lastName: "Hector" };

<Greeting {...props} />

也可以单独把某一个值提取出来:

const Button = (props) => {
  const { kind, ...other } = props;
  const className = kind === "primary" ? "PrimaryButton" : "SecondaryButton";
  return <button {...other}>{className}</button>;
};

<Button kind="primary" onClick={() => console.log("clicked")}></Button>
  1. JSX中的子元素

在JSX语法中对于当前未知的子元素,可以使用props.children来进行占位。

这个子元素可以是:字符串字面量、组件、JS表达式、函数等等。
只要确保这个子元素在渲染之前能被转换成React可以理解的对象即可。

但是有一些内容作为子元素时,会被JSX忽略掉:布尔类型、null、undefined。
如果想要渲染这些值,需要先将他们转为字符串。

以下代码渲染出来的结果是空的:

<p>{undefined}</p>

以下代码会有内容被渲染:

<p>undefined</p>

<p>{String(undefined)}</p>

性能优化

Portals

Portals一般用法

ReactDOM.createPortal(this.props.children,this.el);

使用一个state来控制子元素(this.props.children)的显示与出现。

由于子元素是一个组件,所以可以通过生命周期来控制子元素是否被挂载到this.el上,这个官方案例会是一个很好的例子。

适用场景:对话框、悬浮卡、提示框等。

通过Portals进行事件冒泡

Refs 与DOM

Refs:通过它我们能访问DOM节点,或在render方法中创建React元素。

适合使refs的情况:

  • 管理焦点、文本选择或媒体播放
  • 触发强制动画
  • 集成第三方DOM库

【注】避免使用refs来做任何可以通过声明式方式来实现的事情。

  1. 创建ref
this.myRef = React.createRef();
  1. 使用ref
<input type="text" ref={this.myRef}></input>
  1. 访问ref
const ele = this.myRef.current;

通过this.myRef.current可以访问到使用了该ref的元素,即第2步中的input元素。

React会在组件挂载时给current属性传入DOM元素,并在组件卸载时传入null值。

【注】这里是在class组件中使用ref的方法,并不适用与函数组件,函数组件中ref的使用在React新增的Hook内容中进行介绍。

回调Refs

与上面在创建ref后再使用不同,回调ref通过函数传入的参数来访问到ref元素,然后使用:

先创建ref元素并设为null,然后创建获取ref元素的函数和使用ref元素的函数:

constructor(props) {
    super(props);

    this.textInput = null;

    this.setTextInputRef = (element) => {
      this.textInput = element;
    };

    this.focusTextInput = () => {
      // 使用原生DOM API 使text输入框上获得焦点
      if (this.textInput) this.textInput.focus();
    };
}

然后通过ref={this.setTextInputRef}onClick={this.focusTextInput}来使用ref。

React会在组件挂载时,调用ref回调函数并传入DOM元素,当卸载时调用它并传入null。

Render Props

render props:在React组件之间使用一个值为函数的prop共享代码的简单技术。

<MyComponent render={data => (
    <h1>Hello {data.target}</h1>
)} />

TypeScript

TypeScript是一个JavaScript的类型超集,包含独立的编译器,可以在构建时发现bug和错误。

严格模式

使用PropTypes进行类型检查

创建一个子组件:

class Greeting extends React.Component {
  render() {
    return <h1>Hello, {this.props.name}</h1>;
  }
}

然后在父组件中使用它:

render() {
    return <Greeting name={true} />;
}

在不使用类型检查的情况下,正常输出,结果为:Hello,

给子组件加上类型检查:

import PropTypes from "prop-types";

Greeting.propTypes = {
  name: PropTypes.string,
};

这时再运行就会产生警告。
类型检查要求name的类型是字符串,但是我们传入的却是布尔类型的。

将父组件修改为:

render() {
    return <Greeting name={"true"} />;
}

传入name的值是字符串类型,则不会产生警告。

PropTypes的类型

PropTypes的类型可以是:array、bool、func、number、object、string、symbol、node、element、elementType、any。

- node:任何可被渲染的元素
- element:一个React元素
- elementType:一个React元素类型

PropTypes.类型后加上.isRequired,表示这个值是必须的,如果没有被提供,则会产生警告。

还有其他更复杂的使用:

// 指定的值其中一个
PropTypes.oneOf(['News','Photos'])
// 指定的类型其中一个
PropTypes.oneOfType([
    PropTypes.string,
    PropTypes.number,
    PropTypes.instanceOf(Message)
])
// 指定数据类型的数组
PropTypes.arrayOf(PropTypes.number)

// 指定数据类型的对象
PropTypes.objectOf(PropTypes.number)
// 指定对象由特定类型值组成
PropTypes.shape({
    color: PropTypes.string,
    fontSize: PropTypes.number
})

默认Props值

可以使用defaultProps属性来个组件的prop设置默认值。

Greeting.defaultProps = {
    name: 'Stranger'
}

PropTypes在函数组件中的使用

function HelloWorldComponent({ name }) {
  return <p>Hello, {name}</p>;
}

HelloWorldComponent.propTypes = {
  name: PropTypes.string,
};
  JavaScript知识库 最新文章
ES6的相关知识点
react 函数式组件 & react其他一些总结
Vue基础超详细
前端JS也可以连点成线(Vue中运用 AntVG6)
Vue事件处理的基本使用
Vue后台项目的记录 (一)
前后端分离vue跨域,devServer配置proxy代理
TypeScript
初识vuex
vue项目安装包指令收集
上一篇文章      下一篇文章      查看所有文章
加:2022-02-01 20:30:57  更:2022-02-01 20:32:12 
 
开发: C++知识库 Java知识库 JavaScript Python PHP知识库 人工智能 区块链 大数据 移动开发 嵌入式 开发工具 数据结构与算法 开发测试 游戏开发 网络协议 系统运维
教程: HTML教程 CSS教程 JavaScript教程 Go语言教程 JQuery教程 VUE教程 VUE3教程 Bootstrap教程 SQL数据库教程 C语言教程 C++教程 Java教程 Python教程 Python3教程 C#教程
数码: 电脑 笔记本 显卡 显示器 固态硬盘 硬盘 耳机 手机 iphone vivo oppo 小米 华为 单反 装机 图拉丁

360图书馆 购物 三丰科技 阅读网 日历 万年历 2024年11日历 -2024/11/24 11:46:16-

图片自动播放器
↓图片自动播放器↓
TxT小说阅读器
↓语音阅读,小说下载,古典文学↓
一键清除垃圾
↓轻轻一点,清除系统垃圾↓
图片批量下载器
↓批量下载图片,美女图库↓
  网站联系: qq:121756557 email:121756557@qq.com  IT数码