React 詳細(xì)文檔如下:

                              ##  React 詳細(xì)文檔

1. React 特點:

聲明式編程:
(1) 聲明式編程現(xiàn)在是目前整個大前端開發(fā)模式: Vue React Flutter SwiftUI;
(2) 它允許我們只需要維護(hù)自己的狀態(tài),當(dāng)狀態(tài)改變時,React可以根據(jù)最新的狀態(tài)去渲染我們的UI界面

組件化開發(fā):
(1) 組件化開發(fā)頁面目前前端的流行趨勢, 我們會將復(fù)雜的界面拆分為一個個小組件
(2) 如何合理的進(jìn)行組件的劃分和設(shè)計也是后面我會講到的一個重點;

多平臺適配:
(1) 2013年,React 發(fā)布之處主要是開發(fā)Web 頁面;
(2) 2015年, Facebook推出了 ReactNative,用于開發(fā)移動端跨平臺(目前雖然Flutter非常火爆,但是還是有很多公司在使用ReactNative)
(3) 2017年,Facebook推出ReactVR,用于開發(fā)虛擬現(xiàn)實Web應(yīng)用程序,

2. React 開發(fā)依賴

React開發(fā)必須依賴三個庫

(1) React: 包含react所必須的核心代碼
(2) react-dom: react渲染在不同平臺所需要的核心代碼
(3) babel: 將jsx轉(zhuǎn)換成React代碼工具

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

  1. 是目前前端使用非常廣泛的編輯器, 轉(zhuǎn)移器
  2. 比如當(dāng)下很多瀏覽器并不支持ES6的語法, 但是確實ES6的語法非常的簡潔和方便,我們開發(fā)時希望使用它
  3. 那么編寫源碼時我們就可以使用ES6來編譯, 之后通過Babel工具, 將ES6轉(zhuǎn)成大多數(shù)瀏覽器都支持的ES5語法

React和Babel的關(guān)系:

  1. 默認(rèn)情況下開發(fā)React其實可以不使用babel
  2. 但是目前提示我們自己使用React.createElement來編寫源代碼, 他編寫的代碼非常的繁瑣和可讀性差
  3. 那么我們就可以直接編寫jsx(JavaScript XML) 的語法,并且讓babel幫助我們轉(zhuǎn)換成React.createElement.

第一次接觸React 會被他繁瑣的依賴搞蒙,對于Vue 來說,我們只是依賴一個Vue.js文件即可,但是React居然要依賴三個庫

(4) 其實呢,這三個庫是各司其職的,目前就是讓每個庫單純做自己的事情
(5) 在React的0.14版本之前低沒有react-dom 這個概念的,所有的功能都包含在react里
(6) 為什么要進(jìn)行拆分? 原因就是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 管理(腳手架后續(xù)使用)

ES6的class

在ES6之前,我們通過function來定義類,

認(rèn)識JSX

   const element = <h2>Hello world</h2>;
   ReactDOM.render(element, document.querySelector("#root"))

這段element變量的聲明右側(cè)賦值的標(biāo)簽語法是什么呢?

  1. 他不是一段字符串(因為沒有使用引號包裹),他看起來是一個HTML原生, 但是我們能在js 中直接給一個變量賦值html嗎?
  2. 其實是不可以的,如果我們講type="text/babel"去掉,name就會出現(xiàn)語法錯誤;
  3. 他到底是什么呢? 其實就是一段JSX的語法

JSX是什么?

  1. JSX是一種JavaScript的語法擴(kuò)展(eXtension),也在很多地方稱之為JavaScript XML,因為看起來就是一段XML語法;
  2. 他用于描述我們的UI界面, 并且其完成可以和JavaScript融合在一起使用
  3. 他不同于Vue 中的模塊語法,不需要專門學(xué)習(xí)模塊語法中的一些指令(v-for,v-if,v-else,v-bind)

為什么React選擇JSX

React認(rèn)為渲染邏輯本質(zhì)上與其他UI邏輯存在內(nèi)在的耦合

  1. 比如UI需要綁定事件(button,a原生等等)
  2. 比如UI中需要展示數(shù)據(jù)狀態(tài),在某些狀態(tài)發(fā)生改變時,又需要改變UI

他們之間是密不可分的,所以React沒有講標(biāo)記分離到不同的文件中,而是將他們組合到了一起,這個地方就是組件(component)
其實JSX就是嵌入到JavaScript中的一種結(jié)構(gòu)語法

JSX的書寫規(guī)范

  1. JSX的頂層只能有一個根元素, 所以很多時候會在外層包裹一個div原生
  2. 為了方便閱讀 通常在JSX的外層包裹一個小括號,這樣可以方便閱讀,并且JSX可以進(jìn)行轉(zhuǎn)換書寫
  3. JSX中的標(biāo)簽可以是單標(biāo)簽,也可以是雙標(biāo)簽

如果是單標(biāo)簽的話,必須要/> 結(jié)尾;

JSX 的使用

  1. JSX的注釋寫法:{/注釋的語法結(jié)構(gòu)/}

  2. JSX嵌入變量
    (1) 情況一:當(dāng)為Number String Array 類型時, 可以直接顯示
    (2) 情況二:當(dāng)變量是null,undefined,Boolean類型時,內(nèi)容為空
    如果希望可以顯示null,undefined,Boolean,那么需要轉(zhuǎn)換成字符串;
    轉(zhuǎn)換的方式很多,比如toString方法,和空字符串拼接,String(變量)等方式
    (3) 情況三:對象類型不能作為子元素(not valid as React child)

  3. JSX 嵌入表達(dá)式
    (1) 運算表達(dá)式
    (2) 三元運算符
    (3) 執(zhí)行一個函數(shù)

JSX綁定屬性

  1. 比如元素都會有title屬性
  2. 比如元素img會有src屬性
  3. 比如a元素會有href屬性
  4. 比如元素可能會綁定calss
  5. 比如原生使用內(nèi)聯(lián)樣式style

JSX的本質(zhì)

  1. 實際上,JSX僅僅是React.createElement(component,props,...children)函數(shù)的語法糖.

  2. 所有的JSX最終都會轉(zhuǎn)換為React.createElement的函數(shù)調(diào)用.

  3. createElement需要傳遞三個參數(shù)
    -- 參數(shù)一:type
    當(dāng)前的ReactElement的類型;
    如果是標(biāo)簽元素, 那么就是用字符串表示"div";
    如果是組件元素,那么就直接使用組件的名稱;

    --參數(shù)二:config
    所有的JSX中的屬性都在config中以對象的屬性和值的形式儲存

    --參數(shù)三:children
    存放在標(biāo)簽中的內(nèi)容,以children數(shù)組的方式進(jìn)行儲存
    當(dāng)然,如果是多個元素,React內(nèi)部有對它們進(jìn)行處理,

