React 详细文档如下:

                              ##  React 详细文档

1. React 特点:

声明式编程:
(1) 声明式编程现在是目前整个大前端开发模式: Vue React Flutter SwiftUI;
(2) 它允许我们只需要维护自己的状态,当状态改变时,React可以根据最新的状态去渲染我们的UI界面

组件化开发:
(1) 组件化开发页面目前前端的流行趋势, 我们会将复杂的界面拆分为一个个小组件
(2) 如何合理的进行组件的划分和设计也是后面我会讲到的一个重点;

多平台适配:
(1) 2013年,React 发布之处主要是开发Web 页面;
(2) 2015年, Facebook推出了 ReactNative,用于开发移动端跨平台(目前虽然Flutter非常火爆,但是还是有很多公司在使用ReactNative)
(3) 2017年,Facebook推出ReactVR,用于开发虚拟现实Web应用程序,

2. React 开发依赖

React开发必须依赖三个库

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

babel是什么呢?
Babel,又名叫Babel.js

  1. 是目前前端使用非常广泛的编辑器, 转移器
  2. 比如当下很多浏览器并不支持ES6的语法, 但是确实ES6的语法非常的简洁和方便,我们开发时希望使用它
  3. 那么编写源码时我们就可以使用ES6来编译, 之后通过Babel工具, 将ES6转成大多数浏览器都支持的ES5语法

React和Babel的关系:

  1. 默认情况下开发React其实可以不使用babel
  2. 但是目前提示我们自己使用React.createElement来编写源代码, 他编写的代码非常的繁琐和可读性差
  3. 那么我们就可以直接编写jsx(JavaScript XML) 的语法,并且让babel帮助我们转换成React.createElement.

第一次接触React 会被他繁琐的依赖搞蒙,对于Vue 来说,我们只是依赖一个Vue.js文件即可,但是React居然要依赖三个库

(4) 其实呢,这三个库是各司其职的,目前就是让每个库单纯做自己的事情
(5) 在React的0.14版本之前低没有react-dom 这个概念的,所有的功能都包含在react里
(6) 为什么要进行拆分? 原因就是react-native
(7) react包中 包含了 react和 react-native所共同拥有的核心代码

react-dom 针对web和native所完成的事情不同

(8) web端: react-dom 会讲究jsx 最终渲染成真是的DOM 显示在浏览器中
(9) native端: react-dom 会将jsx 最终渲染成原生的控件 (比如Android中的Button,IOS中的UButton)

引入依赖

  1. 方式一: 直接CDN引入
  2. 方式二: 下载后,添加本地依赖
  3. 方式三: 通过npm 管理(脚手架后续使用)

ES6的class

在ES6之前,我们通过function来定义类,

认识JSX

   const element = 

Hello world

; ReactDOM.render(element, document.querySelector("#root"))

这段element变量的声明右侧赋值的标签语法是什么呢?

  1. 他不是一段字符串(因为没有使用引号包裹),他看起来是一个HTML原生, 但是我们能在js 中直接给一个变量赋值html吗?
  2. 其实是不可以的,如果我们讲type="text/babel"去掉,name就会出现语法错误;
  3. 他到底是什么呢? 其实就是一段JSX的语法

JSX是什么?

  1. JSX是一种JavaScript的语法扩展(eXtension),也在很多地方称之为JavaScript XML,因为看起来就是一段XML语法;
  2. 他用于描述我们的UI界面, 并且其完成可以和JavaScript融合在一起使用
  3. 他不同于Vue 中的模块语法,不需要专门学习模块语法中的一些指令(v-for,v-if,v-else,v-bind)

为什么React选择JSX

React认为渲染逻辑本质上与其他UI逻辑存在内在的耦合

  1. 比如UI需要绑定事件(button,a原生等等)
  2. 比如UI中需要展示数据状态,在某些状态发生改变时,又需要改变UI

他们之间是密不可分的,所以React没有讲标记分离到不同的文件中,而是将他们组合到了一起,这个地方就是组件(component)
其实JSX就是嵌入到JavaScript中的一种结构语法

JSX的书写规范

  1. JSX的顶层只能有一个根元素, 所以很多时候会在外层包裹一个div原生
  2. 为了方便阅读 通常在JSX的外层包裹一个小括号,这样可以方便阅读,并且JSX可以进行转换书写
  3. JSX中的标签可以是单标签,也可以是双标签

如果是单标签的话,必须要/> 结尾;

JSX 的使用

  1. JSX的注释写法:{/注释的语法结构/}

  2. JSX嵌入变量
    (1) 情况一:当为Number String Array 类型时, 可以直接显示
    (2) 情况二:当变量是null,undefined,Boolean类型时,内容为空
    如果希望可以显示null,undefined,Boolean,那么需要转换成字符串;
    转换的方式很多,比如toString方法,和空字符串拼接,String(变量)等方式
    (3) 情况三:对象类型不能作为子元素(not valid as React child)

  3. JSX 嵌入表达式
    (1) 运算表达式
    (2) 三元运算符
    (3) 执行一个函数

JSX绑定属性

  1. 比如元素都会有title属性
  2. 比如元素img会有src属性
  3. 比如a元素会有href属性
  4. 比如元素可能会绑定calss
  5. 比如原生使用内联样式style

JSX的本质

  1. 实际上,JSX仅仅是React.createElement(component,props,...children)函数的语法糖.

  2. 所有的JSX最终都会转换为React.createElement的函数调用.

  3. createElement需要传递三个参数
    -- 参数一:type
    当前的ReactElement的类型;
    如果是标签元素, 那么就是用字符串表示"div";
    如果是组件元素,那么就直接使用组件的名称;

    --参数二:config
    所有的JSX中的属性都在config中以对象的属性和值的形式储存

    --参数三:children
    存放在标签中的内容,以children数组的方式进行储存
    当然,如果是多个元素,React内部有对它们进行处理,

