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框架学习笔记总结

重拾React框架学习笔记总结

环境搭建

  • nvm

nvm用来管理node版本。

brew install nvm

nvm ls-remote  // 查看所有的node可用版本
nvm list  // 查看已安装node版本
nvm install 版本号  // 下载指定node版本,如nvm install v11.14.0
nvm use 版本号  // 使用指定版本
nvm alias default  // 设置默认版本,每次启动终端都使用该版本
  • yarn

yarn管理依赖包

npm install -g yarn

在国内,某些情况使用npm和yarn可能无法正常安装一个库,这个时候我们可以选择使用cnpm npm install -g cnpm --registry=https://registry.npm.taobao.org
  • create-react-app

React脚手架

npm install -g create-react-app

// 创建一个项目
create-react-app demo_app
// 启动项目
cd demo_app  
yarn start

知识点

  • 组件分类

1、函数组件和类组件
2、有状态组件和无状态组件
3、展示型组件和容器型组件

类组件的定义有如下要求:
组件的名称是大写字符开头(无论类组件还是函数组件)
类组件需要继承自React.Component
类组件必须实现render函数

函数组件是使用function来进行定义的函数,只是这个函数会返回和类组件中render函数返回一样的内容。
函数组件有自己的特点:
没有生命周期,也会被更新并挂载,但是没有生命周期函数
没有this(组件实例)
没有内部状态(state)

  • 常用生命周期
constructor() {
    super();
}

render() {
    return (<div></div>)
}

componentDidMount() {
}

componentDidUpdate(prevProps, prevState, snapshot) {
}

componentWillUnmount() {
}
  • 组件之间的通信

1、props传值
2、回调函数
3、Context
4、事件总线events

  • 参数校验以及默认值

传递给子组件的数据,有时候我们希望进行验证。
1、如果你项目中默认继承了Flow或者TypeScript,那么直接就可以进行类型验证。
2、通过prop-types 库来进行参数验证。
3、如果没有传递,希望有默认值,可以使用defaultProps

import PropTypes from 'prop-types';

static propTypes = {
    name: PropTypes.string
}
  
static defaultProps = {
    name: "default"
}
  • setState异步结果获取
1、在setState的回调函数中获取
this.setState({
    index: index
}, () => {
    console.log("获取setState异步结果 index = " + this.state.index);
})

2、在生命周期方法中获取componentDidUpdate
componentDidUpdate() {
    console.log("获取setState异步结果 index = " + this.state.index);
}

setState一定是异步的吗?
其实分成两种情况:
在组件生命周期或React合成事件中,setState是异步;
在setTimeout或者原生dom事件中,setState是同步;

  • 渲染优化

1、通过控制shouldComponentUpdate的返回值来判断是否需要重新渲染页面
2、PureComponent组件默认实现了shouldComponentUpdate
3、高阶memo组件(可以包裹函数组件进行渲染优化)

  • ref的使用

在React的开发模式中,通常情况下不需要、也不建议直接操作DOM原生,但是某些特殊的情况,确实需要获取到DOM进行某些操作:
1、管理焦点,文本选择或媒体播放
2、触发强制动画
3、集成第三方DOM库

创建ref的三种方式
1、传入一个对象
2、传入一个函数


import React, { Component, createRef } from 'react'

constructor() {
    super()
    // 方法一:
    this.testRef = createRef();
    // 方法二:
    this.testRef2 = null;
}

render() {
    return (
    <div>
         // 方法一:
        <div ref={this.testRef}>获取ref</div>
         // 方法二:
        <div ref={arg => this.testRef2 = arg}>获取ref2</div>
    </div>
    )
}

// 方法一
console.log(this.testRef.current);
// 方法二
console.log(this.testRef2);
  • 高阶组件

定义:高阶组件是一个函数,参数是一个组件,返回值是一个组件

  • ref的转发

因为函数式组件没有实例,所以不能获取到对应的组件对象.
在开发中我们可能想要获取函数式组件中某个元素的DOM,可以通过forwardRef高阶函数;

import React, { Component, createRef, forwardRef } from 'react'

