美文网首页
React 详细文档如下:

React 详细文档如下:

作者: coderhzc | 来源:发表于2021-03-25 23:55 被阅读0次
                                  ##  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 = <h2>Hello world</h2>;
       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 (
          <div>
            <h2>当前计数</h2>
            <button>+</button>
            <button>-</button>
          </div>
        )
      }
    }
    
    // 第三步:
    // ReactDOM.render(需要挂载的组件名称, 这个地方会找到你的pubic 里面的 index.html中的 <div id="root"></div> 文件)
    ReactDOM.render(<App/>, 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. 例如 <div /> 会被React 渲染为DOM 节点, <MyComponent /> 会被React传染为自定义组件;
    3. 无论是<div /> 还是<MyComponent /> 均为React 元素
    1. 定义React 代码块的 快捷方式: rcc(类组件)/rfc(函数组件)

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

    import React,{Component} from 'react';
    
    import React, { Component } from 'react';
    
    class App extends Component {
      render() {
        return (
          [
          <div>Hello World</div>
          <div>Hello React</div>
          ]
        );
      }
    }
    
    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 <h2> 我是Header </h2>
    }
    
    
    
    // main
    // main 里面还有细节的拆分
    function Banner() {
      return <h2> 我是轮播图组件 </h2>
    }
    
    function Products() {
      return (
        <div>
          <ul>
            <li>商品列表1</li>
            <li>商品列表2</li>
            <li>商品列表3</li>
            <li>商品列表4</li>
            <li>商品列表5</li>
            <li>商品列表6</li>
          </ul>
        </div>
      )
    }
    function Main() {
      return (
        <div>
          <h2>我是Main组件</h2>
          <Banner />
          <Products />
        </div>
      )
    }
    
    
    
    
    //  Footer
    function Footer() {
      return <h2> 我是Footer组件 </h2>
    }
    
    
    
    class App extends Component {
      render() {
        return (
          <div>
            <Header />
            <Main />
            <Footer />
          </div>
        );
      }
    }
    
    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
      -- 函数式的专用语法:
      <UserContext.Consumer>
      {
      value => {
      return (
      <div>
      <h2>用户昵称: {value.nikename}</h2>
      <h2>用户等级: {value.level}</h2>
      </div>
      )
      }
      }
      </UserContext.Consumer>

    为什么使用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会拆卸原有的树,并且简历起新的树;

    -- 当一个元素从<a> 变为<img>,从<Article>变为<Comment>,都会触发一个完整的重建流程

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

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

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

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

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

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

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

    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 <h2>我是中间部分</h2>
      }
    }
    class HeaderBox extends Component {
      render () {
        return (
          <div>我是头部标签</div>
        )
      }
    }
    
    function Main() {
      return (
        <div>
          <HeaderBox/>
          <Banner/>
        </div>
      )
    }
    class App extends Component {
      render() {
        return (
          <div>
            <Main/>
          </div>
        );
      }
    }
    
    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 (
          <div>
            <h2>当前计数: {this.state.count}</h2>
            <button onClick={e => this.addNumber()}>+ 1</button>
            <button onClick={e => this.changeText()}>改变文本</button>
          </div>
        );
      }
      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 <h2> 我是头部标签 </h2>
      }
    }
    // 2. 使用 memo
    const MemoHeader = memo(function Products () {
      return (
        <div>
          <ul>
            <li>我是商品列表一</li>
            <li>我是商品列表二</li>
            <li>我是商品列表三</li>
            <li>我是商品列表四</li>
            <li>我是商品列表五</li>
          </ul>
        </div>
      )
    })
    
    function Main() {
      return (
        <div>
          <Header/>
          {/* 挂载memo */}
          <MemoHeader/>
        </div>
      )
    }
    
    class App extends PureComponent {
      render() {
        return (
          <div>
            <Main/>
          </div>
        );
      }
    }
    
    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 (
          <div>
            <h2>好友列表</h2>
            <ul>
              {
                this.state.nameList.map((item, index) => {
                  return (
                    <li key={index}>
                      姓名:{item.name}
                      年纪:{item.age}
                      <button onClick={e => this.addNum(index)}>年龄+1</button>
                    </li>
                  )
                })
              }
            </ul>
            <button onClick={e => this.addData()}>添加数据</button>
          </div>
        );
      }
      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 (
          <div>
            Home
          </div>
        )
      }
    }
    // 需求 当我在这个组件中点击按钮 
    // 向上面的Home 组件传递一条数据过去
    // 然后在Home组件中展示出来
    
    // 解决方案:
    // 需要下载一个第三方库 events --- > yarn add events
    class Profile extends PureComponent {
      render() {
        return (
          <div>
            Profile
            {/* 3.发射一个事件出去 */}
            <button onClick={e => this.emitEvent()}>点击了Profile按钮</button>
          </div>
        )
      }
      emitEvent() {
        // 上面全局定义的一个eventBus对象 他new出来的有个事件叫emit
        // 4. emit() 方法里面 第一个参数是:事件名称,第二个是: 传递的参数
        eventBus.emit("sayHello", 'hello,world', 123322)
    
      }
    }
    
    class App extends PureComponent {
      render() {
        return (
          <div>
            <Home />
            <Profile />
          </div>
        );
      }
    }
    
    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 (
          <div>
            {/* 1.方式一: ref=字符串/对象/函数 */}
            <h2 ref='titleRef'>React,Hello</h2>
            <button onClick={e=> this.changeText()}>改变文本</button>
          </div>
        );
      }
      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 (
          <div>
            {/* 3. 使用ref=对象(ref={this.titleRef}) */}
            <h2 ref={this.titleRef}>React,Hello</h2>
            <button onClick={e => this.changeText()}>改变文本</button>
          </div>
        );
      }
      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 (
          <div>
            {/* 1.传入一个函数基本语法:ref={arg =>this.titleRef = arg */}
            <h2 ref={arg =>this.titleRef = arg}>React,Hello</h2>
            <button onClick={e => this.changeText()}>改变文本</button>
          </div>
        );
      }
      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函数来方便的处理表单提交,同时还可以访问影虎填写的表单数据
      -- 实现这种效果的标准方式是使用 "受控组件"

    <form>
      <label>
        名字:
        <input type="text" name="name">
      </label>
    
      <input type="submit" value="提交">
    </form>
    

    非受控组件

    import React, { PureComponent,createRef } from 'react';
    
    class App extends PureComponent {
      constructor(props) {
        super(props)
        this.usernameRef = createRef()
      }
      render() {
        return (
          <div>
            <form onSubmit={e => this.handleSubmit(e)}>
          <label htmlFor="username">
            用户:
            <input type="text" id="username" ref={this.usernameRef} />
          </label>
          <input type="submit" value="提交"/>
            </form>
          </div>
        );
      }
      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(
        <div>
        {this.props.children}
        </div>
      )
    }
    
    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 标签  
          // <Fragment>
          //   <h2>当前计数:{ this.state.count }</h2>
          //   <button onClick={e=> this.addNum()}> + 1 </button>
          // </Fragment>
    
          // 或者还可以这样写de
          <>
            <h2>当前计数:{this.state.count}</h2>
            <button onClick={e => this.addNum()}> + 1 </button>
            <div>
              {
                this.state.friends.map(item=> {
                  return (
                    // <Fragment>
                    //   <div>{item.name}</div>
                    //   <div>{item.age}</div>
                    // </Fragment>
    
                    // 或者这样写 但是你要写key的话,这种段语法 是不可以添加任何属性的
                    // 这种情况你必须要写 <Fragment key={index}></Fragment>
                    <>
                      <div>{item.name}</div>
                      <div>{item.age}</div>
                    </>
                  )
                })
              }
            </div>
          </>
        );
      }
    
      addNum() {
        this.setState({
          count: this.state.count + 1
        })
      }
    }
    

    StrictMode:React

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

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

    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 的方法 */}
    
    <h2 className={"foo bar active title"}>我是标题一</h2>
    {/* 这种写 如果title的后面或者 active的前面不加一个空格的话就会连到一起,这种写不是很方便 */}
    <h2 className={"title " + (isActive ? "active" : "")}> 我是标题二 </h2>
    
    {/* 这样的话比较清楚,为什么呢? 这个地方是可以写一个数组的 */}
    <h2 className={["title", (isActive ? "active" : "")].join(" ")}>我是标题三</h2>
    
    1. 这个时候我们就可以借助第三方库:classnames
      -- 很明显,这个是一个用于动态添加className的一个库

    2. 库的安装:

    yarn add classname
    

    classNames 库的具体用法:

    import React, { PureComponent } from 'react';
    /**
     * 
     * 
     * 
     *   1. React在JSX给了我们开发者足够多的灵活性, 你可以像JavaScript代码一样,通过一些逻辑来决定时候添加某些class:
     *   这些都是原生的React添加class 的方法
    
     *  <h2 className={"foo bar active title"}>我是标题一</h2>
    
     *  这种写 如果title的后面或者 active的前面不加一个空格的话就会连到一起,这种写不是很方便
     *  <h2 className={"title " + (isActive ? "active" : "")}> 我是标题二 </h2>
     * 
     *  这样的话比较清楚,为什么呢? 这个地方是可以写一个数组的 
     *  <h2 className={["title", (isActive ? "active" : "")].join(" ")}>我是标题三</h2>
    
     *  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 (
          <div>
            {/* 这些都是原生的React添加class 的方法 */}
    
            <h2 className={"foo bar active title"}>我是标题一</h2>
            {/* 这种写 如果title的后面或者 active的前面不加一个空格的话就会连到一起,这种写不是很方便 */}
            <h2 className={"title " + (isActive ? "active" : "")}> 我是标题二 </h2>
    
            {/* 这样的话比较清楚,为什么呢? 这个地方是可以写一个数组的 */}
            <h2 className={["title", (isActive ? "active" : "")].join(" ")}>我是标题三</h2>
    
            <hr />
    
            {/* classnames库添加class 语法使用如下: */}
            <h2 className={"foo bar active title"}>我是标题一</h2>
            <h2 className={classNames("foo", "bar", "active")}>我是标题四</h2>
    
            {/* 如果有些属性是必须要加的话,你就写在对象的外面,如下:"title" */}
            <h2 className={classNames({ "active": isActive, "bar": isBar }, "title")}>我是标题五</h2>
    
            {/* 可以跟变量 */}
            <h2 className={classNames("foo", errorClass, warnClass, undiClass, zreo, tenNum, { "active": isActive })}>我是标题六</h2>
            <h2 className={classNames(["active", "title"])}>我是标题七</h2>
            <h2 className={classNames(["active", "title", { "active": isActive }])}>我是标题四</h2>
          </div>
        );
      }
    }
    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 详细文档如下:

          本文链接:https://www.haomeiwen.com/subject/ygudhltx.html