虚拟DOM的创建过程

  1. 我们通过React.createElement最终创建出来一个ReactElement对象

  2. 这个ReactElement对象是什么作用呢? React为什么要创建它呢?
    -- 原因是React利用ReactElement对象组成了一个JavaScript的对象树
    -- JavaScript的对象树就是大名鼎鼎的虚拟DOM(Virtual DOM)

  3. 为什么使用虚拟DOM
    -- 很难跟踪状态的改变: 原有的开发模式,我们很难跟踪到状态的改变,不方便针对我们的应用程序进行调试,
    -- 操作真是DOM性能较低:传统的开发模式进行频繁的DOM操作,而这一做法性能非常的低;

  4. DOM操作性能非常低
    -- 首先,document.createElement本身创建出来的就是一个非常的复杂的对象
    http://developer.mozilla.org/zh-CN/docs/Web/API/Document?createElement
    -- 其次,DOM操作会引起浏览器的回流和重绘,所以在开发中应该避免频繁的DOM操作

  5. 声明式编程
    <1> 虚拟DOM帮助我们从命令式编程到了声明式编程的模式
    <2> React官方说法: Virtual DOM 是一种编程理念,
    -- 在这个理念中,UI以一种理想话或者说虚拟话的方式保存在内存中,并且它是一个相对简单的JavaScript对象
    -- 我们可以通过ReactDOM.render() 让虚拟DOM和真实DOM同步起来, 这个过程中叫做协调(Reconciliation)

前端脚手架

  1. 对于现在比较流行的三大框架都有属于自己的脚手架
    --Vue的脚手架:vue-cli
    --Angular的脚手架:angular-cli
    --React的脚手架:create-react-app

  2. 他们的作用都是帮助我们生成一个通用的目录结构,并且已经将我们所需的工程环境配置好

  3. 使用这些脚手架需要依赖什么呢?
    --目前这些脚手架都是使用node编写的,并且都是基于webpack的;
    --所以必须在自己的电脑上安装node环境

  4. Yarn 和 npm 命令的对比

Npm

npm install                                        
npm install[package]                              
npm install --save[package]                        
npm install --save-dev[package]                    
npm rebuild                                        
npm uninstall[package]                             
npm uninstall --save[package]                      
npm uninstall --save-dev[package]                  
npm uninstall --save-optional[package]             
npm cache clean                                    
rm-rf node_modules && npm install                  

Yarn

yarn install
yarn add[package]
yarn add[package]
yarn add[package][--dev/-D]
yarn install --force
yarn remove[package]
yarn remove[package]
yarn remove[package]
yarn remove[package]
yarn cache clean 
yarn upgrade
  1. React 安装脚手架 命令如下:

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

    --1.0 NodeJs的安装网址: https://nodejs.org/en/download

    --1.2 查看node 是否安装成功: node --version

    --1.3.yarn安装命令:npm install -g yarn
    查看yarn/npm 的版本是否安装成功: yarn/npm --version

    --1.4.React 项目的脚手架安装命令: npm install -g create-react-app
    查看版本命令: create-react-app --version

    --1.5.创建React项目的命令:
    // 创建方式一:
    create-react-app 项目名称(项目名称,不能写大写字母)

    --1.6. 项目创建好了以后:
    cd 02_learn_scaffold
    yarn start (类似于Vue的npm run serve)

需要使用yarn安装各类依赖的话:

yarn add axios 

React 项目结构 详细解说

1. node_modules: 所有依赖的总集合包,和vue的是一样的

2. public {
   favicon.ico:图标 

   index.html:每个项目的入口,单页面复应用

   manifest.json: 和web app配置相关

   logo192.png:图片而已

   robots.txt:设置爬虫规则的
} 

3. src { // 写的所有的源代码文件的
   
   App.css: 当前的App组件的 css 样式

   App.js:App组件的代码文件(函数式组件)

   App.test.js: 对App写一些测试用例的

   index.css: 写全局样式的

   index.js: 整个应用程序的入口js文件

   logo.svg: 项目刚启动时看到的当前页面旋转的那个SVG图片

   reportWebVitals.js 默认帮我们写好的注册PWA相关代码

   setupTests.js:测试初始化文件
}

4. .gitignore(这个文件的主要工作是:忽略一些不需要提交到代码仓库的文件就在这里写,不需要共享的文件写在这里)

5. package.json(关于我们整个项目管理配置的一个文件)

6. README.md 说明文档

7. yarn.lock (记录真实版本的依赖) 

当我们创建好了脚手架以后 需要把 Src 文件夹中的所有的文件全部删掉

  1. 创建 index.js 文件 里面需要写以下代码
// 第一步:
import React from 'react';
//第二步:
import ReactDOM from "react-dom"

// 导入你封装的 js 文件

import { sum } from "./utils"
console.log(sum(10, 20));

// 第三步

class App extends React.Component {
  constructor() {
    super();
    this.state = {
      count: 0
    }
  }
  render() {
    return (
      

当前计数

) } } // 第三步: // ReactDOM.render(需要挂载的组件名称, 这个地方会找到你的pubic 里面的 index.html中的
文件) ReactDOM.render(, document.querySelector('#root')) 这种写到 index.js 中是不规范的,所以 要重新写一个 App.js 文件 把 第三步 抽取到 App.js 文件中

实际代码截图:

image.png

PWA

  1. PWA全称Progressive Web App,即渐进式WEB应用
  2. 一个PWA应用首先是一个网页,可以通过Web技术编写出一个网页应用
  3. 随后添加上 App Manifest 和 Service Worker 来实现PWA的安装和离线等功能
  4. 这种Web存在的形式,我们称为Web App

PWA 解决了哪些问题呢?

  1. 可以添加到主屏幕,点击主屏幕图标可以实现启动动画以及隐藏地址栏
  2. 实现离线缓存功能,即使用户手机没有网络,依然可以使用一些离线功能
  3. 实现了消息推送
  4. 等等一系列类似于Native App 相关功能

webpack 是什么 ?

  1. webpack是一个现代化JavaScript 应用程序的静态模块打包器

  2. 当webpack 处理 应用程序时,他会递归构建一个依赖关系图,其中包含应用程序需要的每个模块 然后将所有这些模块打包成一个或多个bundle;

  3. 想要暴露出webpack的 配置显示在文件中的话 可以执行命令:
    -- yarn eject

    (1) 如果你要是在脚手架创建好了以后的话,去修改项目中的文件 会出现 一个提示,
    你就要执行 一下的命令:

    -- git add .
    -- git commit -m "代码修改"