虛擬DOM的創(chuàng)建過程

  1. 我們通過React.createElement最終創(chuàng)建出來一個ReactElement對象

  2. 這個ReactElement對象是什么作用呢? React為什么要創(chuàng)建它呢?
    -- 原因是React利用ReactElement對象組成了一個JavaScript的對象樹
    -- JavaScript的對象樹就是大名鼎鼎的虛擬DOM(Virtual DOM)

  3. 為什么使用虛擬DOM
    -- 很難跟蹤狀態(tài)的改變: 原有的開發(fā)模式,我們很難跟蹤到狀態(tài)的改變,不方便針對我們的應(yīng)用程序進(jìn)行調(diào)試,
    -- 操作真是DOM性能較低:傳統(tǒng)的開發(fā)模式進(jìn)行頻繁的DOM操作,而這一做法性能非常的低;

  4. DOM操作性能非常低
    -- 首先,document.createElement本身創(chuàng)建出來的就是一個非常的復(fù)雜的對象
    http://developer.mozilla.org/zh-CN/docs/Web/API/Document?createElement
    -- 其次,DOM操作會引起瀏覽器的回流和重繪,所以在開發(fā)中應(yīng)該避免頻繁的DOM操作

  5. 聲明式編程
    <1> 虛擬DOM幫助我們從命令式編程到了聲明式編程的模式
    <2> React官方說法: Virtual DOM 是一種編程理念,
    -- 在這個理念中,UI以一種理想話或者說虛擬話的方式保存在內(nèi)存中,并且它是一個相對簡單的JavaScript對象
    -- 我們可以通過ReactDOM.render() 讓虛擬DOM和真實DOM同步起來, 這個過程中叫做協(xié)調(diào)(Reconciliation)

前端腳手架

  1. 對于現(xiàn)在比較流行的三大框架都有屬于自己的腳手架
    --Vue的腳手架:vue-cli
    --Angular的腳手架:angular-cli
    --React的腳手架:create-react-app

  2. 他們的作用都是幫助我們生成一個通用的目錄結(jié)構(gòu),并且已經(jīng)將我們所需的工程環(huán)境配置好

  3. 使用這些腳手架需要依賴什么呢?
    --目前這些腳手架都是使用node編寫的,并且都是基于webpack的;
    --所以必須在自己的電腦上安裝node環(huán)境

  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.在國內(nèi),某些情況使用npm和yarn可能無法正常的安裝一個庫,這個時候我們就可以選擇使用cnpm
    --CNPM安裝命令: npm install -g cnpm --registry=https://registry.npm.taobao.org

    --1.0 NodeJs的安裝網(wǎng)址: 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.創(chuàng)建React項目的命令:
    // 創(chuàng)建方式一:
    create-react-app 項目名稱(項目名稱,不能寫大寫字母)

    --1.6. 項目創(chuàng)建好了以后:
    cd 02_learn_scaffold
    yarn start (類似于Vue的npm run serve)

需要使用yarn安裝各類依賴的話:

yarn add axios 

React 項目結(jié)構(gòu) 詳細(xì)解說

1. node_modules: 所有依賴的總集合包,和vue的是一樣的

2. public {
   favicon.ico:圖標(biāo) 

   index.html:每個項目的入口,單頁面復(fù)應(yīng)用

   manifest.json: 和web app配置相關(guān)

   logo192.png:圖片而已

   robots.txt:設(shè)置爬蟲規(guī)則的
} 

3. src { // 寫的所有的源代碼文件的
   
   App.css: 當(dāng)前的App組件的 css 樣式

   App.js:App組件的代碼文件(函數(shù)式組件)

   App.test.js: 對App寫一些測試用例的

   index.css: 寫全局樣式的

   index.js: 整個應(yīng)用程序的入口js文件

   logo.svg: 項目剛啟動時看到的當(dāng)前頁面旋轉(zhuǎn)的那個SVG圖片

   reportWebVitals.js 默認(rèn)幫我們寫好的注冊PWA相關(guān)代碼

   setupTests.js:測試初始化文件
}

4. .gitignore(這個文件的主要工作是:忽略一些不需要提交到代碼倉庫的文件就在這里寫,不需要共享的文件寫在這里)

5. package.json(關(guān)于我們整個項目管理配置的一個文件)

6. README.md 說明文檔

7. yarn.lock (記錄真實版本的依賴) 

當(dāng)我們創(chuàng)建好了腳手架以后 需要把 Src 文件夾中的所有的文件全部刪掉

  1. 創(chuàng)建 index.js 文件 里面需要寫以下代碼
// 第一步:
import React from 'react';
//第二步:
import ReactDOM from "react-dom"

// 導(dǎo)入你封裝的 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>當(dāng)前計數(shù)</h2>
        <button>+</button>
        <button>-</button>
      </div>
    )
  }
}

// 第三步:
// ReactDOM.render(需要掛載的組件名稱, 這個地方會找到你的pubic 里面的 index.html中的 <div id="root"></div> 文件)
ReactDOM.render(<App/>, document.querySelector('#root'))

這種寫到 index.js 中是不規(guī)范的,所以 要重新寫一個 App.js 文件 把 第三步 抽取到 App.js 文件中

實際代碼截圖:

image.png

PWA

  1. PWA全稱Progressive Web App,即漸進(jìn)式WEB應(yīng)用
  2. 一個PWA應(yīng)用首先是一個網(wǎng)頁,可以通過Web技術(shù)編寫出一個網(wǎng)頁應(yīng)用
  3. 隨后添加上 App Manifest 和 Service Worker 來實現(xiàn)PWA的安裝和離線等功能
  4. 這種Web存在的形式,我們稱為Web App

PWA 解決了哪些問題呢?

  1. 可以添加到主屏幕,點擊主屏幕圖標(biāo)可以實現(xiàn)啟動動畫以及隱藏地址欄
  2. 實現(xiàn)離線緩存功能,即使用戶手機沒有網(wǎng)絡(luò),依然可以使用一些離線功能
  3. 實現(xiàn)了消息推送
  4. 等等一系列類似于Native App 相關(guān)功能

webpack 是什么 ?

  1. webpack是一個現(xiàn)代化JavaScript 應(yīng)用程序的靜態(tài)模塊打包器

  2. 當(dāng)webpack 處理 應(yīng)用程序時,他會遞歸構(gòu)建一個依賴關(guān)系圖,其中包含應(yīng)用程序需要的每個模塊 然后將所有這些模塊打包成一個或多個bundle;

  3. 想要暴露出webpack的 配置顯示在文件中的話 可以執(zhí)行命令:
    -- yarn eject

    (1) 如果你要是在腳手架創(chuàng)建好了以后的話,去修改項目中的文件 會出現(xiàn) 一個提示,
    你就要執(zhí)行 一下的命令:

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

分而治之的思想 -- 也就是組件化開發(fā)