const Home = forwardRef(function(props, ref) {
  return (
    <div>
      <div ref={ref}>ref div</div>
    </div>
  )
});

export default class App extends Component {
  constructor(props) {
    super();
    this.devRef = createRef();
  }

  render() {
    return (
      <div>
        <Home ref={this.devRef}/>
        <button onClick={e => this.getRef()}>获取ref</button>
      </div>
    )
  }

  getRef() {
    console.log(this.devRef.current);
  }
}
  • fragment

在之前的开发中,我们总是在一个组件中返回内容时包裹一个div元素.
我们又希望可以不渲染这样一个div应该如何操作呢?
使用Fragment,Fragment 允许你将子列表分组,而无需向 DOM 添加额外节点;

React还提供了Fragment的短语法:
它看起来像空标签 <> </>;
但是,如果我们需要在Fragment中添加key,那么就不能使用短语法

render() {
    return (
        <Fragment key="fragment">
        <Home ref={this.devRef}/>
        <button onClick={e => this.getRef()}>获取ref</button>
        </Fragment>
    )
}
  
render() {
    return (
        <>
        <Home ref={this.devRef}/>
        <button onClick={e => this.getRef()}>获取ref</button>
        </>
    )
}
  • React中编写CSS

1、内联样式

内联样式是官方推荐的一种css样式的写法:
style 接受一个采用小驼峰命名属性的 JavaScript 对象,,而不是 CSS 字符串;
并且可以引用state中的状态来设置相关的样式;

内联样式的优点:
1.内联样式, 样式之间不会有冲突
2.可以动态获取当前state中的状态

内联样式的缺点:
1.写法上都需要使用驼峰标识
2.某些样式没有提示
3.大量的样式, 代码混乱
4.某些样式无法编写(比如伪类/伪元素)

<h2 style={{fontSize: "50px", color: "red"}}>标题</h2>

2、普通的CSS

普通的css我们通常会编写到一个单独的文件,之后再进行引入。
这样的编写方式和普通的网页开发中编写方式是一致的:
1、如果我们按照普通的网页标准去编写,那么也不会有太大的问题;
2、但是组件化开发中我们总是希望组件是一个独立的模块,即便是样式也只是在自己内部生效,不会相互影响;
3、但是普通的css都属于全局的css,样式之间会相互影响;
4、这种编写方式最大的问题是样式之间会相互层叠掉;

3、css modules

React的脚手架已经内置了css modules的配置:
1、.css/.less/.scss 等样式文件都修改成 .module.css/.module.less/.module.scss 等;
2、之后就可以引用并且进行使用了;
3、css modules确实解决了局部作用域的问题。

但是这种方案也有自己的缺陷:
1、引用的类名,不能使用连接符(.home-title),在JavaScript中是不识别的;
2、所有的className都必须使用{style.className} 的形式来编写;
3、不方便动态来修改某些样式,依然需要使用内联样式的方式;

// style.module.css文件
.title {
  color: blue;
}

// 引入
import appStyle from './style.module.css';

export default class App extends PureComponent {
  render() {
    return (
      <div id="app">
        <h2 className={appStyle.title}>title</h2>
      </div>
    )
  }
}

4、CSS-in-JS

利用第三方库styled-components

  • Craco对项目进行高级配置

包括主题配置、项目目录别名配置等。

antd主题配置参考链接

1、安装插件

yarn add @craco/craco

2、在项目根目录创建craco.config.js文件

3、修改package.json文件内容

  "scripts": {
    // "start": "react-scripts start",
    // "build": "react-scripts build",
    // "test": "react-scripts test",
    "start": "craco start",
    "build": "craco build",
    "test": "craco test",
    "eject": "react-scripts eject"
  },

4、在craco.config.js配置相关内容

const path = require("path");
const resolve = dir => path.resolve(__dirname, dir);

module.exports = {
    webpack: {
        // 别名配置
        alias: {
            '@': resolve("src"),
            'pages': resolve("src/pages")
        }
    }
}
  • react-transition-group

过渡动画的使用
github介绍
1、添加库依赖
yarn add react-transition-group
2、CSSTransition使用

import React, { Component } from 'react'
import { CSSTransition } from 'react-transition-group'