分而治之的思想 -- 也就是组件化开发

/********* 类组件 start ******************/

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

  2. 在ES6之前,可以通过create-react-class 模块来定义类组件,但是目前官网建议我们使用ES6的class类定义

    使用class定义组件:
    -- constructor是可选的,我们通常在constructor中初始化一些数据
    -- this.state中维护的就是我们组件内部的数据
    -- render() 方法是class组件中唯一必须实现的方法

  3. render函数的返回值

(1) 当render 被调用时,他会检查 this.props 和 this.state的变化并返回以下类型之一

(2) React 元素:

  1. 通常通过JSX 创建
  2. 例如
    会被React 渲染为DOM 节点, 会被React传染为自定义组件;
  3. 无论是
    还是 均为React 元素
  1. 定义React 代码块的 快捷方式: rcc(类组件)/rfc(函数组件)

  2. 如果你不想返回一个根组件的话, 数组或者 fragments: 使得render 方法可以返回多个元素
    那么可以这种写:

import React,{Component} from 'react';

import React, { Component } from 'react';

class App extends Component {
  render() {
    return (
      [
      
Hello World
Hello React
] ); } } export default App;
  1. Portals:可以渲染子节点 到不同的DOM子树中

  2. 字符串或数值类型:他们在DOM 中会被渲染为文本节点

  3. 布尔类型或 null: 什么都不渲染

/********* 类组件 end ******************/

/********* 函数组件 start ******************/

  1. 函数组件是使用function来进行定义的函数,只是这个函数会返回和类组件中的render函数返回一样的内容

  2. 函数组件有自己的特点
    -- 没有生命周期,也会被更新并挂载,但是没有生命周期函数
    -- 没有this(组件实例)
    -- 没有内部状态(state)

/********* 函数组件 end ******************/

认识生命周期

  1. 生命周期和生命周期的关系:
    --生命周期是一个抽象的概念,在生命周期的整个过程,分成了很多的阶段
    (1) 比如挂载阶段(Mounting),组件第一次在DOM树中被渲染的过程
    在挂载阶段要执行的生命周期函数:
    constructor 函数
    render(){}
    React updates DOM and refs
    componentDidMount (已经挂载成功)
    (2) 比如在更新过程(Updataing),组件状态发生变化,重新更新渲染的过程
    New props setStete() forceUpdate()
    render() {}
    React updates DOM and refs
    componentDidUpdate (已经发生更新)
    (3) 比如卸载过程(Unmounting),组件从DOM树中被移除的过程
    Unmounting
    componentWillUnmount (即将被移除掉)

  2. React内部为了告诉我们当前处于那些阶段,会对我们组件内部实现的某些函数进行回调, 这个就是函数的生命周期函数
    (1)比如实现componentDidMount函数: 组件已经挂载到DOM上时,就会回调
    (1.1) componentDidMoun() 会在组件挂载后(插入DOM树中)立即调用
    --依赖DOM的操作可以在这里进行
    --在此处发送网络请求就是最好的地方
    --可以在此处天机一些订阅(会在componentWillUnmount取消订阅)

(2)比如实现componentDidUpdate函数: 组件已经发生了更新时,就会回调
(2.1) componentDidUpdate函数: 组件已经发生了更新时,就会回调,首次渲染不会执行此方法
-- 当组件更新后,可以在此处对DOM进行操作
-- 如果你对更新前后的props进行了比较,也可以选择此处进行网络请求;(例如props未发生变化时,则不会执行网络请求)

(3)比如实现componentWillUnmount函数: 组件即将被移除,就会回调
(3.1) componentWillUnmount() 会在组件的卸载以及销毁之前直接调用的
-- 在此方法中执行必要的清理操作
-- 例如,清楚timer,取消网路请求或清除,在componentWillUnmount中创建的订阅等

  1. 我们谈React 声明周期时,主要谈的是类的生命周期, 因为函数式组件是没有生命周期函数的;(后面我们可以通过hooks来模拟一些生命周期的回调)

3.1. getDerrivedStateFromProps的使用 -- (如果当前的construtor中的state里面的数据永远依赖别的组件传递过来的,如果你同时希望发生变化的话那么你就可以使用这个函数来同步)

3.2 shouldComponentUpdate的使用 --(这个是决定我们的render函数到底需不需要渲染)

3.3 getSnapshotBeforeUpdate的使用 -- (这个可以获取你更新之前的一些数据的)

  1. Constructor
    (1)如果不初始化state或不进行方法绑定,则不需要为React组件实现构造函数

    (2) Constructor
    -- 通过this.state赋值对象来初始化内部的state
    -- 为事件绑定实例( this )

认识组件的嵌套

import React, { Component } from 'react';

// Header
function Header() {
  return 

我是Header

} // main // main 里面还有细节的拆分 function Banner() { return

我是轮播图组件

} function Products() { return (
  • 商品列表1
  • 商品列表2
  • 商品列表3
  • 商品列表4
  • 商品列表5
  • 商品列表6
) } function Main() { return (

我是Main组件

) } // Footer function Footer() { return

我是Footer组件

} class App extends Component { render() { return (
); } } export default App;

组件之间的通讯

<一> 父组件传子组件:

  1. 父组件通过 属性 = 值的形式来传递给子组件数据;

  2. 子组件通过props参数获取父组件传递过来的数据

  3. 参数的验证 对于传递子组件的数据, 有的时候我们可能希望进行验证, 特别是对于大型项目来说
    -- 当然,如果你的项目中默认集成了Flow或者TypeScript,那么直接就可以进行类型验证
    -- 但是即使我们没有使用 Flow或者TypeScript,也是可以通过prop-types库来进行参数验证

    -- 从React v15.5开始, React.propTypes已移入另外一个包中:prop-types库

<二>子传父

  1. 在React中同样的是通过 props 传递消息,只是让父组件给子组件传递一个回调函数,在子组件中调用这个函数即可