/********* 類組件 start ******************/

  1. 類組件的定義有如下要求:
    -- 組件的名稱是大寫字符開頭(無論是類組件還是函數(shù)組件)
    -- 類組件需要繼承自 React.Component
    -- 類組件必須實現(xiàn)render函數(shù)

  2. 在ES6之前,可以通過create-react-class 模塊來定義類組件,但是目前官網(wǎng)建議我們使用ES6的class類定義

    使用class定義組件:
    -- constructor是可選的,我們通常在constructor中初始化一些數(shù)據(jù)
    -- this.state中維護(hù)的就是我們組件內(nèi)部的數(shù)據(jù)
    -- render() 方法是class組件中唯一必須實現(xiàn)的方法

  3. render函數(shù)的返回值

(1) 當(dāng)render 被調(diào)用時,他會檢查 this.props 和 this.state的變化并返回以下類型之一

(2) React 元素:

  1. 通常通過JSX 創(chuàng)建
  2. 例如 <div /> 會被React 渲染為DOM 節(jié)點, <MyComponent /> 會被React傳染為自定義組件;
  3. 無論是<div /> 還是<MyComponent /> 均為React 元素
  1. 定義React 代碼塊的 快捷方式: rcc(類組件)/rfc(函數(shù)組件)

  2. 如果你不想返回一個根組件的話, 數(shù)組或者 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:可以渲染子節(jié)點 到不同的DOM子樹中

  2. 字符串或數(shù)值類型:他們在DOM 中會被渲染為文本節(jié)點

  3. 布爾類型或 null: 什么都不渲染

/********* 類組件 end ******************/

/********* 函數(shù)組件 start ******************/

  1. 函數(shù)組件是使用function來進(jìn)行定義的函數(shù),只是這個函數(shù)會返回和類組件中的render函數(shù)返回一樣的內(nèi)容

  2. 函數(shù)組件有自己的特點
    -- 沒有生命周期,也會被更新并掛載,但是沒有生命周期函數(shù)
    -- 沒有this(組件實例)
    -- 沒有內(nèi)部狀態(tài)(state)

/********* 函數(shù)組件 end ******************/

認(rèn)識生命周期

  1. 生命周期和生命周期的關(guān)系:
    --生命周期是一個抽象的概念,在生命周期的整個過程,分成了很多的階段
    (1) 比如掛載階段(Mounting),組件第一次在DOM樹中被渲染的過程
    在掛載階段要執(zhí)行的生命周期函數(shù):
    constructor 函數(shù)
    render(){}
    React updates DOM and refs
    componentDidMount (已經(jīng)掛載成功)
    (2) 比如在更新過程(Updataing),組件狀態(tài)發(fā)生變化,重新更新渲染的過程
    New props setStete() forceUpdate()
    render() {}
    React updates DOM and refs
    componentDidUpdate (已經(jīng)發(fā)生更新)
    (3) 比如卸載過程(Unmounting),組件從DOM樹中被移除的過程
    Unmounting
    componentWillUnmount (即將被移除掉)

  2. React內(nèi)部為了告訴我們當(dāng)前處于那些階段,會對我們組件內(nèi)部實現(xiàn)的某些函數(shù)進(jìn)行回調(diào), 這個就是函數(shù)的生命周期函數(shù)
    (1)比如實現(xiàn)componentDidMount函數(shù): 組件已經(jīng)掛載到DOM上時,就會回調(diào)
    (1.1) componentDidMoun() 會在組件掛載后(插入DOM樹中)立即調(diào)用
    --依賴DOM的操作可以在這里進(jìn)行
    --在此處發(fā)送網(wǎng)絡(luò)請求就是最好的地方
    --可以在此處天機一些訂閱(會在componentWillUnmount取消訂閱)

(2)比如實現(xiàn)componentDidUpdate函數(shù): 組件已經(jīng)發(fā)生了更新時,就會回調(diào)
(2.1) componentDidUpdate函數(shù): 組件已經(jīng)發(fā)生了更新時,就會回調(diào),首次渲染不會執(zhí)行此方法
-- 當(dāng)組件更新后,可以在此處對DOM進(jìn)行操作
-- 如果你對更新前后的props進(jìn)行了比較,也可以選擇此處進(jìn)行網(wǎng)絡(luò)請求;(例如props未發(fā)生變化時,則不會執(zhí)行網(wǎng)絡(luò)請求)

(3)比如實現(xiàn)componentWillUnmount函數(shù): 組件即將被移除,就會回調(diào)
(3.1) componentWillUnmount() 會在組件的卸載以及銷毀之前直接調(diào)用的
-- 在此方法中執(zhí)行必要的清理操作
-- 例如,清楚timer,取消網(wǎng)路請求或清除,在componentWillUnmount中創(chuàng)建的訂閱等

  1. 我們談React 聲明周期時,主要談的是類的生命周期, 因為函數(shù)式組件是沒有生命周期函數(shù)的;(后面我們可以通過hooks來模擬一些生命周期的回調(diào))

3.1. getDerrivedStateFromProps的使用 -- (如果當(dāng)前的construtor中的state里面的數(shù)據(jù)永遠(yuǎn)依賴別的組件傳遞過來的,如果你同時希望發(fā)生變化的話那么你就可以使用這個函數(shù)來同步)

3.2 shouldComponentUpdate的使用 --(這個是決定我們的render函數(shù)到底需不需要渲染)

3.3 getSnapshotBeforeUpdate的使用 -- (這個可以獲取你更新之前的一些數(shù)據(jù)的)

  1. Constructor
    (1)如果不初始化state或不進(jìn)行方法綁定,則不需要為React組件實現(xiàn)構(gòu)造函數(shù)

    (2) Constructor
    -- 通過this.state賦值對象來初始化內(nèi)部的state
    -- 為事件綁定實例( this )

認(rèn)識組件的嵌套

import React, { Component } from 'react';

// Header
function Header() {
  return <h2> 我是Header </h2>
}



// main
// main 里面還有細(xì)節(jié)的拆分
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. 父組件通過 屬性 = 值的形式來傳遞給子組件數(shù)據(jù);

  2. 子組件通過props參數(shù)獲取父組件傳遞過來的數(shù)據(jù)

  3. 參數(shù)的驗證 對于傳遞子組件的數(shù)據(jù), 有的時候我們可能希望進(jìn)行驗證, 特別是對于大型項目來說
    -- 當(dāng)然,如果你的項目中默認(rèn)集成了Flow或者TypeScript,那么直接就可以進(jìn)行類型驗證
    -- 但是即使我們沒有使用 Flow或者TypeScript,也是可以通過prop-types庫來進(jìn)行參數(shù)驗證

    -- 從React v15.5開始, React.propTypes已移入另外一個包中:prop-types庫

<二>子傳父

  1. 在React中同樣的是通過 props 傳遞消息,只是讓父組件給子組件傳遞一個回調(diào)函數(shù),在子組件中調(diào)用這個函數(shù)即可