export default class TransitionDemo extends Component {
    constructor() {
        super();
        this.state = {
            inProp: false
        }
    }

    render() {
        const { inProp } = this.state;
        return (
            <div>
                <CSSTransition
                    in={inProp}
                    timeout={2000}
                    classNames="my-node"
                    unmountOnExit={true}
                    onEnter={el => console.log("开始进入")}
                    onEntering={el => console.log("正在进入")}
                    onEntered={el => console.log("进入完成")}
                    onExit={el => console.log("开始退出")}
                    onExiting={el => console.log("退出状态")}
                    onExited={el => console.log("退出完成")}>
                    <div className="my-node-style">
                        {"I'll receive my-node-* classes"}
                    </div>
                </CSSTransition>
                <button type="button" onClick={() => this.changeState()}>
                    Click to Enter
                </button>
            </div>
        );
    }

    changeState() {
        const { inProp } = this.state;
        console.log(`changeState = ${inProp}`);
        this.setState({
            inProp: !inProp
        });
    }
}

// css
.my-node-enter {
    opacity: 0;
}

.my-node-enter-active {
    opacity   : 1;
    transition: opacity 200ms;
}

.my-node-exit {
    opacity: 1;
}

.my-node-exit-active {
    opacity   : 0;
    transition: opacity 200ms;
}

.my-node-style {
    font-size: 20px;
    color    : red;
}
  • 常用Hooks

1、useCallback

useCallback在什么时候使用?
场景: 在将一个组件中的函数, 传递给子元素进行回调使用时, 使用useCallback对函数进行处理.

import React, {useState, useCallback, memo} from 'react';

 const HYButton = memo((props) => {
    console.log("HYButton重新渲染: " + props.title);
    return <button onClick={props.increment}>HYButton +1</button>
  });

export default function CallbackHookDemo02() {
  console.log("CallbackHookDemo02重新渲染");

  const [count, setCount] = useState(0);
  const [show, setShow] = useState(true);

  const increment1 = () => {
    console.log("执行increment1函数");
    setCount(count + 1);
  }

//   const increment2 = () => {
//     console.log("执行increment2函数");
//     setCount(count + 1);
//   }

  const increment2 = useCallback(() => {
    console.log("执行increment2函数");
    setCount(count + 1);
  }, [count]);

  return (
    <div>
      <h2>CallbackHookDemo01: {count}</h2>
      {/* <button onClick={increment1}>+1</button>
      <button onClick={increment2}>+1</button> */}
      <HYButton title="btn1" increment={increment1}/>
      <HYButton title="btn2" increment={increment2}/>

      <button onClick={e => setShow(!show)}>show切换</button>
    </div>
  )
}

2、useMemo

使用场景:
1、函数进行大量的计算操作,使用useMemo避免每次渲染时都重现计算
2、对子组件传递相同的对象时,使用useMemo进行性能优化

场景一:
import React, {useState, useMemo} from 'react';

function calcNumber(count) {
  console.log("calcNumber重新计算");
  let total = 0;
  for (let i = 1; i <= count; i++) {
    total += i;
  }
  return total;
}

export default function MemoHookDemo01() {
  const [count, setCount] = useState(10);
  const [show, setShow] = useState(true);

// MemoHookDemo01每次渲染都会调用calcNumber重新计算
  //const total = calcNumber(count);
  
  // useMemo优化只依赖count去重新计算
  const total = useMemo(() => {
     return calcNumber(count);
   }, [count]);

  return (
    <div>
      <h2>计算数字的和: {total}</h2>
      <button onClick={e => setCount(count + 1)}>+1</button>
      <button onClick={e => setShow(!show)}>show切换</button>
    </div>
  )
}



场景二:
import React, { useState, memo, useMemo } from 'react';

const HYInfo = memo((props) => {
  console.log("HYInfo重新渲染");
  return <h2>名字: {props.info.name} 年龄: {props.info.age}</h2>
});