<三>非父子组件的传递

  1. React.createContext
    -- 创建一个需要共享的Context对象
    -- 如果一个组件订阅了Context,那么这个组件会从离自身最近的那个匹配的Provider中读取到当前的context值;
    -- defaultValue是组件在顶层查找过程中没有找到对应的Provider,那么就使用默认值

    const MyContext = React.createContext(defaultValue)
    
  2. Context.Provider
    --每个Context对象都会返回一个Provider React组件,它允许消费组件订阅context 的变化
    --Provider接收一个value属性,传递给消费组件
    --一个Provider可以和多个消费组件有对应关系;
    --多个Provider也可以嵌套使用,里层覆盖外层的数据
    --当Provider的value值发生变化时,它内部的所有消费组件都会被重新渲染

  3. class.contextType
    --挂载在class上的contextType属性会被重新赋值为一个由React.createContext() 创建Context对象
    --这能让你使用this.context来消费Context上的那个值
    --你可以在任何的生命周期访问他,包括在render函数中也是可以的

  4. UserContext.Consumer
    -- 函数式的专用语法:

    {
    value => {
    return (


    用户昵称: {value.nikename}


    用户等级: {value.level}



    )
    }
    }

为什么使用setState

  1. 因为修改了 state之后,希望React根据最新的State来重新渲染界面,但是这种方式的修改React并不知道数据发生变化,

  2. React并没有实现类似于Vue的Object.defineProtype或者Vue3的Proxy的方式监听数据的变化

  3. 我们必须通过setState的方法告知数据已经发生变化了

  4. 在组建中并没有实现setState的方法,为什么可以调用了?
    --原因很简单,setState方法是从Component中继承过来的

  5. setSate 这个是一个异步的更新

  6. 为什么setState 设计为异步?
    -- https://github.com/facebook/react/issues/11527#issuecomment-360199710

    --总结:
    1. setState设计为异步,可以显著的提升性能;
    2. 如果每次调用setState都进行一次更新,那么意味着render函数会被繁琐调用,界面重新渲染,这样效率很低;
    3. 最好的办法应该是获取到多个更新,之后进行批量的更新
    4. 如果同步更新了state,但是还没有执行render函数,那么state和props不能保持同步
    5. state和props不能保持一致性,会在开发中产生很多问题
    PS: setState在那些情况是同步那些是异步呢?
    -- 在组件生命周期或React合成时间中,setState是异步;
    -- 在setTimeout或者原生dom事件中,setState是同步
    

React更新机制

-- 我们在前面已经学习React 的渲染流程
JSX ---> 虚拟DOM --->真是DOM

-- 那么React的更新流程呢?
Props/state改变 --> render函数重新执行 --> 产生新的DOM树-->新旧DOM树进行diff -->计算出差异进行更新-->更新到真实的DOM上

  1. 情况一: 当节点为不同的元素,React会拆卸原有的树,并且简历起新的树;

-- 当一个元素从 变为,从

变为,都会触发一个完整的重建流程

-- 当卸载一颗树时,对应的DOM节点也会被销毁,组件实例将执行componentWillUnmount() 方法
-- 当简历一颗新的树时,对应的DOM节点会创建以及插入到DOM中,组件实例将执行componentWillMount() 方法,紧接着componentDidMount()方法

比如下面代码更改:
React会销毁Counter组件并且重新装载一个新的组件,而不会对Counter进行复用

  1. 情况二:对比同一类型的元素
    -- 当对比两个相同的类型的React元素时,React会保留DOM节点,仅比对及更新与改变的属性
    -- 比如下面的代码更改
    通过比对这两个元素,React知道需要修改DOM元素上的className;

比如下面的代码更改:
当更新style属性时,React仅更新有所更变的属性
通过比对这两个元素时,React知道只需要修改DOM元素上的color样式,无需修改fontWeight.

  1. 如果是同类型的组件元素:
    --组件会保持不变,React会更新改组件的props,并且调用componentWillReceiveProps()和componentWillUpdate()方法
    --下一步,调用render()方法,diff算法会将在之前的结果以及新的结果进行递归

  2. 情况三:对子节点进行递归
    --在默认条件下,当递归DOM节点的子元素时,React会同时遍历两个子元素的列表,当产生差异时,生成一个mutation
    --前面两个比较时完全相同的,所以不会产生mutation;
    --左后一个比较,产生一个mutation,将其插入到新的DOM树中即可

  • first
  • second
  • first
  • second
  • third
  1. 但是如果我们是在中间插入一条数据:
    React会对每个子元素一个mutation,而不是保持
  2. 星际穿越
  3. 盗墓空间
  4. 的不变,这种低效的比较方式会带来一定的性能问题
  • 星际穿越
  • 盗墓空间
  • 大话西游
  • 星际穿越
  • 盗墓空间

keys的优化

我们在前面的遍历列表时,总会提示一个警告,让我们加入key属性

  1. 方式一: 在最后面插入数据
    这种情况,有无key意义并不大

  2. 方式二: 在前面插入数据
    这种做法,在没有key 的情况下,所有的li 都需要进行修改

  3. 当子元素(这里的li) 拥有key时,React使用key为111 和 222的元素仅仅进行移位,不需要进行任何的修改

将key为333 的元素插入到最前面的位置即可

  1. key的注意事项:
    -- key应该是唯一的
    -- key不要使用随机数(随机数在下一次render时,会重新生成一个数字)
    -- 使用index作为可以,对性能是没有优化的

render 函数的调用

  1. 当你有很多的组件都嵌套在App里面的时候 例如:
import React, { Component } from 'react';

class Banner extends Component {
  render() {
    return 

我是中间部分

} } class HeaderBox extends Component { render () { return (
我是头部标签
) } } function Main() { return (
) } class App extends Component { render() { return (
); } } export default App; 这样的话就会你每次在点击button + 1 的时候 他会把所有的组件都会重新的渲染一遍,这样很影响性能, 所以就要用shouldComponentUpdata这个生命周期函数 PS: 这个生命周期不是随便乱用的,这个是当你需要阻断更新的地方你就阻断,不应该阻断的地方你就不要阻断