<三>非父子組件的傳遞

  1. React.createContext
    -- 創(chuàng)建一個需要共享的Context對象
    -- 如果一個組件訂閱了Context,那么這個組件會從離自身最近的那個匹配的Provider中讀取到當(dāng)前的context值;
    -- defaultValue是組件在頂層查找過程中沒有找到對應(yīng)的Provider,那么就使用默認(rèn)值

    const MyContext = React.createContext(defaultValue)
    
  2. Context.Provider
    --每個Context對象都會返回一個Provider React組件,它允許消費組件訂閱context 的變化
    --Provider接收一個value屬性,傳遞給消費組件
    --一個Provider可以和多個消費組件有對應(yīng)關(guān)系;
    --多個Provider也可以嵌套使用,里層覆蓋外層的數(shù)據(jù)
    --當(dāng)Provider的value值發(fā)生變化時,它內(nèi)部的所有消費組件都會被重新渲染

  3. class.contextType
    --掛載在class上的contextType屬性會被重新賦值為一個由React.createContext() 創(chuàng)建Context對象
    --這能讓你使用this.context來消費Context上的那個值
    --你可以在任何的生命周期訪問他,包括在render函數(shù)中也是可以的

  4. UserContext.Consumer
    -- 函數(shù)式的專用語法:
    <UserContext.Consumer>
    {
    value => {
    return (
    <div>
    <h2>用戶昵稱: {value.nikename}</h2>
    <h2>用戶等級: {value.level}</h2>
    </div>
    )
    }
    }
    </UserContext.Consumer>

為什么使用setState

  1. 因為修改了 state之后,希望React根據(jù)最新的State來重新渲染界面,但是這種方式的修改React并不知道數(shù)據(jù)發(fā)生變化,

  2. React并沒有實現(xiàn)類似于Vue的Object.defineProtype或者Vue3的Proxy的方式監(jiān)聽數(shù)據(jù)的變化

  3. 我們必須通過setState的方法告知數(shù)據(jù)已經(jīng)發(fā)生變化了

  4. 在組建中并沒有實現(xiàn)setState的方法,為什么可以調(diào)用了?
    --原因很簡單,setState方法是從Component中繼承過來的

  5. setSate 這個是一個異步的更新

  6. 為什么setState 設(shè)計為異步?
    -- https://github.com/facebook/react/issues/11527#issuecomment-360199710

    --總結(jié):
    1. setState設(shè)計為異步,可以顯著的提升性能;
    2. 如果每次調(diào)用setState都進(jìn)行一次更新,那么意味著render函數(shù)會被繁瑣調(diào)用,界面重新渲染,這樣效率很低;
    3. 最好的辦法應(yīng)該是獲取到多個更新,之后進(jìn)行批量的更新
    4. 如果同步更新了state,但是還沒有執(zhí)行render函數(shù),那么state和props不能保持同步
    5. state和props不能保持一致性,會在開發(fā)中產(chǎn)生很多問題
    PS: setState在那些情況是同步那些是異步呢?
    -- 在組件生命周期或React合成時間中,setState是異步;
    -- 在setTimeout或者原生dom事件中,setState是同步
    

React更新機制

-- 我們在前面已經(jīng)學(xué)習(xí)React 的渲染流程
JSX ---> 虛擬DOM --->真是DOM

-- 那么React的更新流程呢?
Props/state改變 --> render函數(shù)重新執(zhí)行 --> 產(chǎn)生新的DOM樹-->新舊DOM樹進(jìn)行diff -->計算出差異進(jìn)行更新-->更新到真實的DOM上

  1. 情況一: 當(dāng)節(jié)點為不同的元素,React會拆卸原有的樹,并且簡歷起新的樹;

-- 當(dāng)一個元素從<a> 變?yōu)?lt;img>,從<Article>變?yōu)?lt;Comment>,都會觸發(fā)一個完整的重建流程

-- 當(dāng)卸載一顆樹時,對應(yīng)的DOM節(jié)點也會被銷毀,組件實例將執(zhí)行componentWillUnmount() 方法
-- 當(dāng)簡歷一顆新的樹時,對應(yīng)的DOM節(jié)點會創(chuàng)建以及插入到DOM中,組件實例將執(zhí)行componentWillMount() 方法,緊接著componentDidMount()方法

比如下面代碼更改:
React會銷毀Counter組件并且重新裝載一個新的組件,而不會對Counter進(jìn)行復(fù)用

<div>
  <Counter />
</div>

<span>
  <Counter />
</span>

  1. 情況二:對比同一類型的元素
    -- 當(dāng)對比兩個相同的類型的React元素時,React會保留DOM節(jié)點,僅比對及更新與改變的屬性
    -- 比如下面的代碼更改
    通過比對這兩個元素,React知道需要修改DOM元素上的className;
<div className="before" title="stuff"></div>
<div className="after" title="stuff"></div>

比如下面的代碼更改:
當(dāng)更新style屬性時,React僅更新有所更變的屬性
通過比對這兩個元素時,React知道只需要修改DOM元素上的color樣式,無需修改fontWeight.

  1. 如果是同類型的組件元素:
    --組件會保持不變,React會更新改組件的props,并且調(diào)用componentWillReceiveProps()和componentWillUpdate()方法
    --下一步,調(diào)用render()方法,diff算法會將在之前的結(jié)果以及新的結(jié)果進(jìn)行遞歸

  2. 情況三:對子節(jié)點進(jìn)行遞歸
    --在默認(rèn)條件下,當(dāng)遞歸DOM節(jié)點的子元素時,React會同時遍歷兩個子元素的列表,當(dāng)產(chǎn)生差異時,生成一個mutation
    --前面兩個比較時完全相同的,所以不會產(chǎn)生mutation;
    --左后一個比較,產(chǎn)生一個mutation,將其插入到新的DOM樹中即可

<ul>
  <li>first</li>
  <li>second</li>
</ul>

 <ul>
  <li>first</li>
  <li>second</li>
  <li>third</li>
</ul>
  1. 但是如果我們是在中間插入一條數(shù)據(jù):
    React會對每個子元素一個mutation,而不是保持<li>星際穿越</li> 和<li>盜墓空間</li>的不變,這種低效的比較方式會帶來一定的性能問題
<ul>
  <li>星際穿越</li>
  <li>盜墓空間</li>
</ul>
<ul>
  <li>大話西游</li>
  <li>星際穿越</li>
  <li>盜墓空間</li>
</ul>

keys的優(yōu)化

我們在前面的遍歷列表時,總會提示一個警告,讓我們加入key屬性

  1. 方式一: 在最后面插入數(shù)據(jù)
    這種情況,有無key意義并不大

  2. 方式二: 在前面插入數(shù)據(jù)
    這種做法,在沒有key 的情況下,所有的li 都需要進(jìn)行修改

  3. 當(dāng)子元素(這里的li) 擁有key時,React使用key為111 和 222的元素僅僅進(jìn)行移位,不需要進(jìn)行任何的修改