export default function MemoHookDemo02() {
  console.log("MemoHookDemo02重新渲染");
  const [show, setShow] = useState(true);

// MemoHookDemo02每次渲染导致HYInfo重新渲染
  // const info = { name: "why", age: 18 };
  
  // 优化
  const info = useMemo(() => {
    return { name: "why", age: 18 };
  }, []);

  return (
    <div>
      <HYInfo info={info} />
      <button onClick={e => setShow(!show)}>show切换</button>
    </div>
  )
}

3、useRef

useRef返回一个ref对象,返回的ref对象再组件的整个生命周期保持不变。
最常用的ref是两种用法:
用法一:引入DOM(或者组件,但是需要是class组件)元素;
用法二:保存一个数据,这个对象在整个生命周期中可以保存不

用法一:
import React, { useEffect, useRef } from 'react';

class TestCpn extends React.Component {
  render() {
    return <h2>TestCpn</h2>
  }
}

export default function RefHookDemo01() {

  const titleRef = useRef();
  const inputRef = useRef();
  const testRef = useRef();
  const testRef2 = useRef();

  function changeDOM() {
    titleRef.current.innerHTML = "Hello World";
    inputRef.current.focus();
    console.log(testRef.current);
  }

  return (
    <div>
      <h2 ref={titleRef}>RefHookDemo01</h2>
      <input ref={inputRef} type="text"/>
      <TestCpn ref={testRef}/>

      <button onClick={e => changeDOM()}>修改DOM</button>
    </div>
  )
}

用法二:
import React, { useRef, useState, useEffect } from 'react'

export default function RefHookDemo02() {
  const [count, setCount] = useState(0);

  const numRef = useRef(count);

  useEffect(() => {
    numRef.current = count;
  }, [count])

  return (
    <div>
      <h2>count上一次的值: {numRef.current}</h2>
      <h2>count这一次的值: {count}</h2>
      <button onClick={e => setCount(count + 10)}>+10</button>
    </div>
  )
}

4、useImperativeHandle

useImperativeHandle并不是特别好理解,我们一点点来学习。
我们先来回顾一下ref和forwardRef结合使用:
通过forwardRef可以将ref转发到子组件;
子组件拿到父组件中创建的ref,绑定到自己的某一个元素中;

import React, { useRef, forwardRef } from 'react';

const HYInput = forwardRef((props, ref) => {
  return <input ref={ref} type="text"/>
})

export default function ForwardRefDemo() {
  const inputRef = useRef();

  return (
    <div>
      <HYInput ref={inputRef}/>
      <button onClick={e => inputRef.current.focus()}>聚焦</button>
    </div>
  )
}

forwardRef的做法本身没有什么问题,但是我们是将子组件的DOM直接暴露给了父组件:
直接暴露给父组件带来的问题是某些情况的不可控;
父组件可以拿到DOM后进行任意的操作;
但是,事实上在上面的案例中,我们只是希望父组件可以操作的focus,其他并不希望它随意操作;

通过useImperativeHandle可以只暴露固定的操作:
通过useImperativeHandle的Hook,将传入的ref和useImperativeHandle第二个参数返回的对象绑定到了一起;
所以在父组件中,使用 inputRef.current时,实际上使用的是返回的对象;
比如我调用了 focus函数,甚至可以调用 printHello函数;

import React, { useRef, forwardRef, useImperativeHandle } from 'react';

const HYInput = forwardRef((props, ref) => {
  const inputRef = useRef();

  useImperativeHandle(ref, () => ({
    focus: () => {
      inputRef.current.focus();
    }
  }), [inputRef])

  return <input ref={inputRef} type="text"/>
})

export default function UseImperativeHandleHookDemo() {
  const inputRef = useRef();

  return (
    <div>
      <HYInput ref={inputRef}/>
      <button onClick={e => inputRef.current.focus()}>聚焦</button>
    </div>
  )
}

5、useLayoutEffect

useLayoutEffect看起来和useEffect非常的相似,事实上他们也只有一点区别而已:
useEffect会在渲染的内容更新到DOM上后执行,不会阻塞DOM的更新;
useLayoutEffect会在渲染的内容更新到DOM上之前执行,会阻塞DOM的更新;

如果我们希望在某些操作发生之后再更新DOM,那么应该将这个操作放到useLayoutEffect。

6、自定义Hook