shouldComponentUpdate生命周期的使用--这个是类特有的生命周期:

  1. React 给我们提供一个生命周期方法 shouldComponentUpdate(很多时候简称SCU),这个方法接受参数,并且需要有返回值

  2. 该方法有两个参数
    -- 参数一:nextProps修改之后,最新的props属性
    -- 参数二:nextState修改之后,最新的state属性

  3. 该方法返回的是一个Boolean类型
    -- 返回值为true,那么就需要调用render方法
    -- 返回值为false,那么就不需要调用render方法
    -- 默认返回的是true,也就是只要state发生了变化,就会调用render方法

  4. 比如我们在App中增加一个message属性
    --jsx中没有依赖这个message,那么他的改变不应该引起重新渲染
    -- 但是因为render监听到state的改变,就会重新render,所以最后render方法还是被重新调用了

如果每个组件都有这种情况的话,那是不是每个组件中都要写shoundComponentUpdate呢?

不是的,你只需要继承PureComponent 这个就可以解决所有的问题 (这个仅限用于类组件上面的,函数式组件是不行的需要用到 memo)

PureComponent 的使用

import React, { Component } from 'react';

// 所以不管你里面有多少组件 就不需要写shoundComponentUpdate生命周期了
// 也不需要在生命周期中去写判断了 
// 直接去让你的组件去继承 PureComponent 这个就行了 
class App extends PureComponent {
  constructor(props) {
    super(props)
    this.state = {
      count: 0,
      message: "Hello,world"
    }
  }
  // 这样的话就会你每次在点击button + 1 的时候 他会把所有的组件都会重新的渲染一遍,
  // 这样很影响性能, 所以就要用shouldComponentUpdata这个生命周期函数
  // 这个生命周期不是随便乱用的,这个是当你需要阻断更新的地方你就阻断,
  // 不应该阻断的地方你就不要阻断,所以这个地方你要做判断
  // shouldComponentUpdate(nextProps,nextState) // 里面会有2个参数的
  // 最新的 nextProps 和最新的 nextState
  // 如果还有其他的需要通过的那么你就需要继续在下面判断了
  shouldComponentUpdate(nextProps, nextState) {
    // console.log(nextProps);
    // console.log(nextState);
    // return true // 只要这个地方写true的话就是所有的组件需要更新
    // return false // 这个就不会更新了
    if (this.state.count !== nextState.count) {
      return true
    }
    return false

  }
  render() {
    console.log("App render函数调用");
    return (
      

当前计数: {this.state.count}

); } addNumber() { this.setState({ count: this.state.count + 1 }) } changeText() { this.setState({ message: "哈哈哈" }) } } export default App; PS: 所以不管你里面有多少组件 就不需要写shoundComponentUpdate生命周期了,也不需要在生命周期中去写判断了, 直接去让你的组件去继承 PureComponent 这个就行了 (这个仅限用于类组件上面的,函数式组件是不行的需要用到 memo)

memo的使用:

// 1.导入memo 高阶组件
import React, { PureComponent,memo } from 'react';

class Header extends PureComponent {
  render() {
    return 

我是头部标签

} } // 2. 使用 memo const MemoHeader = memo(function Products () { return (
  • 我是商品列表一
  • 我是商品列表二
  • 我是商品列表三
  • 我是商品列表四
  • 我是商品列表五
) }) function Main() { return (
{/* 挂载memo */}
) } class App extends PureComponent { render() { return (
); } } export default App;

setState传递的数据需要不可变的数据

import React, { PureComponent } from 'react';

class App extends PureComponent {
  constructor(props) {
    super(props)
    this.state = {
      nameList: [
        { name: "lilei", age: 20 },
        { name: "hanmeimei", age: 28 },
        { name: "chuchuhu", age: 25 },
      ]
    }
  }
  // shouldComponentUpdate(newProps, newState) {
  //   if (newState.nameList !== this.state.nameList) {
  //     return true
  //   }
  //   return false
  // }
  render() {
    return (
      

好友列表

    { this.state.nameList.map((item, index) => { return (
  • 姓名:{item.name} 年纪:{item.age}
  • ) }) }
); } addData() { // const newData = { name: "tom", age: 30 } // this.state.nameList.push(newData) // this.setState({ // nameList:this.state.nameList // }) // 推荐做法 const newNameList = [...this.state.nameList] newNameList.push({ name: "tom", age: 28 }) this.setState({ nameList: newNameList }) } addNum(index) { const newNames = [...this.state.nameList] const addNewAge = newNames[index].age += 1 this.setState({ age:addNewAge }) } } export default App;

全局事件 events 兄弟之间的传递

  1. 需求 当我在这个组件中点击按钮
    向上面的Home 组件传递一条数据过去
    然后在Home组件中展示出来
    解决方案:
    需要下载一个第三方库 events --- > yarn add events
import React, { PureComponent } from 'react';

// 1.EventEmitter(事件发射)--> 这个是一个类
import { EventEmitter } from "events"
// 2.创建事件总线: event bus
// 像这样的文件你是可以单独的存在一个全局的文件中的
// 然后你再导入到你需要的文件中
const eventBus = new EventEmitter();

class Home extends PureComponent {
  // 5. 要想监听的话 你要在组件的生命周期里面去监听
  // componentDidMount(){} 要在挂载阶段去监听你的事件
  // componentWillUnmount() {} 要在卸载过程去下载你的事件
  componentDidMount() {
    /**
     *  eventBus.addListener()方法里面是有2个参数的
     *  
     *  第一个参数是:监听兄弟组件传递过来的事件
     *  第二个参数是: 一个回调函数
     * 
     * **/ 
    // eventBus.addListener('sayHello',(...args)=>{
    //   console.log(args);
    // })

    eventBus.addListener('sayHello',this.handleSayHelloListenner)
   }
  componentWillUnmount() { 
    // 6. 当组件即将小时的时候 你应当清楚监听事件
    // eventBus.removeListener()清除事件名称
    eventBus.removeListener("sayHello",this.handleSayHelloListenner)
  }

  // 单独定义这个事件
  handleSayHelloListenner(...args) {
    console.log(...args);

  }

  render() {
    return (
      
Home
) } } // 需求 当我在这个组件中点击按钮 // 向上面的Home 组件传递一条数据过去 // 然后在Home组件中展示出来 // 解决方案: // 需要下载一个第三方库 events --- > yarn add events class Profile extends PureComponent { render() { return (
Profile {/* 3.发射一个事件出去 */}
) } emitEvent() { // 上面全局定义的一个eventBus对象 他new出来的有个事件叫emit // 4. emit() 方法里面 第一个参数是:事件名称,第二个是: 传递的参数 eventBus.emit("sayHello", 'hello,world', 123322) } } class App extends PureComponent { render() { return (
); } } export default App;