將key為333 的元素插入到最前面的位置即可

  1. key的注意事項:
    -- key應(yīng)該是唯一的
    -- key不要使用隨機數(shù)(隨機數(shù)在下一次render時,會重新生成一個數(shù)字)
    -- 使用index作為可以,對性能是沒有優(yōu)化的

render 函數(shù)的調(diào)用

  1. 當(dāng)你有很多的組件都嵌套在App里面的時候 例如:
import React, { Component } from 'react';

class Banner extends Component {
  render() {
    return <h2>我是中間部分</h2>
  }
}
class HeaderBox extends Component {
  render () {
    return (
      <div>我是頭部標(biāo)簽</div>
    )
  }
}

function Main() {
  return (
    <div>
      <HeaderBox/>
      <Banner/>
    </div>
  )
}
class App extends Component {
  render() {
    return (
      <div>
        <Main/>
      </div>
    );
  }
}

export default App;
這樣的話就會你每次在點擊button + 1 的時候 他會把所有的組件都會重新的渲染一遍,這樣很影響性能, 所以就要用shouldComponentUpdata這個生命周期函數(shù)

PS: 這個生命周期不是隨便亂用的,這個是當(dāng)你需要阻斷更新的地方你就阻斷,不應(yīng)該阻斷的地方你就不要阻斷

shouldComponentUpdate生命周期的使用--這個是類特有的生命周期:

  1. React 給我們提供一個生命周期方法 shouldComponentUpdate(很多時候簡稱SCU),這個方法接受參數(shù),并且需要有返回值

  2. 該方法有兩個參數(shù)
    -- 參數(shù)一:nextProps修改之后,最新的props屬性
    -- 參數(shù)二:nextState修改之后,最新的state屬性

  3. 該方法返回的是一個Boolean類型
    -- 返回值為true,那么就需要調(diào)用render方法
    -- 返回值為false,那么就不需要調(diào)用render方法
    -- 默認(rèn)返回的是true,也就是只要state發(fā)生了變化,就會調(diào)用render方法

  4. 比如我們在App中增加一個message屬性
    --jsx中沒有依賴這個message,那么他的改變不應(yīng)該引起重新渲染
    -- 但是因為render監(jiān)聽到state的改變,就會重新render,所以最后render方法還是被重新調(diào)用了

如果每個組件都有這種情況的話,那是不是每個組件中都要寫shoundComponentUpdate呢?

不是的,你只需要繼承PureComponent 這個就可以解決所有的問題 (這個僅限用于類組件上面的,函數(shù)式組件是不行的需要用到 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這個生命周期函數(shù)
  // 這個生命周期不是隨便亂用的,這個是當(dāng)你需要阻斷更新的地方你就阻斷,
  // 不應(yīng)該阻斷的地方你就不要阻斷,所以這個地方你要做判斷
  // shouldComponentUpdate(nextProps,nextState) // 里面會有2個參數(shù)的
  // 最新的 nextProps 和最新的 nextState
  // 如果還有其他的需要通過的那么你就需要繼續(xù)在下面判斷了
  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函數(shù)調(diào)用");
    return (
      <div>
        <h2>當(dāng)前計數(shù): {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 這個就行了 (這個僅限用于類組件上面的,函數(shù)式組件是不行的需要用到 memo)

memo的使用:

// 1.導(dǎo)入memo 高階組件
import React, { PureComponent,memo } from 'react';

class Header extends PureComponent {
  render() {
    return <h2> 我是頭部標(biāo)簽 </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傳遞的數(shù)據(jù)需要不可變的數(shù)據(jù)

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}
                  年紀(jì):{item.age}
                  <button onClick={e => this.addNum(index)}>年齡+1</button>
                </li>
              )
            })
          }
        </ul>
        <button onClick={e => this.addData()}>添加數(shù)據(jù)</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. 需求 當(dāng)我在這個組件中點擊按鈕
    向上面的Home 組件傳遞一條數(shù)據(jù)過去
    然后在Home組件中展示出來
    解決方案:
    需要下載一個第三方庫 events --- > yarn add events
import React, { PureComponent } from 'react';

// 1.EventEmitter(事件發(fā)射)--> 這個是一個類
import { EventEmitter } from "events"
// 2.創(chuàng)建事件總線: event bus
// 像這樣的文件你是可以單獨的存在一個全局的文件中的
// 然后你再導(dǎo)入到你需要的文件中
const eventBus = new EventEmitter();

class Home extends PureComponent {
  // 5. 要想監(jiān)聽的話 你要在組件的生命周期里面去監(jiān)聽
  // componentDidMount(){} 要在掛載階段去監(jiān)聽你的事件
  // componentWillUnmount() {} 要在卸載過程去下載你的事件
  componentDidMount() {
    /**
     *  eventBus.addListener()方法里面是有2個參數(shù)的
     *  
     *  第一個參數(shù)是:監(jiān)聽兄弟組件傳遞過來的事件
     *  第二個參數(shù)是: 一個回調(diào)函數(shù)
     * 
     * **/ 
    // eventBus.addListener('sayHello',(...args)=>{
    //   console.log(args);
    // })

    eventBus.addListener('sayHello',this.handleSayHelloListenner)
   }
  componentWillUnmount() { 
    // 6. 當(dāng)組件即將小時的時候 你應(yīng)當(dāng)清楚監(jiān)聽事件
    // eventBus.removeListener()清除事件名稱
    eventBus.removeListener("sayHello",this.handleSayHelloListenner)
  }

  // 單獨定義這個事件
  handleSayHelloListenner(...args) {
    console.log(...args);

  }

  render() {
    return (
      <div>
        Home
      </div>
    )
  }
}
// 需求 當(dāng)我在這個組件中點擊按鈕 
// 向上面的Home 組件傳遞一條數(shù)據(jù)過去
// 然后在Home組件中展示出來

// 解決方案:
// 需要下載一個第三方庫 events --- > yarn add events
class Profile extends PureComponent {
  render() {
    return (
      <div>
        Profile
        {/* 3.發(fā)射一個事件出去 */}
        <button onClick={e => this.emitEvent()}>點擊了Profile按鈕</button>
      </div>
    )
  }
  emitEvent() {
    // 上面全局定義的一個eventBus對象 他new出來的有個事件叫emit
    // 4. emit() 方法里面 第一個參數(shù)是:事件名稱,第二個是: 傳遞的參數(shù)
    eventBus.emit("sayHello", 'hello,world', 123322)

  }
}

class App extends PureComponent {
  render() {
    return (
      <div>
        <Home />
        <Profile />
      </div>
    );
  }
}

export default App;