自定义 Hook 是一个函数,其名称以 “use” 开头,函数内部可以调用其他的 Hook。

export default function CustomLifeHookDemo01() {
  useLoggingLife("CustomLifeHookDemo01");
  return (
    <div>
      <h2>CustomLifeHookDemo01</h2>
      <Home/>
      <Profile/>
    </div>
  )
}

// 自定义生命周期打印
function useLoggingLife(name) {
  useEffect(() => {
    console.log(`${name}组件被创建出来了`);

    return () => {
      console.log(`${name}组件被销毁掉了`);
    }
  }, []);
}

7、react-redux的hook用法

// 未使用hooks
import React, { Component } from 'react'
import { connect } from 'react-redux';
import { changeInputValue } from './store_react_redux/actionCreators';

class App extends Component {

  render() {
    return (
      <div>
        <input
          placeholder="请输入信息"
          style={{ width: "200px", height: '40px', border: '1px solid red' }}
          value={this.props.inputValue}
          onChange={this.props.handleChangeInput}
        />
      </div>
    );
  }
}

const mapStateToProps = (state) => {
  return {
    inputValue: state.inputValue
  }
}

const mapDispatchToProps = (dispatch) => {
  return {
    handleChangeInput(e) {
      dispatch(changeInputValue(e.target.value));
    }
  }
}

export default connect(mapStateToProps, mapDispatchToProps)(App);


// 使用hooks
import React, { Component } from 'react'
import { shallowEqual, useDispatch, useSelector } from 'react-redux';
import { changeInputValue } from './store_react_redux/actionCreators';

export default function App() {
  const dispatch = useDispatch();
  const {inputValue} = useSelector(state => state, shallowEqual);

  return (
    <div>
      <input
        placeholder="请输入信息"
        style={{ width: "200px", height: '40px', border: '1px solid red' }}
        value={inputValue}
        onChange={(e) => dispatch(changeInputValue(e.target.value))}
      />
    </div>
  );
}

  • React SSR

SSR(Server Side Rendering,服务端渲染),指的是页面在服务器端已经生成了完成的HTML页面结构,不需要浏览器解析;

对应的是CSR(Client Side Rendering,客户端渲染),我们开发的SPA页面通常依赖的就是客户端渲染;

同构:
一套代码既可以在服务端运行又可以在客户端运行,这就是同构应用。
同构是一种SSR的形态,是现代SSR的一种表现形式。
当用户发出请求时,先在服务器通过SSR渲染出首页的内容。
但是对应的代码同样可以在客户端被执行。
执行的目的包括事件绑定等以及其他页面切换时也可以在客户端被渲染;

React SSR成熟框架 Next.js

官方文档

  • React 构建打包优化

1、yarn build打包

文件说明:

js文件目录
[hash].chunk.js
代表是所有依赖的第三方库, vendor(第三方库) 的代码;

main.[hash].chunk.js 
我们自己编写的应用程序代码;

runtime~main.[hash].js 
Webpack runtime逻辑的chunk;
用于加载和运行你的应用程序;

media
一些字体文件和图片

2、打包优化

很多模块,其实没有必要一开始就进行加载,会影响首屏加载速度;
我们可以让某些组件用到时再加载(懒加载);
如何可以让一个组件进行懒加载呢?
使用react给我们提供的lazy函数即可

import Discover from "../pages/discover";
改为
const Discover = React.lazy(_ => import("../pages/discover"));

然后渲染路由的地方增加Suspense标签
<Suspense fallback={renderLoading()}>
{renderRoutes(routes)}
</Suspense>

function renderLoading() {
    return <div>loading...</div>
}
  JavaScript知识库 最新文章
ES6的相关知识点
react 函数式组件 & react其他一些总结
Vue基础超详细
前端JS也可以连点成线(Vue中运用 AntVG6)
Vue事件处理的基本使用
Vue后台项目的记录 (一)
前后端分离vue跨域,devServer配置proxy代理
TypeScript
初识vuex
vue项目安装包指令收集
上一篇文章      下一篇文章      查看所有文章
加:2021-08-24 15:27:49  更:2021-08-24 15:29:50 
 
开发: 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/23 12:56:36-

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