事件总线 总结:

  1. 前端通过Context主要实现的是数据的共享, 但是在开发中如果有跨组件之间的时间传递,应该如何操作呢?

(1) 在Vue中我们可以通过Vue的实例,快速实现一个事件总线(eventBus),来完成操作

(2) 在React中,我们可以依赖一个使用较多的库events来完成对应的操作

(3) 我们可以通过npm 或者yarn来安装events

yarn add events

(4) events常用的API:
-- 创建EventEmitter对象: eventBus对象
const eventBus = new EventEmitter()

-- 发射事件: eventBus.emit("事件名称",参数列表)
eventBus.emit("sayHello", 'hello,world', 123322)

-- 监听事件:events.addListener("事件监听",监听函数)
 eventBus.addListener('sayHello',this.handleSayHelloListenner)

-- 移除事件:eventBus.removeListener("事件监听",监听函数)
 eventBus.removeListener(sayHello",this.handleSayHelloListenner)

如何使用ref

  1. 在React的开发模式中,通常项情况下不需要,也不建议直接去操作DOM原生,但是某些特殊情况,确实需要获取到DOM进行某些操作

(1) 管理焦点,文本选择或媒体播放
(2) 触发强制动画
(3) 继承第三方DOM库

  1. 如何创建refs来获取对应的DOM呢? 目前是有三种方式:
    (1) 方式一: 传入字符串
    -- 使用时通过this.refs. 传入的字符串格式获取对应的元素
import React, { PureComponent } from 'react';

class App extends PureComponent {
  componentDidMount() {
    // document.getElementById()  // 一般不推荐
  }
  render() {
    return (
      
{/* 1.方式一: ref=字符串/对象/函数 */}

React,Hello

); } changeText() { // 2. // console.log(this.refs.titleRef); this.refs.titleRef.innerHTML = "哈哈哈哈" } } export default App;

(2) 方式二: 传入一个对象
-- 对象是通过React.createRef() 方式创建出来的
-- 使用时获取到创建的对象其中有一个current属性就是对应的元素


  // 1.导入一个 createRef
import React, { createRef, PureComponent } from 'react';

class App extends PureComponent {
  constructor(props) {
    super(props)
    //  2.第二步:    
    this.titleRef = createRef();
  }
  render() {
    return (
      
{/* 3. 使用ref=对象(ref={this.titleRef}) */}

React,Hello

); } changeText() { // 4.对象的使用方式: console.log(this.titleRef); // {current:h2} this.titleRef.current.innerHTML = "Hello,JavaScript" } } export default App;

(3) 方式三:传入一个函数
-- 该函数会在DOM被挂载时进行回调, 这个函数会传入一个 元素对象,我们可以自己保存;
-- 使用时,直接拿到之前保存的元素对象即可

import React, { PureComponent } from 'react';

class App extends PureComponent {
  constructor(props) {
    super(props)
    this.titleRef = null
  }
  render() {
    return (
      
{/* 1.传入一个函数基本语法:ref={arg =>this.titleRef = arg */}

this.titleRef = arg}>React,Hello

); } changeText() { // 2. console.log(this.titleRef); this.titleRef.innerHTML = "Hello,TypeScript" } } export default App;

refs 的类型

  1. ref 的值根据节点的类型而有所不同
    -- 当ref属性用于HTML元素时.构造函数中使用React.createRef()创建的ref接收底层DOM元素作为DOM 元素作为其current属性
    -- 当ref属性用于自定义class组件是,ref对象接收组价的挂载实例作为其current属性
    -- 你不能在函数组件上使用ref属性.因为他们没有实的
  2. 函数式组件是没有实例的,所以无法通过ref获取他们的实例
    -- 但是某些时候,我们可能想要获取函数式组件中的某个DOM元素
    -- 这个时候我们可以通过React.forwardRef,后面我们也会学习hooks中如何使用ref

认识受控组件

  1. 在React中,HTML表单的处理方式和普通的DOM元素不太一样,表单元素通常会保存在一些内部的state

  2. 比如下面的HTML表单元素
    -- 这个处理方式是DOM默认的处理HTML表单的行为, 在用户点击提交时会提交到某个服务器中, 并且刷新页面
    -- 在React中,并没有禁止这个行为,他依然是有效的
    -- 但是尝尝情况下使用JavaScript函数来方便的处理表单提交,同时还可以访问影虎填写的表单数据
    -- 实现这种效果的标准方式是使用 "受控组件"

非受控组件

import React, { PureComponent,createRef } from 'react';

class App extends PureComponent {
  constructor(props) {
    super(props)
    this.usernameRef = createRef()
  }
  render() {
    return (
      
this.handleSubmit(e)}>
); } handleSubmit(e) { e.preventDefault() console.log(this.usernameRef.current.value); } } export default App;

认识高阶函数

认识高阶组件

  1. 高阶组件的英文名叫Higher-Order Components,简称HOC

  2. 官方的定义:高阶组件是参数为组件,返回值为新组件的函数

  3. 首先,高阶组件 本身不是一个组件,而是一个函数

  4. 其次,这个函数的参数是一个组件, 返回值也是一个组件

  5. 高阶组件的调用类似于这样:
    const EnhancedComponent = higherOrderComonent(WrappedComponent)

Portals 的使用

  1. 在某些情况下,我们希望渲染的内容独立于父组件,甚至是独立于当前挂载到的DOM元素中,(默认都是挂载到id为root的DOM元素上的)

  2. Portal 提供了一种将子节点渲染到存在父组件以外的DOM节点的优秀的案例
    --第一个参数(child) 是任何可渲染的React子元素,例如一个元素,字符串或fragment;

-- 第二个参数是(container) 是一个DOM元素

ReactDOM.createPortal(child,container)
  1. 通常来讲,当你从组建的render方法返回一个元素时,该方法被挂载到DOM节点中离其最近的父节点:

  2. 然而,有时候将子元素插入到DOM节点中的不同位置也是有好处的

render() {
  // React 挂载一个新的div, 并且把子元素渲染其中
  return(
    
{this.props.children}
) }
render() {
  // React 并没有创建一个新的div 它只是把子元素渲染到'DOMNode'中
  return ReactDOM.createPortal(
    this.props.children,
    domNode
    )
}

Modal 组建案例

  1. 比如说,我们准备开发一个Modal组件, 他可以将他的子组件渲染到品目的中间位置:

fragment的使用

// 1.fragment的使用 导入Fragment,类似于小程序中的 block 标签
import React, { PureComponent, Fragment } from 'react';
export default class App extends PureComponent {
  constructor(props) {
    super(props)

    this.state = {
      count: 0,
      friends:[
        {name:"huzhenchu",age:18,sex:"男"},
        {name:"huzhenchu",age:18,sex:"男"},
        {name:"huzhenchu",age:18,sex:"男"},
      ]
    }
  }
  render() {
    return (
      // 2.Fragment标签,类似于小程序中的 block 标签  
      // 
      //   

当前计数:{ this.state.count }

// //
// 或者还可以这样写de <>

当前计数:{this.state.count}

{ this.state.friends.map(item=> { return ( // //
{item.name}
//
{item.age}
//
// 或者这样写 但是你要写key的话,这种段语法 是不可以添加任何属性的 // 这种情况你必须要写 <>
{item.name}
{item.age}
) }) }
); } addNum() { this.setState({ count: this.state.count + 1 }) } }

StrictMode:React

ReactDOM.render(
  
    
  ,
  document.getElementById('root')
)
  1. StrictMode 是一个用来突出显示应用程序中潜在问题的工具
    -- 与Fragment一样,StrictMode不会渲染任何可见的UI;
    --它为其后代元素触发额外的检查和警告
    --严格模式检查仅在开发模式下运行;他们不会影响生产构建

  2. 可以为应用程序的任何部分启用严格模式
    --不会对Header和Footer组件运行严格模式检查
    --但是,ComponentOne 和 ComponentTwo以及他们的所有后代元素都将进行检查








  3. 严格模式检查什么?
    --1. 识别不安全的生命周期
    --2. 使用过时的ref API
    --3. 检查意外的副作用
    --4. 使用废弃的findDOMMNode

组件化开发的CSS

  1. 局部CSS的样式
  2. 动态的CSS的书写
  3. 支持所有的CSS 特性
  4. 避免样式的全局污染

React 中的四种CSS样式的编写

1.1. 内联样式的写法
-- style 接收一个采用小驼峰命名的属性的JavaScript对象,而不是CSS字符串
-- 并且可以引用state中的状态来设置相关的样式
1.2. 内联样式的优点
-- 1. 内联样式,样式之间不会有冲突
-- 2. 可以动态获取当前state中的状态

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

  1. 使用普通的css来写,导入到各个JS 文件中
    -- 缺点: 会覆盖和层叠其他文件中的css样式
  1. CSS modules
    (1) CSS modules 并不是React特有的解决方案,而是所有使用了类似于webpack配置的环境下都可以使用的.
    --但是,如果在其他项目中使用,那么我们需要自己来进行配置,比如配置webpack.config.js中的modules:true等.

    (2) React的脚手架已经内置了css modules 的配置
    --.css/.less/.scss等样式文件都修改成.module.css/.module.less/.module.scss等

    (3) 这样写的话 就可以完美的解决层叠问题和样式覆盖问题的

    (4) 优点 确实解决了局部的作用域问题,

    (5) 缺点
    --引用的类名,不能使用连接符比如(.home-title),在JavaScript中是不识别的
    --所有的className都必须使用(style.classname)的形式来编写
    --不方便动态来修改某些样式,依然需要使用内联样式的方式

  1. CSS in JS
    (1) "CSS-in-JS" 是一种模式,其中CSS由JavaScript生成而不是在外部定义的
    (2) 注意此功能并不是React的一部分,而是由第三方库提供,React对样式如何定义并没有明确态度
    (3) 事实上CSS-in-JS的模式就是一种将样式(CSS) 也写入到JavaScript中的方式,并且可以方便的使用JavaScript的状态,所以有被人称之为 All in JS

  2. 认识styled-components
    (1) CSS-in-JS通过JavaScript来为CSS赋予一些能力,包括类似于CSS预处理器一样的样式嵌套,函数定义,逻辑复用,动态修改状态..
    (2) 依然CSS预处理器也是具备某些能力,但是获取动态状态依然是一个不好处理的点;
    (3) 目前CSS-in-JS可以说是React最受欢迎的一种解决方案

    (4) 目前比较流行的CSS-in-JS 的库有哪些呢?
    --styled-components
    --emotion
    --glamorous
    (5) 目前可以说 styled-components依然是社区最流行的CSS-in-JS

    (6) 安装styled-components

    yarn add styled-components
    

React 中 编写className的写法 React中添加Class :

  1. React在JSX给了我们开发者足够多的灵活性, 你可以像JavaScript代码一样,通过一些逻辑来决定时候添加某些class:
{/* 这些都是原生的React添加class 的方法 */}

我是标题一

{/* 这种写 如果title的后面或者 active的前面不加一个空格的话就会连到一起,这种写不是很方便 */}

我是标题二

{/* 这样的话比较清楚,为什么呢? 这个地方是可以写一个数组的 */}

我是标题三

  1. 这个时候我们就可以借助第三方库:classnames
    -- 很明显,这个是一个用于动态添加className的一个库

  2. 库的安装:

yarn add classname

classNames 库的具体用法:

import React, { PureComponent } from 'react';
/**
 * 
 * 
 * 
 *   1. React在JSX给了我们开发者足够多的灵活性, 你可以像JavaScript代码一样,通过一些逻辑来决定时候添加某些class:
 *   这些都是原生的React添加class 的方法

 *  

我是标题一

* 这种写 如果title的后面或者 active的前面不加一个空格的话就会连到一起,这种写不是很方便 *

我是标题二

* * 这样的话比较清楚,为什么呢? 这个地方是可以写一个数组的 *

我是标题三

* 2. 这个时候我们就可以借助第三方库:classnames * -- 很明显,这个是一个用于动态添加className的一个库 * 3. 库的安装命令: * yarn add classname * * **/ // 1. 现下载安装 className库 yarn add classname // 2. 导入 import classNames from "classname" class App extends PureComponent { constructor(props) { super(props); this.state = { isActive: true, isBar: false } } render() { const { isActive, isBar } = this.state; const errorClass = "error"; const warnClass = null; // 这个不会被加进去 const undiClass = undefined; // 这个不会被加进去 const zreo = 0; // 这个不会被加进去 const tenNum = 10 // 数字为真的时候会被加进去 return (
{/* 这些都是原生的React添加class 的方法 */}

我是标题一

{/* 这种写 如果title的后面或者 active的前面不加一个空格的话就会连到一起,这种写不是很方便 */}

我是标题二

{/* 这样的话比较清楚,为什么呢? 这个地方是可以写一个数组的 */}

我是标题三


{/* classnames库添加class 语法使用如下: */}

我是标题一

我是标题四

{/* 如果有些属性是必须要加的话,你就写在对象的外面,如下:"title" */}

我是标题五

{/* 可以跟变量 */}

我是标题六

我是标题七

我是标题四

); } } export default App;

AntDesign 的使用

  1. AntDesign 安装 使用 npm 或者 yarn 安装
    npm install antd -save
    或者
    yarn add antd'

  2. Antd 是否有多余的代码逻辑添加进来呢?
    不会的

认识craco

  1. 想要修改create-react-app 的默认配置可以使用:
yarn run eject 可以把信息全部展示出来,但是这种不太推荐
  1. 推进的修改 craco
    1. 安装craco 这个包
      -- yarn add @craco/craco
    2. 还要安装一个命令:
      -- yarn add craco-less
    3. 然后安装 craco-less 并修改 craco.config.js 文件如下。
const CracoLessPlugin = require('craco-less');

module.exports = {
  plugins: [
    {
      plugin: CracoLessPlugin,
      options: {
        lessLoaderOptions: {
          lessOptions: {
            modifyVars: { '@primary-color': '#1DA57A' },
            javascriptEnabled: true,
          },
        },
      },
    },
  ],
};

文件配置取别名:

const CracoLessPlugin = require('craco-less');
// 导入当前路径path 模块, 这个就是node_modules里面自带的一个模块路径
const path = require('path')
// __dirname:当前页面的路径,dir 是你要传入的一个路径 然后 path.resolve 做一个拼接
const resolve = dir => path.resolve(__dirname, dir)

// 修改主题颜色
module.exports = {
  plugins: [
    {
      plugin: CracoLessPlugin,
      options: {
        lessLoaderOptions: {
          // 修改主题颜色
          lessOptions: {
            modifyVars: { '@primary-color': '#f00' },
            javascriptEnabled: true,
          },
        },
      },
    },
  ],
  //webpack 别名的修改配置
  webpack: {
    alias: {
      "@": resolve("src"), // @:这个意思就是代表当前路径所在的 src的这个路径
      "component":resolve("src/component"), // component就是当前所在的路径 所在的src下面的component,如果以后写component就是找src/component路径了

    }
  }
};

axios 的网络请求

  1. Axios 的基本使用命令:
yarn add axios

React-transition-group介绍

  1. React社区为我们提供了react-transition-group用来过渡完成动画

  2. 这个是需要安装一个插件的,安装命令:

    npm 安装

    npm install react-transition-group --save

    yarn

    yarn add react-transition-group

  3. react-transition-group 主要包含四个组件:
    -- Transition
    该组件是一个和平台无关的组件(不一定要结合CSS);
    一般是结合CSS来完成样式, 比较常用的是CSSTransition

    -- CSSTransition
    在前端开发中,通常使用的是CSSTransition来完成过渡动画效果

    --SwitchTransition
    两个组件显示和影藏切换时,使用该组件

    --TransitionGroup
    将多个动画组件包裹在其中, 一般用于列表中元素的动画

Redux的核心理念-reducer

  1. reducer 是一个纯函数

  2. reducer做的事情就是将传入的state和action结合起来生成一个新的state

  3. Redux的三大原则
    --整个应用程序的state被储存在一个object tree中,并且这个object tree只储存在一个store中;
    --Redux并没有强制让我们不能创建多个Store,但是那样做并不利于数据的维护
    --单一的数据源可以让整个应用程序的state变得方便维护,追踪,修改

  4. State是只读的
    --唯一修改state的方法一定是触发action,不要试图在其他地方通过任何的方式来修改State
    --这样就确保了View或网络请求都不能直接修改state,他们只能通过action来描述自己想要如何修改state
    --这样可以保证所有的修改都被集中化处理,并且按照严格的顺序执行,所以不需要担心race condition 的问题

  5. 使用纯函数来执行修改
    -- 通过reducer将旧state和actions联系在一起,并且返回一个新的State
    --随着应用程序的复杂度增加, 我们可以将reduce拆分成多个小的reducers,分别操作不同state tree的一部分
    --但是所有的reducer都应该是纯函数, 不能产生任何的副作用;

  6. redux三个核心的东西就是: reducer store actios
    --把所有的state储存在store中 想要修改的话 必须要通过 actions 然后通过reducer来进行视图的更改, 但是所有的reducer都应该是纯函数,所有的数据应该只存在一个store中

React-router 路由

  1. 安装react-router会自动帮助我们安装react-router的依赖 命令如下:
yarn add react-router-dom
  1. react-router最主要的API是给我们提供的一些组件
    (1) BrowserRouter或HashRouter
    --Router中包含了对路径的改变的监听,并且将会相应的路径传递给子组件
    --BrowserRouter使用history模式
    --HashRouter使用hash模式
    (2) Link和NavLink
    --通常路径的跳转是使用Link组件,最终会被渲染成a 元素
    --NavLink是在Link基础之上增加了一些样式属性
    --to属性:LInk最重要的属性,用于设置跳转到的路径
    (3) Route:
    --Route用于路径的匹配
    --path属性:用于设置匹配到的路径
    --component属性:设置匹配到路径后,渲染组件
    --exact:精准匹配,只有精准匹配到完全一致的路径,才会渲染对应的组件

你可能感兴趣的:(React 详细文档如下:)