事件總線 總結(jié):

  1. 前端通過Context主要實現(xiàn)的是數(shù)據(jù)的共享, 但是在開發(fā)中如果有跨組件之間的時間傳遞,應(yīng)該如何操作呢?

(1) 在Vue中我們可以通過Vue的實例,快速實現(xiàn)一個事件總線(eventBus),來完成操作

(2) 在React中,我們可以依賴一個使用較多的庫events來完成對應(yīng)的操作

(3) 我們可以通過npm 或者yarn來安裝events

yarn add events

(4) events常用的API:
-- 創(chuàng)建EventEmitter對象: eventBus對象
const eventBus = new EventEmitter()

-- 發(fā)射事件: eventBus.emit("事件名稱",參數(shù)列表)
eventBus.emit("sayHello", 'hello,world', 123322)

-- 監(jiān)聽事件:events.addListener("事件監(jiān)聽",監(jiān)聽函數(shù))
 eventBus.addListener('sayHello',this.handleSayHelloListenner)

-- 移除事件:eventBus.removeListener("事件監(jiān)聽",監(jiān)聽函數(shù))
 eventBus.removeListener(sayHello",this.handleSayHelloListenner)

如何使用ref

  1. 在React的開發(fā)模式中,通常項情況下不需要,也不建議直接去操作DOM原生,但是某些特殊情況,確實需要獲取到DOM進(jìn)行某些操作

(1) 管理焦點,文本選擇或媒體播放
(2) 觸發(fā)強制動畫
(3) 繼承第三方DOM庫

  1. 如何創(chuàng)建refs來獲取對應(yīng)的DOM呢? 目前是有三種方式:
    (1) 方式一: 傳入字符串
    -- 使用時通過this.refs. 傳入的字符串格式獲取對應(yīng)的元素
import React, { PureComponent } from 'react';

class App extends PureComponent {
  componentDidMount() {
    // document.getElementById()  // 一般不推薦
  }
  render() {
    return (
      <div>
        {/* 1.方式一: ref=字符串/對象/函數(shù) */}
        <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() 方式創(chuàng)建出來的
-- 使用時獲取到創(chuàng)建的對象其中有一個current屬性就是對應(yīng)的元素


  // 1.導(dǎo)入一個 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) 方式三:傳入一個函數(shù)
-- 該函數(shù)會在DOM被掛載時進(jìn)行回調(diào), 這個函數(shù)會傳入一個 元素對象,我們可以自己保存;
-- 使用時,直接拿到之前保存的元素對象即可

import React, { PureComponent } from 'react';

class App extends PureComponent {
  constructor(props) {
    super(props)
    this.titleRef = null
  }
  render() {
    return (
      <div>
        {/* 1.傳入一個函數(shù)基本語法: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 的值根據(jù)節(jié)點的類型而有所不同
    -- 當(dāng)ref屬性用于HTML元素時.構(gòu)造函數(shù)中使用React.createRef()創(chuàng)建的ref接收底層DOM元素作為DOM 元素作為其current屬性
    -- 當(dāng)ref屬性用于自定義class組件是,ref對象接收組價的掛載實例作為其current屬性
    -- 你不能在函數(shù)組件上使用ref屬性.因為他們沒有實的
  2. 函數(shù)式組件是沒有實例的,所以無法通過ref獲取他們的實例
    -- 但是某些時候,我們可能想要獲取函數(shù)式組件中的某個DOM元素
    -- 這個時候我們可以通過React.forwardRef,后面我們也會學(xué)習(xí)hooks中如何使用ref

認(rèn)識受控組件

  1. 在React中,HTML表單的處理方式和普通的DOM元素不太一樣,表單元素通常會保存在一些內(nèi)部的state

  2. 比如下面的HTML表單元素
    -- 這個處理方式是DOM默認(rèn)的處理HTML表單的行為, 在用戶點擊提交時會提交到某個服務(wù)器中, 并且刷新頁面
    -- 在React中,并沒有禁止這個行為,他依然是有效的
    -- 但是嘗嘗情況下使用JavaScript函數(shù)來方便的處理表單提交,同時還可以訪問影虎填寫的表單數(shù)據(jù)
    -- 實現(xiàn)這種效果的標(biāo)準(zhǔn)方式是使用 "受控組件"

<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;

認(rèn)識高階函數(shù)

認(rèn)識高階組件

  1. 高階組件的英文名叫Higher-Order Components,簡稱HOC

  2. 官方的定義:高階組件是參數(shù)為組件,返回值為新組件的函數(shù)

  3. 首先,高階組件 本身不是一個組件,而是一個函數(shù)

  4. 其次,這個函數(shù)的參數(shù)是一個組件, 返回值也是一個組件

  5. 高階組件的調(diào)用類似于這樣:
    const EnhancedComponent = higherOrderComonent(WrappedComponent)

Portals 的使用

  1. 在某些情況下,我們希望渲染的內(nèi)容獨立于父組件,甚至是獨立于當(dāng)前掛載到的DOM元素中,(默認(rèn)都是掛載到id為root的DOM元素上的)

  2. Portal 提供了一種將子節(jié)點渲染到存在父組件以外的DOM節(jié)點的優(yōu)秀的案例
    --第一個參數(shù)(child) 是任何可渲染的React子元素,例如一個元素,字符串或fragment;

-- 第二個參數(shù)是(container) 是一個DOM元素

ReactDOM.createPortal(child,container)
  1. 通常來講,當(dāng)你從組建的render方法返回一個元素時,該方法被掛載到DOM節(jié)點中離其最近的父節(jié)點:

  2. 然而,有時候?qū)⒆釉夭迦氲紻OM節(jié)點中的不同位置也是有好處的

render() {
  // React 掛載一個新的div, 并且把子元素渲染其中
  return(
    <div>
    {this.props.children}
    </div>
  )
}
render() {
  // React 并沒有創(chuàng)建一個新的div 它只是把子元素渲染到'DOMNode'中
  return ReactDOM.createPortal(
    this.props.children,
    domNode
    )
}

Modal 組建案例

  1. 比如說,我們準(zhǔn)備開發(fā)一個Modal組件, 他可以將他的子組件渲染到品目的中間位置:

fragment的使用

// 1.fragment的使用 導(dǎo)入Fragment,類似于小程序中的 block 標(biāo)簽
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標(biāo)簽,類似于小程序中的 block 標(biāo)簽  
      // <Fragment>
      //   <h2>當(dāng)前計數(shù):{ this.state.count }</h2>
      //   <button onClick={e=> this.addNum()}> + 1 </button>
      // </Fragment>

      // 或者還可以這樣寫de
      <>
        <h2>當(dāng)前計數(shù):{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 是一個用來突出顯示應(yīng)用程序中潛在問題的工具
    -- 與Fragment一樣,StrictMode不會渲染任何可見的UI;
    --它為其后代元素觸發(fā)額外的檢查和警告
    --嚴(yán)格模式檢查僅在開發(fā)模式下運行;他們不會影響生產(chǎn)構(gòu)建

  2. 可以為應(yīng)用程序的任何部分啟用嚴(yán)格模式
    --不會對Header和Footer組件運行嚴(yán)格模式檢查
    --但是,ComponentOne 和 ComponentTwo以及他們的所有后代元素都將進(jìn)行檢查
    <Header />
    <React.StrictMode>
    <div>
    <ComponentOne />
    <ComponentTwo />
    </div>
    </React.StrictMode>
    <Footer />

  3. 嚴(yán)格模式檢查什么?
    --1. 識別不安全的生命周期
    --2. 使用過時的ref API
    --3. 檢查意外的副作用
    --4. 使用廢棄的findDOMMNode

組件化開發(fā)的CSS

  1. 局部CSS的樣式
  2. 動態(tài)的CSS的書寫
  3. 支持所有的CSS 特性
  4. 避免樣式的全局污染

React 中的四種CSS樣式的編寫

1.1. 內(nèi)聯(lián)樣式的寫法
-- style 接收一個采用小駝峰命名的屬性的JavaScript對象,而不是CSS字符串
-- 并且可以引用state中的狀態(tài)來設(shè)置相關(guān)的樣式
1.2. 內(nèi)聯(lián)樣式的優(yōu)點
-- 1. 內(nèi)聯(lián)樣式,樣式之間不會有沖突
-- 2. 可以動態(tài)獲取當(dāng)前state中的狀態(tài)

1.3. 內(nèi)聯(lián)樣式的缺點:
--寫法上都需要使用駝峰標(biāo)識
--某些樣式?jīng)]有提示
--大量的樣式,代碼混亂
--某些樣式無法編寫(比如偽類/偽元素)

  1. 使用普通的css來寫,導(dǎo)入到各個JS 文件中
    -- 缺點: 會覆蓋和層疊其他文件中的css樣式
  1. CSS modules
    (1) CSS modules 并不是React特有的解決方案,而是所有使用了類似于webpack配置的環(huán)境下都可以使用的.
    --但是,如果在其他項目中使用,那么我們需要自己來進(jìn)行配置,比如配置webpack.config.js中的modules:true等.

    (2) React的腳手架已經(jīng)內(nèi)置了css modules 的配置
    --.css/.less/.scss等樣式文件都修改成.module.css/.module.less/.module.scss等

    (3) 這樣寫的話 就可以完美的解決層疊問題和樣式覆蓋問題的

    (4) 優(yōu)點 確實解決了局部的作用域問題,

    (5) 缺點
    --引用的類名,不能使用連接符比如(.home-title),在JavaScript中是不識別的
    --所有的className都必須使用(style.classname)的形式來編寫
    --不方便動態(tài)來修改某些樣式,依然需要使用內(nèi)聯(lián)樣式的方式

  1. CSS in JS
    (1) "CSS-in-JS" 是一種模式,其中CSS由JavaScript生成而不是在外部定義的
    (2) 注意此功能并不是React的一部分,而是由第三方庫提供,React對樣式如何定義并沒有明確態(tài)度
    (3) 事實上CSS-in-JS的模式就是一種將樣式(CSS) 也寫入到JavaScript中的方式,并且可以方便的使用JavaScript的狀態(tài),所以有被人稱之為 All in JS

  2. 認(rèn)識styled-components
    (1) CSS-in-JS通過JavaScript來為CSS賦予一些能力,包括類似于CSS預(yù)處理器一樣的樣式嵌套,函數(shù)定義,邏輯復(fù)用,動態(tài)修改狀態(tài)..
    (2) 依然CSS預(yù)處理器也是具備某些能力,但是獲取動態(tài)狀態(tài)依然是一個不好處理的點;
    (3) 目前CSS-in-JS可以說是React最受歡迎的一種解決方案

    (4) 目前比較流行的CSS-in-JS 的庫有哪些呢?
    --styled-components
    --emotion
    --glamorous
    (5) 目前可以說 styled-components依然是社區(qū)最流行的CSS-in-JS

    (6) 安裝styled-components

    yarn add styled-components
    

React 中 編寫className的寫法 React中添加Class :

  1. React在JSX給了我們開發(fā)者足夠多的靈活性, 你可以像JavaScript代碼一樣,通過一些邏輯來決定時候添加某些class:
{/* 這些都是原生的React添加class 的方法 */}

<h2 className={"foo bar active title"}>我是標(biāo)題一</h2>
{/* 這種寫 如果title的后面或者 active的前面不加一個空格的話就會連到一起,這種寫不是很方便 */}
<h2 className={"title " + (isActive ? "active" : "")}> 我是標(biāo)題二 </h2>

{/* 這樣的話比較清楚,為什么呢? 這個地方是可以寫一個數(shù)組的 */}
<h2 className={["title", (isActive ? "active" : "")].join(" ")}>我是標(biāo)題三</h2>
  1. 這個時候我們就可以借助第三方庫:classnames
    -- 很明顯,這個是一個用于動態(tài)添加className的一個庫

  2. 庫的安裝:

yarn add classname

classNames 庫的具體用法:

import React, { PureComponent } from 'react';
/**
 * 
 * 
 * 
 *   1. React在JSX給了我們開發(fā)者足夠多的靈活性, 你可以像JavaScript代碼一樣,通過一些邏輯來決定時候添加某些class:
 *   這些都是原生的React添加class 的方法

 *  <h2 className={"foo bar active title"}>我是標(biāo)題一</h2>

 *  這種寫 如果title的后面或者 active的前面不加一個空格的話就會連到一起,這種寫不是很方便
 *  <h2 className={"title " + (isActive ? "active" : "")}> 我是標(biāo)題二 </h2>
 * 
 *  這樣的話比較清楚,為什么呢? 這個地方是可以寫一個數(shù)組的 
 *  <h2 className={["title", (isActive ? "active" : "")].join(" ")}>我是標(biāo)題三</h2>

 *  2. 這個時候我們就可以借助第三方庫:classnames
 *  -- 很明顯,這個是一個用于動態(tài)添加className的一個庫

 *  3. 庫的安裝命令:
 *  yarn add classname

 * 
 * **/

// 1. 現(xiàn)下載安裝 className庫 yarn add classname

// 2. 導(dǎo)入 
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; // 這個不會被加進(jìn)去
    const undiClass = undefined; // 這個不會被加進(jìn)去
    const zreo = 0; // 這個不會被加進(jìn)去
    const tenNum = 10 // 數(shù)字為真的時候會被加進(jìn)去
    return (
      <div>
        {/* 這些都是原生的React添加class 的方法 */}

        <h2 className={"foo bar active title"}>我是標(biāo)題一</h2>
        {/* 這種寫 如果title的后面或者 active的前面不加一個空格的話就會連到一起,這種寫不是很方便 */}
        <h2 className={"title " + (isActive ? "active" : "")}> 我是標(biāo)題二 </h2>

        {/* 這樣的話比較清楚,為什么呢? 這個地方是可以寫一個數(shù)組的 */}
        <h2 className={["title", (isActive ? "active" : "")].join(" ")}>我是標(biāo)題三</h2>

        <hr />

        {/* classnames庫添加class 語法使用如下: */}
        <h2 className={"foo bar active title"}>我是標(biāo)題一</h2>
        <h2 className={classNames("foo", "bar", "active")}>我是標(biāo)題四</h2>

        {/* 如果有些屬性是必須要加的話,你就寫在對象的外面,如下:"title" */}
        <h2 className={classNames({ "active": isActive, "bar": isBar }, "title")}>我是標(biāo)題五</h2>

        {/* 可以跟變量 */}
        <h2 className={classNames("foo", errorClass, warnClass, undiClass, zreo, tenNum, { "active": isActive })}>我是標(biāo)題六</h2>
        <h2 className={classNames(["active", "title"])}>我是標(biāo)題七</h2>
        <h2 className={classNames(["active", "title", { "active": isActive }])}>我是標(biāo)題四</h2>
      </div>
    );
  }
}
export default App;

AntDesign 的使用

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

  2. Antd 是否有多余的代碼邏輯添加進(jìn)來呢?
    不會的

認(rèn)識craco

  1. 想要修改create-react-app 的默認(rèn)配置可以使用:
yarn run eject 可以把信息全部展示出來,但是這種不太推薦
  1. 推進(jìn)的修改 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');
// 導(dǎo)入當(dāng)前路徑path 模塊, 這個就是node_modules里面自帶的一個模塊路徑
const path = require('path')
// __dirname:當(dāng)前頁面的路徑,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"), // @:這個意思就是代表當(dāng)前路徑所在的 src的這個路徑
      "component":resolve("src/component"), // component就是當(dāng)前所在的路徑 所在的src下面的component,如果以后寫component就是找src/component路徑了

    }
  }
};

axios 的網(wǎng)絡(luò)請求

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

React-transition-group介紹

  1. React社區(qū)為我們提供了react-transition-group用來過渡完成動畫

  2. 這個是需要安裝一個插件的,安裝命令:

    npm 安裝

    npm install react-transition-group --save

    yarn

    yarn add react-transition-group

  3. react-transition-group 主要包含四個組件:
    -- Transition
    該組件是一個和平臺無關(guān)的組件(不一定要結(jié)合CSS);
    一般是結(jié)合CSS來完成樣式, 比較常用的是CSSTransition

    -- CSSTransition
    在前端開發(fā)中,通常使用的是CSSTransition來完成過渡動畫效果

    --SwitchTransition
    兩個組件顯示和影藏切換時,使用該組件

    --TransitionGroup
    將多個動畫組件包裹在其中, 一般用于列表中元素的動畫

Redux的核心理念-reducer

  1. reducer 是一個純函數(shù)

  2. reducer做的事情就是將傳入的state和action結(jié)合起來生成一個新的state

  3. Redux的三大原則
    --整個應(yīng)用程序的state被儲存在一個object tree中,并且這個object tree只儲存在一個store中;
    --Redux并沒有強制讓我們不能創(chuàng)建多個Store,但是那樣做并不利于數(shù)據(jù)的維護(hù)
    --單一的數(shù)據(jù)源可以讓整個應(yīng)用程序的state變得方便維護(hù),追蹤,修改

  4. State是只讀的
    --唯一修改state的方法一定是觸發(fā)action,不要試圖在其他地方通過任何的方式來修改State
    --這樣就確保了View或網(wǎng)絡(luò)請求都不能直接修改state,他們只能通過action來描述自己想要如何修改state
    --這樣可以保證所有的修改都被集中化處理,并且按照嚴(yán)格的順序執(zhí)行,所以不需要擔(dān)心race condition 的問題

  5. 使用純函數(shù)來執(zhí)行修改
    -- 通過reducer將舊state和actions聯(lián)系在一起,并且返回一個新的State
    --隨著應(yīng)用程序的復(fù)雜度增加, 我們可以將reduce拆分成多個小的reducers,分別操作不同state tree的一部分
    --但是所有的reducer都應(yīng)該是純函數(shù), 不能產(chǎn)生任何的副作用;

  6. redux三個核心的東西就是: reducer store actios
    --把所有的state儲存在store中 想要修改的話 必須要通過 actions 然后通過reducer來進(jìn)行視圖的更改, 但是所有的reducer都應(yīng)該是純函數(shù),所有的數(shù)據(jù)應(yīng)該只存在一個store中

React-router 路由

  1. 安裝react-router會自動幫助我們安裝react-router的依賴 命令如下:
yarn add react-router-dom
  1. react-router最主要的API是給我們提供的一些組件
    (1) BrowserRouter或HashRouter
    --Router中包含了對路徑的改變的監(jiān)聽,并且將會相應(yīng)的路徑傳遞給子組件
    --BrowserRouter使用history模式
    --HashRouter使用hash模式
    (2) Link和NavLink
    --通常路徑的跳轉(zhuǎn)是使用Link組件,最終會被渲染成a 元素
    --NavLink是在Link基礎(chǔ)之上增加了一些樣式屬性
    --to屬性:LInk最重要的屬性,用于設(shè)置跳轉(zhuǎn)到的路徑
    (3) Route:
    --Route用于路徑的匹配
    --path屬性:用于設(shè)置匹配到的路徑
    --component屬性:設(shè)置匹配到路徑后,渲染組件
    --exact:精準(zhǔn)匹配,只有精準(zhǔn)匹配到完全一致的路徑,才會渲染對應(yīng)的組件
最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時請結(jié)合常識與多方信息審慎甄別。
平臺聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點,簡書系信息發(fā)布平臺,僅提供信息存儲服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

  • HTML模版 之后出現(xiàn)的React代碼嵌套入模版中。 1. Hello world 這段代碼將一個一級標(biāo)題插入到指...
    ryanho84閱讀 6,445評論 0 9
  • —— 基礎(chǔ)知識、JSX介紹、React 元素、組件和屬性、狀態(tài)和生命周期 此文檔來自 React 官方文檔,在英文...
    thelastcookies閱讀 525評論 0 1
  • GUIDS 第一章 為什么使用React? React 一個提供了用戶接口的JavaScript庫。 誕生于Fac...
    jplyue閱讀 3,710評論 1 11
  • 自己最近的項目是基于react的,于是讀了一遍react的文檔,做了一些記錄(除了REFERENCE部分還沒開始讀...
    潘逸飛閱讀 3,741評論 1 10
  • 這里是一些使用React時候需要注意的一些點,你可以在看的時候自行加上“為什么?”“React是不可以...?”這...
    不安分的程序員天朗閱讀 433評論 0 2

友情鏈接更多精彩內(nèi)容