欧美free性护士vide0shd,老熟女,一区二区三区,久久久久夜夜夜精品国产,久久久久久综合网天天,欧美成人护士h版

首頁綜合 正文
目錄

柚子快報邀請碼778899分享:前端 React 學習筆記

柚子快報邀請碼778899分享:前端 React 學習筆記

http://yzkb.51969.com/

React

一、React的簡介

1、介紹

React 是一個用于構建用戶界面的 JAVASCRIPT 庫。 React主要用于構建UI,很多人認為 React 是 MVC 中的 V(視圖)。 React 起源于 Facebook 的內部項目,用來架設 Instagram 的網站,并于 2013 年 5 月開源。 React 擁有較高的性能,代碼邏輯非常簡單,越來越多的人已開始關注和使用它

2、特點

1). 高效 ?React通過對DOM的模擬,最大限度地減少與DOM的交互。

2). 靈活 ?React可以與已知的庫或框架很好地配合。

3). JSX ? JSX 是 JavaScript 語法的擴展。React 開發(fā)不一定使用 JSX ,但我們建議使用它。

4). 組件 ? 通過 React 構建組件,使得代碼更加容易得到復用,能夠很好的應用在大項目的開發(fā)中。

單向響應的數據流 ? React 實現了單向響應的數據流,從而減少了重復代碼,這也是它為什么比傳統(tǒng)數據綁定更簡單。

3、框架對比

與其他框架的共同點是,都采用虛擬dom,和數據驅動

angularJsreactJsvueJs控制器√--過濾器√-√(vue2有,vue3沒有)指令√-√模板語法√-√服務√--組件-√√jsx-√2.0之后加入

二、環(huán)境搭建

1、引入文件的方式(CDN)

1、React.js:

React的核心庫,解析組件,識別jsx

https://cdn.staticfile.org/react/16.4.0/umd/react.development.js

2、reactDom.js:

處理有dom相關的操作

https://cdn.staticfile.org/react-dom/16.4.0/umd/react-dom.development.js

3、Babel.js

Babel 可以將 ES6 代碼轉為 ES5 代碼,這樣我們就能在目前不支持 ES6 瀏覽器上執(zhí)行 React 代碼。Babel 內嵌了對 JSX 的支持。通過將 Babel 和 babel-sublime 包(package)一同使用可以讓源碼的語法渲染上升到一個全新的水平

https://cdn.staticfile.org/babel-standalone/6.26.0/babel.min.js

2、官方腳手架(模塊化)

安裝 create-react-app

yarn global add create-react-app | npm install create-react-app -g

創(chuàng)建 react項目

create-react-app 目錄 | npx create-react-app 目錄 | npm init react-app 目錄

yarn eject ? 解構出所有的配置文件 可選

yarn start | ?npm start 開發(fā)

yarn build | ?npm run build 打包

?

//調試 需要安裝給chrome瀏覽器一個插件 react-dev-tools

環(huán)境解析

react: 核心包,解析組件,識別jsx react-dom: 編譯 -> 瀏覽器 react-scrpts: react的項目環(huán)境配置 manifest.json 生成一個網頁的桌面快捷方式時,會以這個文件中的內容作為圖標和文字的顯示內容 registerServiceWorker.js支持離線訪問,所以用起來和原生app的體驗很接近,只有打包生成線上版本的react項目時,registerServiceWorker.js才會有效。服務器必須采用https協(xié)議 對Internet Explorer 9,10和11的支持需要polyfill。

第三方腳手架

yeomen/dva/umi

第一個React程序

Hello?React!

? ?

? ?

注意:

1、script標簽的type取值為:text/babel

2、在script標簽里寫的html代碼,不用雙引號或者單引號,這種寫法是下面要講解的jsx。

總結:一個react的程序,就是把JSX通過ReactDOM.render()函數渲染到網頁上。

另外,React的代碼也可以單獨寫在一個文件里,擴展名為js,或者jsx。引入時,記住type=“text/babel”

JSX

1、JSX的介紹

什么是JSX:JSX=javascript xml,就是Javascript和XML結合的一種格式。是 JavaScript 的語法擴展,換句話說:只要你把HTML代碼寫在JS里,那就是JSX。

在實際開發(fā)中,JSX 在產品*打包階段*都已經編譯成純 JavaScript,不會帶來任何副作用,反而會讓代碼更加直觀并易于維護。官方定義是:類 XML 語法的 ECMAScript 擴展。

2、特點:

JSX 執(zhí)行更快,因為它在編譯為 JavaScript 代碼后進行了優(yōu)化。

它是類型安全的,在編譯過程中就能發(fā)現錯誤。

使用 JSX 編寫模板更加簡單快速。

3、JSX的語法

JSX就是把html代碼寫在javascript中,那么,寫在javascript中有啥要求(與原來在html中的區(qū)別),這就是jsx語法要說的內容。

示例:

var msg="哥們!"

?

const element =

Hello, world {msg}

? ? 沒有雙引號,不是字符串

?

const List = () => (

?

? ? ? ?

    ?

    ? ? ? ? ?

  • list item
  • ?

    ? ? ? ? ?

  • list item
  • ?

    ? ? ? ? ?

  • list item
  • ?

    ? ? ? ?

?

?

); ?

XML基本語法

只能有一個根標簽,養(yǎng)成外面加上圓括號的習慣。 標簽要閉合(單邊標簽得有斜杠)

元素類型

小寫首字母對應 HTML的標簽,組件名首字母大寫。 注釋使用 / * 內容 */ html標簽內注釋{/* 最外層有花括號*/}

元素屬性

內聯(lián)樣式的style: style屬性不能直接按照html的寫法寫。應該用 JavaScript的寫法(json對象)。 樣式名以駝峰命名法表示, 如font-size需寫成fontSize。默認像素單位是 px(px不用寫) let _style = { backgroundColor:"red" };

ReactDOM.render(

? ?

Hello, world!

,

? ?document.getElementById('box')

); 外部樣式的class:HTML中曾經的 class 屬性改為 className(因為class是js的關鍵字),外部樣式時使用 ReactDOM.render(

?

Hello, world!

,

? document.getElementById('box')

); for 屬性改為 htmlFor(因為for是js的關鍵字)。(for屬性在html標簽中是擴充單選框|復選框的選擇范圍) 事件名用駝峰命名法。HTML是全小寫(onclick),JSX中是駝峰(onClick)

javascript表達式

使用單花括號。 react里沒有vue中的指令,在JSX的任何地方需要javascript的變量,表達式等等,只需要套上 單花括號就行。 const element =

Hello, {120+130}!

const element =

Hello, {getName("張三瘋")}!

注意:單花括號里只能寫表達式,不能寫語句(如:if,for)

總結:

對比原生,JSX相當于把原生里的html和javascript代碼混寫在一起,

vue中有很多的指令,react中只需要使用單花括號就行。

ReactDOM.render()函數

ReactDOM.render 是 React 的最基本方法。用于將JSX寫的模板(經過模板渲染(把javascript的結果替換單花括號里的內容))轉為 HTML 語言,并渲染到指定的HTML標簽里。

ReactDOM.render( JSX寫的html模板,dom容器對象);

總結:一個react的程序,就是把JSX通過ReactDOM.render()函數渲染到網頁上。

程序員完成的是JSX的編寫。

條件渲染

var sex='女';

if(sex=='男'){

var sexJSX=

我是男的

;

}else{

var sexJSX=

我是女的

;

}

?

ReactDOM.render(

    {sexJSX}

,

document.getElementById('box')

);

注意:if語句不要寫在單花括號里。

列表渲染

1)、渲染數組:

//數組里存放jsx

var arr=[

? ?

  • 張三瘋
  • ,

    ? ?

  • 張四瘋
  • ,

    ? ?

  • 張五瘋
  • ]

    ?

    const show = ()=> (

    ? ?

      {arr}

    )

    ReactDOM.render(show(),document.getElementById("box"));

    2)使用Javascript的map()或者普通for循環(huán)進行列表的渲染

    //普通for循環(huán)

    ?

    let arr = ["鉛筆","油筆","鋼筆","毛筆"];

    var arr2 =[];

    for(let i in arr){

    arr2.push(

  • {arr[i]}
  • );

    }

    ?

    const show = ()=> (

      {arr2}

    )

    ?

    ReactDOM.render(show(),document.getElementById("box"));

    ?

    //map

    const goods = ['鉛筆','鋼筆'];

    const goodsJSX = goods.map(function(val,index){

    return

  • {val}
  • });

    ?

    ReactDOM.render(

    ? ?//以下相當于react里的模板,不能出現js的語句,可以有表達式

      {goodsJSX}

    ,

    document.getElementById('box')

    );

    組件:定義

    react中定義組件有三種寫法:函數方式,ES5的寫法(現在不用了),ES6(類)的寫法

    函數方式的組件

    函數的返回值是JSX就行。即就是:如果一個函數的返回值是JSX,那么就可以當標簽的方式使用。

    // 定義組件,組件名首字母大寫(大駝峰,就是PascalCase)

    function MyCom(){

    const msg="hello";

    return (

    • {msg}:三國演義
    • {msg}:紅樓夢

    )

    }

    ReactDOM.render(

    ? ? ? {MyCom()}

    ? ?, //使用組件

    ? ?document.getElementById('box')

    );

    ES5的寫法:

    React.CreateClass()函數(React16后,已經被廢棄了)

    var MyCom = React.createClass({ ?

    ?render:function(){ //vue中也有render函數,是完成模板的代碼

    ? ?return (

    ? ? ?

    ? ? ? ?

    Hello, world!

    ? ? ?

    ? );

    }

    });

    ES6類定義組件:

    定義一個類,繼承自 React.Component,并且,在該類里,必須有個render()函數,render函數返回一個JSX代碼。

    換句話說:一個普通的ES6的類,繼承自React.Component,有一個render()函數,并且render()函數返回一個JSX,那么就是組件。

    // 定義組件

    class MyCom extends ?React.Component{

    constructor(props){ //props:是外部傳入的數據,相當于vue中的props

    super(props);

    // state是內部的數據,相當于vue中的date

    this.state={

    name:"田哥"

    }

    }

    ?

    render(){

    const msg="hello";

    return (

    • {this.state.name}:三國演義
    • {msg}:紅樓夢

    )

    }

    }

    多個組件

    // 標題

    function MyTitle(){

    return (

    商品列表

    )

    }

    // 詳情

    function MyDetail(){

    return (

    • 鉛筆
    • 鋼筆

    )

    }

    ReactDOM.render(


    ,

    ? ?document.getElementById('box')

    );

    組件:props

    props 是組件對外的接口。接收外部傳入的數據。是組件的屬性(等同于html標簽的屬性)。 注意:Props對于使用它的組件內部來說,是只讀的。一旦賦值不能修改。

    外部傳值:

    <組件名 屬性名1=值1 屬性名2=值2 .. />

    屬性值="靜態(tài)值"

    屬性值={js數據}

    組件內部使用:

    1)、函數組件:

    {props.屬性名}

    示例:

    function MyPerson(props){

    return (

    {props.name}

    {props.sex}

    )

    }

    ?

    ReactDOM.render(

    ,

    document.getElementById('box')

    );

    2)、類組件:

    {this.props.屬性名}

    示例:

    class MyPerson extends React.Component{

    render(){

    return (

    {this.props.name}

    {this.props.sex}

    )

    }

    }

    ?

    ReactDOM.render(

    ,

    document.getElementById('box')

    );

    補充:如果傳遞數據多的話,可以使用對象,但是必須使用擴展運算符(...)

    擴展運算符: ES6增加了擴展運算符: ... 三個點是ES幾的_?. 是es幾的-CSDN博客

    class MyPerson extends React.Component{

    // constructor(props){

    // super(props);

    // }

    render(){

    return (

    {this.props.name}

    {this.props.sex}

    )

    }

    }

    ?

    let person={

    name:"張三瘋",

    sex:"男"

    }

    ?

    ReactDOM.render(

    ,

    document.getElementById('box')

    );

    props的默認值

    1)、用 ||

    function MyPerson(props){

    let sex1 = props.sex || "女";

    return (

    性別:{sex1}

    )

    }

    ReactDOM.render(

    ,

    document.getElementById('box')

    );

    2)、defaultProps

    格式:

    ?

    //1)、函數式組件和類組件都可以:

    ?

    組件名.defaultProps={

    ? ?屬性名: 默認值

    }

    ?

    //2)、若為類組件,也可以在類的內部使用static修飾(把以下代碼要寫在類里面。

    ?

    static defaultProps={

    ? ?屬性名: 默認值

    }

    ?

    示例:

    function MyPerson(props){

    let sex1 = props.sex || "女";

    return (

    姓名:{props.name}

    性別:{sex1}

    )

    }

    ?

    MyPerson.defaultProps={

    name:"無名氏"

    }

    ?

    ReactDOM.render(

    ,

    document.getElementById('box')

    );

    ?

    props的類型檢查:

    注意:react15.5后,React.propTypes已經移入到另外一個庫里,請使用prop-types。

    https://cdn.staticfile.org/prop-types/15.6.1/prop-types.js

    ?

    //類型約定:

    組件名.propTypes={

    ? ?屬性名1:PropTypes.類型名,

    ? ?屬性名2:PropTypes.類型名

    ? ?//必傳參數

    屬性名: PropsTypes.類型名.isRequired

    }

    ?

    類型名的可以取值為:

    PropTypes.array, PropTypes.bool, PropTypes.func, PropTypes.number, PropTypes.object, PropTypes.string, PropTypes.symbol

    如:

    function MyPerson(props){

    return (

    年齡:{props.age}

    )

    }

    ?

    MyPerson.propTypes={

    ? ?//注意大小寫

    age:PropTypes.number.isRequired

    }

    ?

    ReactDOM.render(

    ,

    document.getElementById('box')

    );

    類組件:state狀態(tài)機

    state 是狀態(tài),狀態(tài)就是數據,state表示組件的內部數據(相當于vue組件中的data)。而props是外部傳入組件的數據,類組件可以直接使用state,但是函數式組件就得用hooks(useState)

    定義并初始化

    class App extends React.Component {

    ?constructor(props){ ?

    ? ? ?super(props); ?

    ? ? ?//在react中,如果希望狀態(tài)的變化會引起其它數據或者界面的變化,那么,把數據定義在state里。對應著vue中的data。

    ? ? ?this.state={

    ? ?//設置狀態(tài)的初始值

    ? ? ? ?屬性名:屬性值

    ? ? }

    }

    }

    讀取狀態(tài)

    this.state.屬性名

    修改狀態(tài)

    必須調用setState()函數,不要直接賦值,而且,setState()函數是異步的。

    這是因為react框架的響應式原理和vue框架的響應式原理根本就不一樣。

    1)、react框架的setState方法內部會調用render函數,這樣就實現了重新渲染

    2)、vue框架用的是數據劫持和觀察者模式。

    1、修改狀態(tài)時,必須要調用setState。因為,只要調用了setState函數,那就會調用了render()。如果直接賦值,不會把數據響應式地渲染到DOM上(即:沒有調render()函數)

    class Book extends React.Component{ ? ? ? ?

    ?

    ? ? ? ?constructor(props){

    ? ? ? ? ? ?super(props);

    ? ? ? ? ? ?this.state = {

    ? ? ? ? ? ? ? ?copyright:"版權歸2011"

    ? ? ? ? ? }

    ?

    ? ? ? ? ? ?// 千萬不能這么干,因為,數據修改后,不會響應式的呈現頁面上(不會再次渲染DOM)

    ? ? ? ? ? ?// this.copyright="版權歸2011";

    ?

    ? ? ? ? ? ?// 下面這句話,可以先不管

    ? ? ? ? ? ?this.changeVal = this.changeVal.bind(this);

    ? ? ? }

    ?

    ? ? ? ?changeVal(){

    ? ? ? ? ? ?// this.copyright = "哈哈哈";

    ? ? ? ? ? ?// console.log("this.copyright ",this.copyright);

    ?

    ? ? ? ? ? ?// 這不行,這個不會把數據響應式地渲染到DOM上。(即:沒有調render()函數)

    ? ? ? ? ? ?// this.state.copyright = "哈哈哈";

    ?

    ? ? ? ? ? ?// 這 行,因為,只要調用了setState函數,那就會調用了render();

    ? ? ? ? ? ?this.setState({

    ? ? ? ? ? ? ? ?copyright:"哈哈哈"

    ? ? ? ? ? });

    ? ? ? }

    ?

    ? ? ? ?render=()=>{

    ? ? ? ? ? ?console.log("render");

    ? ? ? ? ? ?return (

    ? ? ? ? ? ? ? ?

      ? ? ? ? ? ? ? ? ? ?

    • 書名:{this.props.name}
    • ? ? ? ? ? ? ? ? ? ?

    • 書齡:{this.props.age}
    • ? ? ? ? ? ? ? ? ? ?

    • {this.state.copyright}
    • ? ? ? ? ? ? ? ? ? ?

    • {this.copyright}
    • ? ? ? ? ? ? ? ? ? ?

      ? ? ? ? ? ? ? ?

    ? ? ? ? ? )

    ? ? ? }

    ? }

    ?

    ? ?Book.propTypes = {

    ? ? ? ?"name":PropTypes.string.isRequired,

    ? ? ? ?"age":PropTypes.number

    ? }

    ?

    ? ?const jsx =

    ?

    ? ?ReactDOM.render(jsx,document.getElementById("box"));

    //淺合并state

    this.setState({

    屬性名:屬性值

    })

    ?

    示例代碼:

    //1、假設定義的狀態(tài)是:

    this.state = {

    ? ?person:{

    ? ? ? ?name:"魏鵬",

    ? ? ? ?sex:"男"

    ? }

    }

    //2、如果修改時,使用下面的方式,肯定不行。因為,是淺合并,不會進入到下一級的屬性進行對比

    this.setState({

    ? ?person:{

    ? ? ? ?name:"鵬鵬"

    ? }

    })

    ?

    vue和react在響應式上的區(qū)別:

    1、vue:背后使用了數據劫持和觀察者模式

    2、react,修改數據時,調用setState,setState內部調用了render。

    2、setState()函數是異步的

    //改變狀態(tài)前想做一些事情:

    this.setState((prevState,prevProps)=>{

    ?//一般是用于在setState之前做一些操作,this.state==prevState==修改之前的state

    ? ?

    ? return {

    ? ?sname:value

    }

    })

    ?

    //改變狀態(tài)后想做一些事情(如:使用新值):

    this.setState({

    ?屬性名:屬性值

    }, () => {

    ? ?

    ?//一般是用于在setState之后做一些操作。

    ?//this.state == 修改之后的state

    ? ?

    })

    ?

    ?

    //改變狀態(tài)前后都想做一些事情:

    this.setState((prevState)=>{

    ? ?// prevState:是舊值

    ? ?console.log("prevState",prevState)

    ? ?return {

    ? ? ? ?age:15

    ? }

    },()=>{

    ? ?// this.state:就是新值。

    ? ?console.log(this.state.age);

    });

    class Book extends React.Component {

    ?

    ? ? ? ?constructor() {

    ? ? ? ? ? ?super();

    ? ? ? ? ? ?this.msg = "hi";

    ? ? ? ? ? ?this.state = {

    ? ? ? ? ? ? ? ?name: "三國演義",

    ? ? ? ? ? ? ? ?author: "李茂軍02"

    ? ? ? ? ? }

    ? ? ? ? ? ?this.changeVal = this.changeVal.bind(this);

    ? ? ? }

    ?

    ? ? ? ?changeVal() {

    ?

    ? ? ? ? ? ?// setState函數是異步的

    ? ? ? ? ? ?// this.setState({

    ? ? ? ? ? ?// ? ? name:"ddd"

    ? ? ? ? ? ?// });

    //console.log("this.state.name", this.state.name);//三國演義

    ? ? ? ? ? ?

    ? ? ? ? ? ?// 在修改狀態(tài)之前做事情

    ? ? ? ? ? ?// this.setState((prevState)=>{

    ? ? ? ? ? ?// ? ? console.log("prevState",prevState); //三國演義

    ? ? ? ? ? ?// ? ? console.log("this.state",this.state);//三國演義

    ? ? ? ? ? ?// ? ? return {

    ? ? ? ? ? ?// ? ? ? ? name:"ddd"

    ? ? ? ? ? ?// ? ? }

    ? ? ? ? ? ?// });

    ?

    ? ? ? ? ? ?// 在修改之后做一些事情

    ?

    ? ? ? ? ? ?this.setState({

    ? ? ? ? ? ? ? ?name: "ddd"

    ? ? ? ? ? }, () => {

    ? ? ? ? ? ? ? ?console.log("this.state.name", this.state.name);//ddd

    ? ? ? ? ? ? ? ?console.log(document.getElementById("pId").innerHTML);//ddd

    ? ? ? ? ? });

    ?

    ? ? ? }

    ?

    ? ? ? ?render() {

    ? ? ? ? ? ?console.log("render");

    ? ? ? ? ? ?return (

    ? ? ? ? ? ? ? ?

    ? ? ? ? ? ? ? ? ? ?

    {this.msg}

    ? ? ? ? ? ? ? ? ? ?

    書名:{this.state.name}

    ? ? ? ? ? ? ? ? ? ?

    作者:{this.state.author}

    ? ? ? ? ? ? ? ? ? ?

    價格:{this.props.price}

    ? ? ? ? ? ? ? ? ? ?

    ? ? ? ? ? ? ? ?

    ? ? ? ? ? )

    ? ? ? }

    ? }

    ?

    ? ?const h = (

    ? ? ? ?

    hello 我是react

    ? ? ? ?

    ? ? ? ? ? ?

    ? ? ? ?

    ? ?

    );

    ?

    ? ?ReactDOM.render(

    ? ? ? ?h,

    ? ? ? ?document.getElementById("box")

    ? );

    ?

    注意:

    1、 直接修改state屬性的值不會重新渲染組件 ,如:this.state.msg="hello"; //親,千萬不要這么干

    補充:

    給引用類型的某個屬性賦值(淺合并)

    class Book extends React.Component{ ? ?

    ?

    ? ? ? ?constructor(props){

    ? ? ? ? ? ?super(props);

    ? ? ? ? ? ?this.state = {

    ? ? ? ? ? ? ? ?book:{

    ? ? ? ? ? ? ? ? ? ?name:"做一個有錢的人",

    ? ? ? ? ? ? ? ? ? ?age:22

    ? ? ? ? ? ? ? }

    ? ? ? ? ? }

    ? ? ? ? ? ?this.changeVal = this.changeVal.bind(this);

    ? ? ? }

    ?

    ? ? ? ?changeVal(){

    ? ? ? ? ? ?// 這是不行的

    ? ? ? ? ? ?// this.setState({

    ? ? ? ? ? ?// ? ? "book.name":"做一個有意義的人"

    ? ? ? ? ? ?// });

    ?

    ? ? ? ? ? ?// 得這樣做(浪費了空間):就得這么寫

    ? ? ? ? ? ?let b = {

    ? ? ? ? ? ? ? ?...this.state.book,

    ? ? ? ? ? ? ? ?name:"做一個有意義的人"

    ? ? ? ? ? };

    ? ? ? ? ? ?this.setState({

    ? ? ? ? ? ? ? ?book:b

    ? ? ? ? ? });

    ? ? ? ? ? ?

    ? ? ? ? ? ?//或者這樣做(節(jié)約了空間):這樣不推薦,因為,這樣破壞了setState的異步

    ? ? ? ? ? ?let b = this.state.book;

    ? ? ? ? ? ?b.name = "大學";

    ? ? ? ? ? ?this.setState({

    ? ? ? ? ? ? ? ?book:b

    ? ? ? ? ? });

    ?

    ? ? ? }

    ?

    ? ? ? ?render=()=>{

    ? ? ? ? ? ?return (

    ? ? ? ? ? ? ? ?

      ? ? ? ? ? ? ? ? ? ?

    • {this.state.book.age}
    • ? ? ? ? ? ? ? ? ? ?

    • {this.state.book.name}
    • ? ? ? ? ? ? ? ? ? ?

      ? ? ? ? ? ? ? ?

    ? ? ? ? ? )

    ? ? ? }

    ? }

    示例代碼:

    1)、基本示例:

    class MyPerson extends React.Component{

    constructor(props){

    super(props);

    this.state={

    age:12

    }

    this.changeAge = this.changeAge.bind(this);

    }

    ?

    changeAge(){

    this.setState({

    age:this.state.age+1

    });

    }

    ?

    render(){

    return (

    年齡:{this.state.age}

    );

    }

    }

    2)、setState是異步的示例:

    ?

    class MyPerson extends React.Component{

    constructor(props){

    super(props);

    this.state={

    age:12,

    isAdult:"未成年"

    }

    this.changeAge = this.changeAge.bind(this);

    }

    /*

    這種寫法達不到效果:

    changeAge(){

    this.setState({

    age:this.state.age+1

    });

    // setState是異步的,所以,以下的打印不是最新的值

    console.log(this.state.age);

    // setState是異步的,所以,以下的判斷達不到效果

    if(this.state.age>=18){

    this.setState({

    isAdult:"已成年"

    });

    }

    }

    */

    changeAge(){

    this.setState({

    age:this.state.age+1

    },()=>{

    console.log(this.state.age);

    if(this.state.age>=18){

    this.setState({

    isAdult:"已成年"

    });

    }

    });

    }

    ?

    render(){

    return (

    年齡:{this.state.age}

    年齡:{this.state.isAdult}

    );

    }

    }

    無狀態(tài)組件

    無狀態(tài)組件就是組件內部沒有(不需要)state,無狀態(tài)組件也可以理解為展示組件,僅做展示用,可以根據外部傳來的props來渲染模板的內容,內部沒有數據。相當于木偶(沒腦子)。你說顯示啥,咱就顯示啥。

    var MyFooter = (props) => (

    ????

    {props.xxx}

    );

    僅僅只是一個函數,就ok了。

    有狀態(tài)組件

    有狀態(tài)組件就是不但外部可以傳入,內部也有state。

    有狀態(tài)組件也可以理解為容器組件,用來容納展示組件,在容器組件里處理數據的邏輯,把結果在展示組件里呈現。容器組件也叫智能組件

    創(chuàng)建有狀態(tài)組件如下:

    class Home extends React.Component { //容器組件

    ? ?constructor(props) {

    ? ? ? ?super(props);

    this.state={

    Hots:[],

    ? ? ? Goodslist:[]

    }

    }

    ? ?getHots(){

    ? ? ? ?發(fā)送axios請求獲取數據

    ? }

    ?

    ? ?getGoodsList(){

    ? ? ? 發(fā)送axios請求獲取數據

    ? }

    ? ?render() {

    ? ? ? ?return (

    ? ? //展示組件

    ? ? //展示組件

    ? ? ? )

    ? }

    }

    注意:

    做項目時,經常會把有狀態(tài)組件和無狀態(tài)組件進行結合使用。

    1)、 有狀態(tài)組件:一般會使用類組件,處理數據的業(yè)務邏輯,包括和后端進行交互,獲取數據。把數據傳給無狀態(tài)組件

    2)、 無狀態(tài)組件:一般會使用函數式組件,只做展示用,沒有自己的數據,會接收有狀態(tài)組件傳來的數據。

    事件處理

    React事件的特點:

    1、React 事件綁定屬性的命名采用駝峰式寫法,而不是小寫。如:onClick。

    2、如果采用 JSX 的語法你需要傳入一個函數作為事件處理函數,而不是一個字符串(DOM 元素的寫法)

    3、阻止事件的默認行為,不能使用return false, 必須使用 preventDefault。

    4、事件處理函數里的this是undefined(啊………),如果希望事件處理函數里的this是React組件對象本身,則需要用bind。

    事件語法

    1、格式

    示例:

    class Home extends React.Component{

    ? ?

    fn01() {

    ?console.log("fn01");

    }

    ? ?

    render(){

    ?return (

    ?

    ? ?

    ? ? {console.log("事件綁定箭頭函數")}} />

    ? ?

    )

    }

    }

    2、事件處理函數里的this

    事件處理函數里的this是undefined,如何讓事件處理函數里的this是React組件對象本身

    一、使用bind

    1)、構造器(構造函數)里:this.方法=this.方法.bind(this) ?

    2)、在事件綁定時寫,onClick={this.方法.bind(this)}

    ?

    二、使用箭頭函數

    3)、定義方法時,直接使用箭頭函數: 方法=()=>{箭頭函數定義方法} ?

    4)、事件綁定時,使用箭頭函數:onClick={()=>this.方法()}

    如:

    class MyPerson extends React.Component{

    constructor(props){

    super(props);

    this.state={

    age:12,

    isAdult:"未成年"

    }

    this.changeAge = this.changeAge.bind(this);

    }

    ?

    changeAge(){

    ………………………………

    }

    //直接使用箭頭函數

    changeAge=()=>{

    this指向會找上一級

    }

    ?

    render(){

    return (

    {this.changeAge()}} />

    年齡:{this.state.age}

    年齡:{this.state.isAdult}

    );

    }

    }

    3、事件對象

    event對象是經過react處理過的。

    如何獲取事件對象------直接聲明即可。

    實例方法(ev) ev 代理事件對象 ,ev.target 返回真實DOM

    事件對象的獲?。?/p>

    1)、直接聲明(沒有其它參數的情況下)

    changeAge1=(ev)=>{

    ? ?console.log(ev);

    ? ?console.log(ev.target);

    }

    ?

    this.changeAge1(ev)} />

    2)、箭頭函數里直接聲明(有其它參數的情況下)

    changeAge2(ev,num){

    ? ?console.log(ev);

    ? ?console.log(ev.target);

    ? ?console.log(num);

    }

    //注意:給onClick綁定的函數,還是只有一個參數,這個參數就是事件對象,此處在綁定的函數里再調用另外一個函數進行傳參

    this.changeAge2(ev,2)} />

    注意:給事件屬性綁定的函數,永遠只會有一個參數,該參數就是事件對象。

    4、阻止瀏覽器的默認行為:

    只能用preventDefault,不能在事件函數里使用return false

    組件的內容 :children

    組件的內容,使用 props.children屬性獲取

    ?

    function Button(props) {

    ?console.log("props",props);

    ?return (

    ? ?<>

    ? ? ?

    Button_children:{props.children}

    ? ?

    )

    }

    ?

    refs

    獲取DOM的。

    表示對組件真正實例(也就是html標簽,也就是DOM對象)的引用,其實就是ReactDOM.render()返回的組件實例;ref可以寫在html官方標簽里,也可以寫在組件(自定義標簽里),和vue的ref是同樣的意思。

    官方建議: 勿過度使用 Refs(盡量不要操作DOM),在對邏輯進行處理的時候盡量優(yōu)先考慮state(數據驅動)

    用法

    1). 賦值為 字符串(官方不推薦使用)

    ?

    ? this.refs.username.value

    2). 賦值為 回調函數

    當給 HTML 元素添加 ref 屬性時,ref 回調接收了底層的 DOM 元素作為參數。

    ref 回調會在componentDidMount 或 componentDidUpdate 這些生命周期回調之前執(zhí)行。

    //ref的值賦成回調函數時,回調的參數就是當前dom元素。

    // callback refs 回調

    ?

    this.定義一個實例屬性 = el} //el就是dom對象

    ? ?

    ? ?

    this.定義一個實例屬性 //后期用作訪問jsx元素

    //當組件掛載時,將 DOM el元素傳遞給 ref 的回調

    //當組件卸載時,則會傳遞 null。

    //ref 回調會在 componentDidMount 和 componentDidUpdate 生命周期之前調用

    ? ?

    ? ?如:

    ? this.input1 ?= currDom} />

    ? this.input2 = currDom} />

    3). React.createRef() (React16.3提供)

    使用此方法來創(chuàng)建ref。將其賦值給一個變量,通過ref掛載在dom節(jié)點或組件上,該ref的current屬性將能拿到dom節(jié)點或組件的實例

    //1、在構造函數里

    // 實例化了ref對象

    this.firstRef = React.createRef() //發(fā)生在構造器

    ?

    //2、掛載在ref屬性上

    ? ?

    //3、獲取dom元素

    this.firstRef.current //current是官方的屬性

    受控元素(組件)

    一個標簽(組件)受react中控制,受數據,受函數,等等(其實,就是一個標簽(組件)里用了react里的東西)。

    表單的value受控,受數據控制,

    //model 控制 view。

    ? //view 控制 model

    雙向綁定

    class MyCom extends React.Component{

    constructor(){

    super();

    this.state={

    username:"李祥"

    }

    }

    ?

    changeVal(e){

    this.setState({

    username:e.target.value

    })

    }

    ?

    render(){

    return (

    this.changeVal(e)} />

    )

    }

    }

    處理多個輸入元素(雙向綁定的封裝)

    可以為每個元素添加一個 name 屬性(通常和數據名一致),處理函數根據 event.target.name 的值來選擇要做什么

    class MyCom extends React.Component{

    ? ?constructor(props){

    ? ? ? ?super(props);

    ? ? ? ?this.state={

    ? ? ? ? ? ?userName:'',

    ? ? ? ? ? ?content:''

    ? ? ? }

    ? }

    ?

    ? ?changeVal(ev){

    ? ? ? ?this.setState({

    ? ? ? ? ? [ev.target.name]:ev.target.value

    ? ? ? });

    ? }

    ?

    ? ?render(){

    ? ? ? ?return (

    ? ? ? ? ? ?

    ? ? ? ? ? ? ? ?

    {this.state.userName}

    ? ? ? ? ? ? ? ?this.changeVal(ev)} />

    ? ? ? ? ? ? ? ?

    {this.state.content}

    ? ? ? ? ? ? ? ?this.changeVal(ev)} />

    ? ? ? ? ? ?

    ? ? ? )

    ? }

    }

    非受控元素(組件)

    要編寫一個非受控組件,而不是為每個狀態(tài)更新都編寫數據處理函數,你可以 使用 ref 來從 DOM 節(jié)點中獲取表單數據

    默認值

    表單元素上的 value 將會覆蓋 DOM 節(jié)點中的值,在非受控組件中,你經常希望 React 能賦予組件一個初始值,但是不去控制后續(xù)的更新,指定一個 defaultValue 屬性,而不是 value

    可選案例:

    增刪改查

    生命周期及其鉤子函數

    react組件生命周期經歷的階段:初始化階段 -----> 運行階段(更新期)-----> 銷毀階段

    初始化階段 (掛載):(在這個階段完成了vue中數據掛載和模板渲染)

    組件實例被創(chuàng)建并插入 DOM 中時,其生命周期鉤子函數的調用順序如下(粗體為使用比較多的):

    1)、constructor

    構造函數里,可以做狀態(tài)的初始化,接收props的傳值

    2)、componentWillMount: 在渲染前調用,相當于vue中的beforeMount

    3)、render

    渲染函數,不要在這里修改數據。 vue中也有render函數。

    4)、componentDidMount 相當于vue中的 mounted

    渲染完畢,在第一次渲染后調用。之后組件已經生成了對應的DOM結構, 如果你想和其他JavaScript框架(swiper)一起使用,可以在這個方法中使用,包括調用setTimeout, setInterval或者發(fā)送AJAX請求等操作,相當于vue的mounted

    運行中階段(更新)(相當于vue中更新階段)

    當組件的 props 或 state 發(fā)生變化時會觸發(fā)更新(嚴謹的說,是只要調用了setState()或者改變了props時)。組件更新的鉤子函數調用順序如下:

    1)、shouldComponentUpdate(nextProps, nextState) 組件應該更新嗎? 需要返回true或者false。如果是false,那么組件就不會繼續(xù)更新了。

    2)、componentWillUpdate,即將更新。相當于vue中的 beforeUpdate

    3)、 componentWillReceiveProps(nextProps): 在組件接收到一個新的 prop (更新后)時被調用。這個方法在初始化render時不會被調用。nextProps 是props的新值,而 this.props是舊值。相當于vue中的 beforeUpdate

    4)、render

    不要在這里修改數據

    5)、componentDidUpdate。相當于vue中的 updated

    在組件完成更新后立即調用。在初始化時不會被調用。 相當于vue中的updated

    銷毀階段(卸載)

    componentWillUnmount()

    即將卸載,可以做一些組件相關的清理工作,例如取消計時器、網絡請求等

    示例:

    ?

    class MyPerson extends React.Component{

    constructor(props){

    super(props);

    console.log("====constructor===");

    this.state={

    age:12

    }

    }

    ?

    changeAge2(ev,num){

    this.setState({

    age:this.state.age+1

    });

    }

    ?

    componentWillMount(){

    console.log("====首次渲染前:componentWillMount===");

    }

    ?

    componentDidMount(){

    console.log("====首次渲染完畢:componentDidMount===");

    }

    ?

    shouldComponentUpdate(){

    console.log("====希望更新組件嗎?(state發(fā)生變化了):shouldComponentUpdate===");

    return true;

    }

    ?

    componentWillUpdate(){

    console.log("====組件更新前(state發(fā)生變化了):componentWillUpdate===");

    }

    ?

    componentWillReceiveProps(){

    console.log("====組件更新前(props發(fā)生變化了):componentWillReceiveProps===");

    }

    ?

    componentDidUpdate(){

    console.log("====組件更新后:componentDidUpdate===");

    }

    ?

    render(){

    console.log("====render===");

    return (

    this.changeAge2(e,2)} />

    年齡:{this.state.age}

    );

    }

    }

    ?

    ReactDOM.render(

    ,

    document.getElementById('box')

    );

    注意(面試題):

    父更新則子更新,子更新父不更新

    ?

    class BookList extends React.Component{ ? ?

    ? ?constructor(props){

    ? ? ? super();

    ? ? ? console.log("BookList:constructor");

    ? }

    ? ?// 首次渲染前

    ? ?componentWillMount(){

    ? ? ? ?console.log("BookList:componentWillMount");

    ? } ? ?

    ? ?// 首次渲染后

    ? ?componentDidMount(){

    ? ? ? ?console.log("BookList:componentDidMount");

    ? }

    ?

    ? ?change=()=>{

    ? ? ? ?this.setState({});

    ? }

    ?

    ? ?componentWillUpdate(){

    ? ? ? ?console.log("BookList:componentWillUpdate");

    ? }

    ?

    ? ?componentDidUpdate(){

    ? ? ? ?console.log("BookList:componentDidUpdate");

    ? }

    ?

    ? ?render(){

    ? ? ? ?console.log("BookList:render"); ? ? ? ?

    ? ? ? ?return (

    ? ? ? ? ? ?

    ? ? ? ? ? ? ? ?

    書籍列表

    ? ? ? ? ? ? ? ?

    ? ? ? ? ? ? ? ?

    ? ? ? ? ? ?

    ? ? ? ) ? ? ? ?

    ? }

    }

    ?

    class Book extends React.Component{ ? ?

    ? ?constructor(props){

    ? ? ? ?super();

    ? ? ? console.log("Book:constructor"); ? ? ?

    ? }

    ?

    ? ? ? ?// 首次渲染前

    ? ?componentWillMount(){

    ? ? ? ?console.log("Book:componentWillMount");

    ? }

    ? ?

    ? ?// 首次渲染后

    ? ?componentDidMount(){

    ? ? ? ?console.log("Book:componentDidMount");

    ? }

    ?

    ? ?componentWillUpdate(){

    ? ? ? ?console.log("Book:componentWillUpdate");

    ? }

    ?

    ? ?componentDidUpdate(){

    ? ? ? ?console.log("Book:componentDidUpdate");

    ? }

    ?

    ? ?change=()=>{

    ? ? ? ?this.setState({});

    ? }

    ?

    ? ?render(){

    ? ? ? ?console.log("Book:render"); ? ?

    ? ? ? ?return (

    ? ? ? ? ? ?

    ? ? ? ? ? ? ? ?

    ? ? ? ? ? ?

    ? ? ? ) ? ? ? ?

    ? }

    }

    ?

    ReactDOM.render(

    ? ?

    ? ? ? ?

    ? ? ? ?

    ? ?

    ,

    ? ?document.getElementById("box")

    );

    react17,不建議使用的鉤子函數(面試過程中有問到):

    UNSAFE_componentWillMount 不建議用,可以會出現bug,不能初始化因為會受到React16.xFiber的協(xié)調算法,函數會執(zhí)行多次,如果把異步請求放到該鉤子函數中,異步請求可能也會執(zhí)行多次。

    UNSAFE_componentWillReceiveProps UNSAFE_componentWillUpdate

    es5版(可以不用看了)

    實例化

    取得默認屬性(getDefaultProps) 外部傳入的props 初始狀態(tài)(getInitialState) state狀態(tài) 即將掛載 componentWillMount 描畫VDOM render 掛載完畢 componentDidMount

    補充:vue組件更新和react組件更新的觸發(fā)條件(更新時機)不一樣

    1、vue的更新觸發(fā)條件:

    ? ?

    ?

    data(){

    ? ?return {

    ? ? ? ?msg:'hi',

    ? ? ? ?str:"hello"

    ? }

    }

    ?

    methods:{

    ? ?changeMsg(){

    ? ? ? ?this.str="hhhhh";//不會引起組件的重新渲染

    ? ? ? ?this.msg="hi";//不會引起組件的重新渲染

    ? ? ? ?this.msg="hello"http://引起組件的重新渲染

    ? }

    }

    vue組件更新_引起組件更新的起因,什么會引發(fā)組件的更新_別人的vue一運行components內容就改變-CSDN博客

    2、react組件的更新觸發(fā)條件:

    只要改變props或者state,無論數據有沒有顯示在模板里,都會引發(fā)渲染。并且,只要是賦值,無論賦值前后的數據是不是一樣都會引起組件的渲染(其實就是只要調用setState函數,傳參為空對象就行),所以,在React里需要牽扯到組件更新的性能優(yōu)化。

    性能優(yōu)化

    react組件更新的時機:只要setState()被調用了,就會引起組件更新。不論數據改前改后是否一樣,或者修改的數據是否在頁面上呈現,都會進行更新組件。

    但是vue中,數據必須在模板使用,并且數據發(fā)生變化才能引起組件的重新渲染。所以,在React里,如果要做性能優(yōu)化,可以把這種無效的渲染阻止掉。

    1)、shouldComponentUpdate()鉤子函數

    ?

    shouldComponentUpdate(nextProps, nextState){

    console.log("this.state",this.state);//當前狀態(tài)

    console.log("nextState",nextState);//新的狀態(tài)

    ? ?return false;//不再渲染。

    }

    ?

    我們可以在這個鉤子函數里return false,來跳過組件更新

    示例:

    class Books extends React.Component {

    ?

    ? ? ? ?constructor(props) {

    ? ? ? ? ? ?super();

    ? ? ? ? ? ?this.state = {

    ? ? ? ? ? ? ? msg:"hello react",

    ? ? ? ? ? ? ? ?d_str:"how are you!" ? //約定:以 d_ 開頭的數據不再模板上渲染

    ? ? ? ? ? }

    ? ? ? }

    ?

    ? ? ? ? ?// 比較兩個對象的內容(屬性不是以d_開頭)是否一樣。

    ? ? ? ?compareObj(obj1,obj2){

    ? ? ? ? ? ?// 1、兩個對象的屬性數量如果不一樣,那么就肯定不相等

    ? ? ? ? ? ?if(Object.keys(obj1).length!=Object.keys(obj2).length){

    ? ? ? ? ? ? ? ?return false;

    ? ? ? ? ? }

    ? ? ? ? ? ?// 2、兩個對象的屬性數量一樣

    ? ? ? ? ? ?for(let key in obj1){

    ? ? ? ? ? ? ? ?if(typeof obj1[key]=="object"){

    ? ? ? ? ? ? ? ? ? ?if(typeof obj2[key]=="object"){

    ? ? ? ? ? ? ? ? ? ? ? ?// 兩個屬性都是對象。

    ? ? ? ? ? ? ? ? ? ? ? ?if(!key.startsWith("d_") && !this.compareObj(obj1[key],obj2[key])){

    ? ? ? ? ? ? ? ? ? ? ? ? ? ?return false;

    ? ? ? ? ? ? ? ? ? ? ? }

    ? ? ? ? ? ? ? ? ? }else{

    ? ? ? ? ? ? ? ? ? ? ? ?// obj1是對象,obj2不是對象

    ? ? ? ? ? ? ? ? ? ? ? ?return false;

    ? ? ? ? ? ? ? ? ? }

    ? ? ? ? ? ? ? }else{

    ? ? ? ? ? ? ? ? ? if(typeof obj2[key]=="object"){ ? ? ? ? ? ? ? ? ? ? ?

    ? ? ? ? ? ? ? ? ? ? ? ?// obj1不是對象,obj2是對象

    ? ? ? ? ? ? ? ? ? ? ? ?return false;

    ? ? ? ? ? ? ? ? ? }else{

    ? ? ? ? ? ? ? ? ? ? ? if(!key.startsWith("d_") && obj1[key]!=obj2[key]){

    ? ? ? ? ? ? ? ? ? ? ? ? ? ?return false;

    ? ? ? ? ? ? ? ? ? ? ? }

    ? ? ? ? ? ? ? ? ? }

    ? ? ? ? ? ? ? }

    ? ? ? ? ? }

    ? ? ? ? ? ?return true;

    ? ? ? }

    ?

    ? ? ? ?shouldComponentUpdate(nextProps,nextState){

    ? ? ? ? ? ?console.log("shouldComponentUpdate:是否更新?");

    ? ? ? ? ? ?console.log("this.state",this.state);

    ? ? ? ? ? ?console.log("nextState",nextState);

    ? ? ? ? ? ?// if(數據沒有變化 || 數據發(fā)生了變化,但是該數據并不在模板上使用){

    ? ? ? ? ? ?// ? ? return false

    ? ? ? ? ? ?// }

    ?

    ? ? ? ? ? ?if(this.compareObj(this.state,nextState)){

    ? ? ? ? ? ? ? ?return false;

    ? ? ? ? ? }

    ?

    ? ? ? ? ? ?return true;

    ? ? ? }

    ?

    ? ? ? changeMsg=()=>{

    ? ? ? ? ? ?this.setState({

    ? ? ? ? ? ? ? ?msg:this.state.msg+"1"

    ? ? ? ? ? ? ? ?// msg:this.state.msg

    ? ? ? ? ? })

    ? ? ? }

    ?

    ? ? ? ?changeStr=()=>{

    ? ? ? ? ? ?this.setState({

    ? ? ? ? ? ? ? ?d_str:this.state.d_str+"1"

    ? ? ? ? ? })

    ? ? ? ? ? ?// this.setState({});

    ? ? ? }

    ?

    ? ? ? ?render(){

    ? ? ? ? ? ?console.log("render"); ? ? ? ? ? ?

    ? ? ? ? ? ?return (

    ? ? ? ? ? ? ? ?

    ? ? ? ? ? ? ? ? ? ?

    生命周期及其鉤子函數

    ? ? ? ? ? ? ? ? ? ?

    {this.state.msg}

    ? ? ? ? ? ? ? ? ? ?

    ? ? ? ? ? ? ? ? ? ?

    ? ? ? ? ? ? ? ?

    ? ? ? ? ? )

    ? ? ? }

    ? }

    ?

    2)、PureComponent

    React15.3中新加了一個 PureComponent 類,只要把繼承類從 Component 換成 PureComponent 即可

    PureComponent 默認提供了一個具有淺比較的shouldComponentUpdate方法。只是比較數據(第一層)是否有變化,也不會判斷數據是否在頁面上展示。 當props或者state改變時,PureComponent將對props和state進行淺比較。即:如果說狀態(tài)是引用類型,只要地址發(fā)生變化,照樣會重新渲染。 不能再重寫shouldComponentUpdate

    Component和PureComponent的區(qū)別:

    當組件繼承自 Component 時,只要setState()被調用了,就會引起組件更新。不論數據改前改后的是否一樣,或者修改的數據是否在頁面上呈現,都會進行更新組件。

    PureComponent只能保證數據不變化的情況下不做再次的渲染,而不關心數據是否在頁面上渲染,而且比較數據變化時,只比較第一層。

    那么,親,您注意:

    1、您為什么要把不在界面上顯示的數據寫在state里呢?難道,直接定義成類的屬性不香嗎?

    2、因為,使用setState時,只能改變根屬性,所以,PureComponent只比較一層,確實夠用了。

    假如:狀態(tài)是引用類型,那么,修改引用類型的某個屬性時,引用類型的地址肯定會變化。

    class MyCom extends React.PureComponent{

    constructor(){

    super();

    this.state={

    username:"張三瘋",

    age:12,

    wife:{

    name:"寶寶的寶寶"

    }

    }

    }

    ?

    componentWillUpdate(){

    console.log("componentWillUpdate");

    }

    componentDidUpdate(){

    console.log("componentDidUpdate");

    }

    ?

    fn(){

    //1、 不會引起組件的更新,因為,值沒有變

    // this.setState({

    // username:this.state.username

    // });

    //2、 會引起組件的更新,因為,值變了

    // this.setState({

    // username:"dddd"

    // });

    //3、 會引起組件的更新,因為,值變了(即使age么有在頁面上顯示,也會引起更新)

    // this.setState({

    // age:this.state.age+1

    // })

    ?

    //4、會引起組件的更新,雖然,name的值沒有變化,但是,wife的值變化了(wife是引用類型)

    let obj = this.state.wife;

    obj.name="寶寶的寶寶";

    this.setState({

    wife:obj

    });

    }

    ?

    render(){

    console.log("render");

    return (


    this.fn()} />

    )

    }

    }

    ?

    腳手架

    facebook的官方腳手架

    搭建項目:

    一、第一種:

    create-react-app5.0.3 之前的版本:

    1)、安裝 create-react-app (CRA)

    npm install create-react-app -g | yarn global add create-react-app

    2)、用腳手架創(chuàng)建 react項目

    create-react-app? 項目名稱

    如:create-react-app reactapp01

    注意:項目名稱不能有大寫字母。

    3)、 啟動項目:

    npm start ? | ? yarn start

    二、第二種:

    從creact-react-app的5.0.3開始,不再支持全局安裝create-react-app進行搭建項目,而是,直接搭建項目。

    1)、搭建項目:

    npm init react-app ?項目名稱

    2)、 啟動項目:

    npm start ? | ? yarn start

    目錄解析:

    4.1)第一級目錄

    node_modules:是項目依賴的模塊

    src:是程序員寫代碼的地方,src是source的縮寫,表示源代碼

    public: 靜態(tài)資源。react腳手架中的靜態(tài)圖片資源和動態(tài)圖片資源都放在此處

    4.2)展開目錄:

    Public:

    index.html:是html網頁,是個容器。這個文件千萬不敢刪除,也不能改名。

    只有Public目錄下 的文件才會被index.html文件引用,這是靜態(tài)資源,index.html不會引用src目錄下的文件

    manifest.json: 生成一個網頁的桌面快捷方式時,會以這個文件中的內容作為圖標和文字的顯示內容

    src:

    src目錄是源代碼,webpack只會打包這個目錄下的文件,所以,把需要打包的文件都放在這個目錄下。

    Index.js:是主js文件,千萬不敢刪除,也不能改名

    Index.css:是index.js引入的css文件(也是模塊,webpack會把css也打包成模塊) 千萬不敢刪除,也不能改名

    App.js:是一個組件示例(模塊),在 index.js里會引入這個組件。我們自己需要寫組件時,只需要復制App.js文件即可。

    App.css:是App.js文件引入的css文件(也是模塊,webpack會打包)。

    Logo.svg:是圖片

    registerServiceWorker.js:支持離線訪問,所以用起來和原生app的體驗很接近,只有打包生成線上版本的react項目時,registerServiceWorker.js才會有效。服務器必須采用https協(xié)議

    5)、打包

    npm run ?build | yarn build

    6)、如果要解構出配置文件:

    npm ?run ?eject ?| ?yarn eject ? 解構出所有的配置文件 可選

    7)、如果需要調試,安裝react-dev-tools工具

    先進入到https://github.com/facebook/react網址 通過git clone https://github.com/facebook/react-devtools.git下載到本地(或者直接點擊下載) 下載之后進入到react-devtools目錄下,用npm安裝依賴

    npm --registry https://registry.npm.taobao.org install

    然后在npm run build:extension:chrome

    環(huán)境配置

    1、把配置解構

    npm run eject | yarn eject

    ?

    ?

    2、修改端口

    //修改script/start.js

    const DEFAULT_PORT = parseInt(process.env.PORT, 10) || 3001;

    ?

    3、去除eslint 警告

    //config/webpack.config.js

    //注釋關于eslint的導入和rules規(guī)則

    資源限制

    本地資源導入(import) 不可以導入src之外的包 圖片聲音等靜態(tài)資源的路徑: 都放在public下,寫路徑時,不要寫public(和vue腳手架不一樣)。因為,react腳手架里不會對靜態(tài)的src路徑對應的圖片打包。

    在腳手架里做項目的步驟:

    1)、創(chuàng)建目錄

    在src目錄下創(chuàng)建以下文件夾:

    assets :靜態(tài)資源文件夾 放置的是import或者require引入的資源。 components:組件文件夾 /components/a組件/ a.js 和 a.css pages:頁面文件夾

    2)、圖片文件夾

    1.如果不希望圖片被打包處理:

    把圖片放到public文件夾中,使用絕對路徑(img src="/img/image.jpg" />)

    2.如果希望圖片被(webpack)打包

    圖片放在assets下。

    使用require引用,require(‘圖片的相對路徑'),Require中只能使用字符串不能使用變量。如:

    vue腳手架和react腳手架不同

    1、編譯打包時,

    圖片路徑問題:vue:靜態(tài)路徑會打包,動態(tài)不會;react都不會打包,如果希望react里的靜態(tài)路徑打包,那就使用用require引入圖片。

    反向代理:

    Proxying API Requests in Development | Create React App

    1、安裝模塊(http-proxy-middleware): 這個模塊在vue腳手架里是默認安裝的。

    npm install http-proxy-middleware --save-dev

    ?

    yarn add http-proxy-middleware -D

    2、在項目源代碼的根目錄創(chuàng)建文件: src/setupProxy.js

    const { createProxyMiddleware } = require('http-proxy-middleware');

    ?

    module.exports = function(app) {

    ?console.log("proxy");

    ?app.use(

    ? ?'/api',

    ? ?createProxyMiddleware({

    ? ? ?target: 'http://xmb8nf.natappfree.cc',

    ? ? ?changeOrigin: true,

    ? ? ?// 重寫接口路由

    ? ? ?pathRewrite: {

    ? ? ? ?'^/api': ''

    ? ? }

    ? })

    );

    };

    3、重啟服務器

    yarn start

    第三方腳手架

    yeomen/dva/umi

    組件傳值

    1).父子組件通信方式

    (1) Props傳遞數據與Props傳遞方法

    父組件--->子組件:用props傳遞數據

    1)、定義組件時,聲明props形參:

    class Person extends React.Component {

    ?constructor(props) {

    ? ? super(props);

    }

    render() {

    ? ?return (

    ? ? ? ?

    ? ? ?

    ? ? ? ? 姓名:{this.props.name}

    ? ? ?

    ? );

    }

    }

    ?

    2)、使用組件時,傳入參數:

    子組件--->父組件,用props傳遞方法

    父組件利用props傳遞方法給子組件,子組件回調這個方法的同時,將數據傳遞進去,使得父組件的相關方法得到回調,這個時候就可以把數據從子組件傳遞給父組件了

    //1、父組件

    class App extends React.Component {

    ? ?constructor(){

    ? ? ? ?super();

    ? ? ? ?this.state={

    ? ? ? ? ? ?t:"Home"

    ? ? ? }

    ? }

    ?

    ? ?changeData(str){

    ? ? ? ?console.log("子傳給了父親:",str);

    ? }

    ?

    ? ?render = () => (

    ? ? ? ?

    ? ? ? ? ?

    ? ? ? ? ?

    ? ? ? ?

    ? )

    };

    ?

    //2、子組件

    class Home extends React.Component {

    ? ?constructor(name){

    ? ? ? ?super();

    ? ? ? ?this.name=name;

    ? ? ? ?this.state={

    ? ? ? ? ? ?msg:"hello "

    ? ? ? }

    ? }

    ?

    ? ?render = () => (

    ? ? ? ?

    ? ? ? ? ? ?

    我是{this.props.title}

    ? ? ? ? ? ?

    {this.state.msg+this.props.title}

    ? ? ? ? ? ?

    ? ? ? ? ? ? ? ?type="button"

    ? ? ? ? ? ? ? ?value="傳給父親"

    ? ? ? ? ? ? ? ?onClick={()=>this.props.fn('HELLO dad')} />

    ? ? ? ?

    ? )

    }

    (2) ref 標記

    組件間通信除了props外還有onRef方法,不過React官方文檔建議不要過度依賴ref。

    思路:當在子組件中調用onRef函數時,正是調用從父組件傳遞的函數。this.props.onRef(this)這里的參數指向子組件本身,父組件接收該引用作為第一個參數:onRef = {ref =>(this.child = ref)}然后它使用this.child保存引用。之后,可以在父組件內訪問整個子組件實例,并且可以調用子組件函數。

    //子組件

    class Person extends React.Component {

    ?constructor(props) {

    ? ?super(props);

    ? ?this.state={

    ? ? ? ?msg:"hello"

    ? }

    }

    ?//渲染完畢

    ?componentDidMount(){

    ? ? ?//子組件首次渲染完畢后,把子組件傳給父組件(通過props的onRef)

    ? ? ?this.props.onRef(this);//把子組件傳給父組件,注意,此處的this是子組件對象

    }

    ? ? ?

    ?render() {

    ? ?return ( ?

    我是子組件
    );

    }

    }

    ?

    //父組件:

    class Parent extends React.Component{

    ? ?constructor(props){

    ? ? ? ?super(props);

    ? ? ? ?this.child =null;//定義了一個屬性,該屬性表示子組件對象

    ? }

    ? ?

    ? ?testRef=(ref)=>{

    ? ? ? ?this.child = ref //給父組件增加屬性child,child保存著子組件對象

    ? ? ? ?console.log(ref) // -> 獲取整個Child元素

    ? }

    ? ?

    ? ?handleClick=()=>{

    ? ? ? ?alert(this.child.state.msg) // -> 通過this.child可以拿到child所有狀態(tài)和方法

    ? ? ? ?this.child.setState({

    ? ? ? ? ?msg:"哈哈"

    ? ? ? })

    ? }

    ? ?

    ? ?render(){

    ? ? ? ?return (

    ? ? ? ? ? ?

    ? ? ? ? ? ? ? ?

    ? ? ? ? ? ?

    ? ? ? )

    ? }

    }

    ReactDOM.render(,$("box"));

    2).非父子組件通信方式

    (1)訂閱發(fā)布(pubsub模塊)

    相當于vue的事件總線

    訂閱: token=pubsub.subscribe('消息名',回調函數('消息名',數據){ 函數體 }),相當于事件總線的$on綁定事件 發(fā)布: pubsub.publish('消息名',數據),相當于事件總線的$emit觸發(fā)事件 清除指定訂閱:pubsub.unsubscribe(token|'消息名'); 清除所有:pubsub.unsubscribeAll()

    var pubsub = new PubSub();

    ?

    class MyCom1 extends React.Component{

    constructor(){

    super();

    }

    testf(){

    pubsub.publish('user_add', {

    firstName: 'John',

    lastName: 'Doe',

    email: 'johndoe@gmail.com'

    });

    }

    render(){

    return (

    MyCom1

    this.testf()} />

    )

    }

    }

    ?

    class MyCom2 extends React.Component{

    constructor(){

    super();

    this.state={

    msg:"hi"

    }

    //訂閱

    pubsub.subscribe('user_add', function (data) {

    console.log('User added');

    console.log('user data:', data);

    });

    }

    ?

    render(){

    return (

    MyCom2

    )

    }

    }

    ?

    const jsx =


    ?

    ReactDOM.render(

    jsx,

    ? ?document.getElementById('box')

    );

    (2)狀態(tài)提升

    使用 react 經常會遇到幾個組件需要共用狀態(tài)(數據)的情況。這種情況下,我們最好將這部分共享的狀態(tài)提升至他們最近的父組件當中進行管理。 即:把原本屬于子組件的state,放在父組件里進行管理。

    https://www.reactjscn.com/docs/lifting-state-up.html

    父組件:src/components/Parent.js

    ?

    import React from "react";

    import Son1 from "./Son1";

    import Son2 from "./Son2";

    ?

    export default class Parent extends React.Component {

    ? ?constructor(props){

    ? ? ? ?super(props);

    ? ? ? ?this.state = {val:'默認值'};

    ? }

    ?

    ? ?tempFn(val){

    ? ? ? ?console.log("tempFn");

    ? ? ? ?console.log("val",val);

    ? ? ? ?

    ? ? ? ?this.setState({

    ? ? ? ? ? ?val:val

    ? ? ? })

    ? ? ? ?

    ? }

    ? ?

    ? ?render = () => (

    ? ? ? ?

    ? ? ? ? ? ? 父組件

    ? ? ? ? ? ?


    ? ? ? ? ? ? this.tempFn(val)} />

    ? ? ? ? ? ?

    ? ? ? ?

    ? )

    }

    ?

    Son1組件

    src/components/Son1.js

    ?

    export default class Son1 extends React.Component {

    ? ?constructor(props){

    ? ? ? ?super(props);

    ? ? ? ?this.state = {

    ? ? ? ? ? ?name:"我是son1"

    ? ? ? };

    ? }

    ?

    ? ?chuan(){

    ? ? ? ?this.props.onMyClick(this.state.name);

    ? }

    ? ?

    ? ?render = () => (

    ? ? ? ?

    ? ? ? ? ? ? son1組件

    ? ? ? ? ? ? {this.chuan()}} />

    ? ? ? ? ? ?


    ? ? ? ?

    ? )

    }

    ?

    Son2組件

    src/components/Son2.js

    ?

    export default class Son2 extends React.Component {

    ? ?constructor(props){

    ? ? ? ?super(props);

    ? ? ? ?this.state = {};

    ? }

    ? ?

    ? ?render = () => (

    ? ? ? ?

    ? ? ? ? ? ?

    son2組件

    ? ? ? ? ? ?

    val:{this.props.val}

    ? ? ? ? ? ?


    ? ? ? ? ? ?

    ? ? ? ?

    ? )

    }

    3).context 狀態(tài)樹(共享的狀態(tài)樹)傳參

    在平時使用react的過程中,數據都是自頂而下的傳遞方式,例如,如果在頂層組件(如:App)的state存儲了theme主題相關的數據作為整個App的主題管理。那么在不借助任何第三方的狀態(tài)管理框架的情況下,想要在子組件里獲取theme數據,就必須的一層層傳遞下去,即使兩者之間的組件根本不需要該數據,這樣的傳遞方式很麻煩(代碼復雜),并且浪費內存。

    Context 旨在共享一個組件樹,可被視為 “全局” 的數據,達到越級傳遞,場景:當前經過身份驗證的用戶,主題或首選語言,包括管理當前的 locale,theme,或者一些緩存數據。

    createContext():用于創(chuàng)建context對象(上下文),需要一個defaultValue的參數,并返回一個包含Provider(提供者),以及Consumer(消費者)的對象

    Provider:提供者,提供數據。接收一個將要被往下層層傳遞的props,該值需在組件樹最頂層設置。一個Provider可以關聯(lián)到多個Consumers。這是一個頂層用于提供context的組件,包含一個value的props,value是實際的context數據。

    Consumer:消費者,使用者,接收一個函數作為子(DOM)節(jié)點,函數接收當前 context 的值。這是一個底層用于獲取context的組件,需要一個函數作為其子元素,該函數包含一個value的參數,這個參數就是上層所傳遞context value

    創(chuàng)建一個context對象,并設定默認值。語法如下:

    const {Provider, Consumer} = React.createContext(defaultValue);

    從父朝子傳值(外朝內)

    示例代碼:

    context模塊: ./src/utils/myContext;

    ?

    import {createContext} from "react";

    ?

    export const {Provider, Consumer} = createContext({

    ?name:"張三瘋"

    });

    ?

    ?

    //頂層組件:./src/App.js

    ?

    import {Provider} from "./utils/myContext"

    ?

    function App() {

    ?let val={

    ? ?name:"hi"

    };

    ?return (

    ? ?

    ? ? ?

    ? ? ? ?

    ? ? ?

    ? ?

    );

    }

    ?

    //孫子組件: App->Home->Goodslist

    import {Consumer} from "../utils/myContext";

    ?

    export default class GoodsList extends React.Component {

    ?

    ? ?render = () => (

    ? ? ? ?

    ? ? ? ? ? ?

    商品列表:

    ? ? ? ? ? ?

    ? ? ? ? ? ?

    ? ? ? ? ? ? ? ? ? {

    (value) ?=>

    { value.name }

    }

    ? ? ? ? ? ?

    ? ? ? ?

    ? )

    }

    注意:

    export const {Provider, Consumer} = createContext({

    name:"張三瘋" });

    這個默認值是在頂層組件沒有使用Provider組件時的值,而不是,沒有給value屬性賦值時的值。

    即:頂層組件的代碼如下:

    function App() {

    let val={

    ? ? name:"hi"

    };

    return (

    ?

    ?

    ? ? ? ?

    ?

    );

    }

    在子組件改變狀態(tài)樹的數據

    //根組件:

    import {Provider} from "./utils/myContext"

    ?

    export default class App extends React.Component {

    ?constructor(props){

    ? ?super(props); ?

    ? ?this.state={

    ? ? ? ?name:"宋晨",

    ? ? ? ?setName:this.fn

    ? }

    }

    ?

    ?fn=(str)=>{

    ? ? ?this.setState({

    ? ? ? ?name:str

    ? ? });

    }

    ?

    ?render = () => (

    ? ?

    ? ? ?

    ? ? ? ?

    ? ? ?

    ? ?

    )

    }

    ?

    //子組件里:

    import {Consumer} from "../../utils/myContext";

    ?

    export default class Banner extends Component {

    ?

    render = () => (

    ?

    ? ?

    ? ? ? {

    ? ? ? ? ? (obj)=>(

    ? ? ? ? ? ? ?

    ? ? ? ? ? ? ? ? ?

    姓名:{obj.name}

    ? ? ? ? ? ? ? ? ?obj.setName("hiwww")} />

    ? ? ? ? ? ? ?

    ? ? ? ? )

    ? ? ? }

    ? ? ? ?

    ? ?

    ? )

    }

    高階組件(HOC)的構建與應用

    https://www.reactjscn.com/docs/higher-order-components.html

    高階組件(HOC)是react中對組件邏輯進行重用的高級技術。但高階組件本身并不是React API。它只是一種模式,這種模式是由react自身的組合性質必然產生的。

    具體而言,高階組件就是一個函數,且該函數接受一個組件作為參數,并返回一個新的組件,高階組件會對傳入的組件做一些通用的處理。比如:我們希望給組件的的下方增加一個版權信息,那么就可以使用高階組件。把原始組件傳入,然后,給組件增加一個版權信息后,再返回。

    高階組件是通過將原組件 包裹(wrapping) 在容器組件(container component)里面的方式來組合(composes) 使用原組件。高階組件就是一個沒有副作用的純函數(有參數,有返回值,并且在函數內部不會修改參數)。

    如:

    const EnhancedComponent = higherOrderComponent(WrappedComponent);

    高階函數是:higherOrderComponent

    傳入的組件是:WrappedComponent

    返回的組件是:EnhancedComponent

    示例代碼:

    // 帶上版權的高階函數,并且給組件賦能(增加屬性和方法)

    function withCopyRight(OldCom){

    ? ?//給組件賦能

    ? ?OldCom.prototype.aaa = "hi";

    ? ?OldCom.prototype.fn = function(){

    ?

    ? };

    ? ?

    ? ?class NewCom extends React.Component{ ? ? ?

    ? ? ? ?render() {

    ? ? ? ? ? ?return (

    ? ? ? ? ? ? ?

    ? ? ? ? ? ? ? ? ? ?

    ? ? ? ? ? ? ? ? ? ?


    ? ? ? ? ? ? ? ? ? ?

    ? ? ? ? ? ? ? ? ? ? ? Copyright ? 2020 Sohu All Rights Reserved. 搜狐公司 版權所有

    ? ? ? ? ? ? ? ? ? ?

    ? ? ? ? ? ? ?

    ? ? ? ? ? );

    ? ? ? }

    ? } ?

    ? ?

    ? ?return NewCom;

    }

    ?

    //原始組件

    class CommentList extends React.Component {

    ? ?componentDidMount(){

    ? ? ? ?console.log("this.aaa",this.aaa);

    ? ? ? ?console.log("this.fn",this.fn);

    ? }

    ? ?render() { ? ? ?

    ? ? ? return (

    ? ? ? ? ?

    ? ? ? ? ? ? ? 新聞11111111111111111111

    ? ? ? ? ?

    ? ? ? );

    ? }

    }

    ?

    //調用高階函數后的組件

    const CommentListWithCopyRight = withCopyRight(CommentList);

    ?

    ReactDOM.render(

    ,$("box"));

    function $(id){

    ? ?return document.getElementById(id);

    }

    高階組件的優(yōu)點:

    1、給組件賦能 :如:給類的props或者state增加數據和方法,或者給類的prototype增加數據和方法。

    2、解耦

    3、開閉原則:對修改關閉,對擴展開放

    高階組件的注意點(這些特點慢慢理解消化,使用其做了項目后,回過頭來再看一下):

    1、不要在render函數里使用(調用)高階組件

    2、必須將靜態(tài)(static)方法做拷貝

    當使用高階組件包裝組件,原始組件被容器組件包裹,也就意味著新組件會丟失原始組件的所有靜態(tài)方法,解決這個問題的方法就是,將原始組件的所有靜態(tài)方法全部拷貝給新組件。

    3、Refs不能傳遞

    一般來說,高階組件可以傳遞所有的props屬性給包裹的組件,但是不能傳遞refs引用。

    4、不要在高階組件內部修改(或以其它方式修改)原組件的原型屬性(prototype)。

    5、約定:將不相關的props屬性傳遞給包裹組件

    6、約定:最大化使用組合

    7、約定:包裝顯示名字以便于調試

    路由

    官網 中文

    vue-routerreact-router路由配置分離式(在一個單獨的文件里配置)嵌套式(路由配置在組件內部,任何組件都可以寫)匹配排他性(只有一個路由被匹配上)包容性(多個路由可能會同時被匹配上)路由上下文this.$router,this.$route掛在組件屬性上的history,location,match路由對象需要專門創(chuàng)建不需要專門創(chuàng)建,都是組件的方式使用,相關對象掛在了組件的屬性上

    1、作用:

    路由最基本的職責就是當頁面的 訪問地址 與 Route 上的 path 匹配上時,就渲染出對應的 UI 界面(組件)。

    實現SPA應用,整個項目只有一個完整頁面。頁面切換不會刷新頁面,內容局部更新

    2、react-router提供了兩種路由模塊

    1).React-Router:

    提供了一些router的核心API,包括Router, Route, Switch等,但是它沒有提供 DOM 操作進行跳轉的API。很少用

    2).React-Router-DOM:

    提供了 BrowserRouter,HashRouter , Route, Link,Switch等 API,我們可以通過 DOM 的事件控制路由。例如點擊一個按鈕進行跳轉,所以在開發(fā)過程中,我們更多是使用React-Router-DOM。

    3、路由的兩種模式:

    1).HashRouter組件:

    url中會有個#,例如localhost:3000/#,HashRouter就會出現這種情況,它是通過hash值來對路由進行控制。如果你使用HashRouter,你的路由就會默認有這個#。

    2).BrowserRouter組件:

    很多情況下我們則不是這種情況,我們不需要這個#,因為它看起來很怪,這時我們就需要用到BrowserRouter。

    記?。喝サ舳嘤嗟腂rowserRouter 標簽,整個項目中,寫一個BrowserRouter 標簽就行,否則,react路由時,不能正常渲染,需要刷新,才能渲染。

    4、路由的配置

    1). Route組件:

    vue的路由配置是分離式的,在一個單獨的文件里進行路由配置。react是嵌套式的,路由配置就寫在組件內部,任何組件都可以寫。

    vue中是在json數組里寫好配置,然后傳入vueRouter對象,而React直接使用組件BrowserRouter(或HashRouter)和Route進行配置。

    Route:路徑和組件的對應關系。職責就是當頁面的訪問地址與 Route 上的 path 匹配時,就渲染出對應的組件

    如:

    ? ?

    ? ?

    5、路由匹配到的組件展示在何處

    也是使用Route組件。即:Route組件既是路由配置,又是渲染組件的地方。

    6、路由的跳轉

    聲明式導航 vue中使用Router-Link組件,react中使用Link或NavLink 組件進行路由跳轉導航

    1).Link:

    主要屬性是to,to可以接受string或者一個object,來控制url。

    去About

    去About

    ?

    2).NavLink:

    它可以為當前選中的路由設置類名、樣式以及回調函數等。to屬性跳轉路徑,activeClassName當元素處于活動狀態(tài)時應用于元素的樣式

    去About

    去About

    編程式導航

    ?

    this.props.history.push({pathname:'/about'})

    ?

    注意:

    如果當前組件不是通過路由跳轉過來的,那么,當前組件的props里,沒有history。

    6、基本使用步驟:

    1)、下載路由模塊:

    npm install --save react-router-dom

    2)、創(chuàng)建若干個組件

    創(chuàng)建components文件夾,并建立若干個組件

    如:About.js,InBox.js,Goodslist,

    3)、路由配置:

    src/index.js

    //引入模塊

    import { BrowserRouter, Route } from "react-router-dom";

    ?

    ReactDOM.render(

    ?

    ? ? ?

    ? ? ?

    ? ? ?

    ?,

    ?document.getElementById('root')

    );

    4)、路由跳轉

    聲明式導航

    在App.js里做鏈接跳轉

    src/app.js

    ?

    import { Link } from 'react-router-dom'

    ?

    function App() {

    ?return (

    ? ?

    ? ? ?

      ? ? ? ? ?

    • About
    • ? ? ? ? ?

    • Inbox
    • ? ? ? ?

    ? ?

    );

    }

    擴展:

    可以嘗試把 app.js里的 link 標簽換成a標簽??纯错撁媸遣皇菚虚W動。

    (PS:如果使用a標簽,在每次點擊時,頁面被重新加載,標簽會避免這種狀況發(fā)生。當 你點擊時,url會更新,組件會被重新渲染,但是頁面不會重新加載)

    二級路由

    直接在InBox組件里在寫路由配置就行:

    ? ?

    ? ?

    鉛筆  | 

    橡皮

    ? ?


    ? ?

    ? ?

    ? ?

    7、路由傳參

    路由傳參完成的是組件之間的數據傳遞(組件傳值)

    1)、params

    路由配置:

    路由跳轉(傳值):

    聲明式導航:

    鉛筆

    編程式導航:

    this.props.history.push(?'/Inbox/01008' )

    取值(接值):

    this.props.match.params.id

    優(yōu)勢 : 刷新,參數依然存在

    缺點 : 只能傳字符串,并且,如果傳的值太多的話,url會變得長而丑陋。

    場景:傳遞一個參數時使用。

    2)、query

    路由配置:

    不用改變路由配置表。

    路由跳轉(傳值):

    聲明式導航:

    鉛筆

    編程式導航:

    this.props.history.push(?{pathname:'/Inbox',query:{id:'01009'}} )

    取值:

    this.props.location.query.id

    優(yōu)勢:傳參優(yōu)雅,傳遞參數可傳對象;

    缺點:刷新地址欄,參數丟失

    3)、state

    同query差不多,只是屬性名不一樣,而且state傳的參數是加密的,query傳的參數是公開的,只需要把query改為state即可。

    路由配置:

    不用改變路由配置表。

    路由跳轉(傳值):

    聲明式導航

    鉛筆

    編程式導航

    this.props.history.push({pathname:'/Inbox',state:{id:"01"}});

    取值:

    this.props.location.state.id

    優(yōu)勢:傳參優(yōu)雅,傳遞參數可傳對象

    缺點:刷新地址欄,(hash方式會丟失參數,Browser模式不會丟失參數)

    4)、search

    路由配置:

    不用改變路由配置表。

    傳值:

    聲明式導航

    鉛筆

    編程式導航

    this.props.history.push({pathname:'/Inbox',search:'?a=1&b=2'})

    取值:

    this.props.location.search

    用location.search所獲取的是查詢字符串(如:?a=1&b=2),所以,還需要進一步的解析,自己自行解析,也可以使用第三方模塊:qs,或者nodejs里的query-string

    8、路由上下文

    在react-router里面,如果組件是通過路由跳轉的,那么它會把路由相關的API掛在了組件的props上(vue-Router使用的$router和$route),并且分為history,location,match。

    history:歷史,用來跳轉的,并做歷史記錄。有函數:push(),replace(),go() …………

    location:地址,地址欄上路徑,并保存著query,state,search等數據。

    match:匹配,匹配到的路徑,有params

    9、非路由跳轉的組件(標簽的方式)如何獲取路由上下文

    先看一下,組件在頁面上展示的兩種情況下,對應的props對象的內容

    1)、標簽名的方式直接展示的組件(沒有屬性),props是空

    2)、路由跳轉的方式展示的組件(就算沒有屬性),props會自動增加屬性:history,match,location。

    如果用標簽名的方式,還想獲取到路由上下文,有以下解決方案:

    通過屬性傳遞 首先要求,當前組件是路由跳轉過來的,然后把路由上下文通過屬性的方式傳遞給子組件 通過withRouter包裝 接收路由上下文的組件用withRouter函數(這是一個高階組件)進行包裹

    import {withRouter} from 'react-router-dom'

    ?

    class 組件 extends Component{

    ? ?

    }

    ?

    export default withRouter(組件)

    exact屬性:

    react的路由匹配(path后的路徑和地址欄路徑)默認是模糊的(path后面的路徑只要包含在地址欄的路徑里就能匹配上對應的組件)

    如果想使用嚴格匹配(path后面的路徑和地址欄的路徑完全相同),那么,把Route組件的exact屬性設置為true。

    假如,有如下路由配置:

    ? ?

    ? ?

    地址欄中輸入:

    http://localhost:3000/My

    那么路徑 “/My”,匹配到的路徑是: “/” 和 “/My”,并且,在瀏覽器會把匹配到的所有組件的內容進行顯示。

    如果希望 路徑 /My 值匹配 path=“/My”,那么,這么寫:

    404

    在路由配置里不設置path屬性,那么,就總是會匹配上。404頁面就需要這樣做(當然還得結合Switch)

    總是會匹配

    Switch

    排他性匹配。

    react默認的路由匹配是包容性的,即:匹配到的多個路由對應的所有組件會同時被顯示在頁面上。如果只希望顯示匹配到的第一個組件(換句話說:匹配到的第一個符合要求的路徑后,其它路徑就不再做匹配),那么使用switch。

    假如,有如下路由配置:

    ? ?

    ? ?

    ? ?

    ? ? 總是會匹配

    地址欄中輸入:

    http://localhost:3000/My

    那么路徑 “/My”,匹配到的路徑是:“/My” 和最后一個,即,在瀏覽器上從上到下會顯示組件My和Error的內容。

    路由配置改成如下:

    ?

    ? ?

    ? ? ?

    ? ? ?

    ? ? ? 總是會匹配

    ? ?

    ?

    Switch應該寫在Route的外層,而且,在Switch和Route中間不能有任何組件

    地址欄中輸入:

    http://localhost:3000/My

    那么路徑 “/My”,只會讓瀏覽器顯示匹配到的第一個組件My。

    區(qū)分:exact和switch

    exact:表示路徑匹配規(guī)則,exact={true} 表地址欄的路徑和 路由配置中path一定要完全相等

    switch:表示排他性,即:一旦地址欄上路徑和某個path匹配成功后,就不再匹配其它path。

    Redirect

    附:路由提供組件的詳解(自行研究)

    組件及其作用:

    組件作用路由模式BrowserRouter約定模式 為 history,使用 HTML5 提供的 history API 來保持 UI 和 URL 的同步路由模式HashRouter約定模式 為 hash,使用 URL 的 hash 來保持 UI 和URL 的同步聲明式跳轉NavLink聲明式跳轉 還可以約定 路由激活狀態(tài)聲明式跳轉Link聲明式跳轉 無激活狀態(tài)重定向Redirect重定向 ~~ replace匹配并展示Route路由配置和展示。匹配組件,并展示組件。即匹配成功后,組件立即被替換成匹配的組件排他性匹配Switch排他性匹配。如果不想使用包容性,那么使用Switch。高階組件withRouter把不是通過路由切換過來的組件中,將 history、location、match 三個對象傳入props對象上(高階組件)

    結構

    BrowserRouter|HashRouter (整個項目里只能有一個BrowserRouter|HashRouter ) App(或其它組件)

    NavLink|Link Route(外層可以包裹Switch) Redirect

    子組件

    NavLink|Link Route ...

    BrowserRouter

    屬性類型作用basenamestring所有位置的基本URL。如果您的應用是從服務器上的子目錄提供的,則需要將其設置為子目錄。格式正確的基本名稱應以斜杠開頭,但不能以斜杠結尾getUserConfirmationFunction用于確認導航的功能。默認使用window.confirm。

    Route

    屬性類型作用pathstring |object路由匹配路徑。沒有path屬性的Route 總是會 匹配exactboolean為true時,要求全路徑匹配(/home)。路由默認為“包含”的(/和/home都匹配),這意味著多個 Route 可以同時進行匹配和渲染componentFunction |component在地址匹配的時候React的組件才會被渲染,route props也會隨著一起被渲染renderFunction內聯(lián)渲染和包裝組件,要求要返回目標組件的調用

    Link

    屬性類型作用tostring | 對象{pathname:,search:,hash:}要跳轉的路徑或地址replaceboolean是否替換歷史記錄

    NavLink

    屬性類型作用tostring|對象{pathname:,search:,hash:}要跳轉的路徑或地址replaceboolean是否替換歷史記錄activeClassNamestring當元素被選中時,設置選中樣式,默認值為 activeactiveStyleobject當元素被選中時,設置選中樣式

    Switch

    該組件用來渲染匹配地址的第一個Route或者Redirect,僅渲染一個路由,排他性路由,默認全匹配(場景:側邊欄,引導選項卡等)

    屬性類型作用locationstring objectchildrennode

    Redirect

    該組件用來渲染匹配地址的第一個Route或者Redirect,僅渲染一個路由,排他性路由,默認全匹配(場景:側邊欄和面包屑,引導選項卡等

    屬性類型作用fromstring來自tostring object去向pushboolean添加歷史記錄exactboolean嚴格匹配sensitiveboolean區(qū)分大小寫

    補一下:async和await

    概念:

    async和await是回調地獄的終極解決方案,是ES7新增的兩個關鍵字。

    async:異步

    await:等待

    功能:

    將異步操作按同步操作的方式書寫,即上一步未執(zhí)行完,會阻塞當前函數的線程,不影響主線程

    知識點:

    1)、async:修飾的函數表示函數里面有異步操作,返回值是Promise對象

    2)、await:

    2.1)、await必須放在async修飾的函數里。

    2.2)、await修飾代碼后,await所在行的后面的行的代碼會等待await修飾的代碼執(zhí)行完畢。

    2.3)、一般來說,await后跟 promise對象,或者返回Promise對象的函數;

    2.4)、await 修飾函數(或者promise對象)后,那么,返回值變成了Promise對象中resolve的參數;

    如 Let res = await fn(); //res是函數fn返回的Promise對象里的resolve的參數。

    2.5)、如果要拿到reject里參數,就使用try catch。

    如:

    try{

    ?

    Let res = await fn(); //res是 fn函數里Promise的resolve的參數

    ?

    }catch(err){

    ?

    err 是 fn函數里Promise的reject的參數

    ?

    }

    完整代碼:

    原理使用promise的axios代碼:

    ?

    created(){ ?

    ? ?axios({

    ? ? ? ?url:"/books",

    ? ? ? ?method:"get",

    ? ? ? ?params:{

    ? ? ? ? ? ?type:this.type

    ? ? ? }

    ? })

    ? .then(res=>{

    ? ? ? ?this.books = res.data; ? ? ? ?

    ? })

    ? .catch(err=>{

    ? ? ? ?console.log("服務器出錯");

    ? }) ? ? ?

    },

    ?

    經過async和await改造后的代碼(沒有了回調);

    ?

    async created(){ ?

    ? ?try {

    ? ? ? ?// 1、發(fā)送請求

    ? ? ? ?let res = await axios({

    ? ? ? ? ? ?url:"/books",

    ? ? ? ? ? ?method:"get",

    ? ? ? ? ? ?params:{

    ? ? ? ? ? ? ? ?type:this.type

    ? ? ? ? ? }

    ? ? ? })

    ?

    ? ? ? ?//2、把獲得的數據賦給變量

    ? ? ? ?this.books = res.data;

    ?

    ? } catch (error) {

    ? ? ? ?console.log("服務器出錯",error); ?

    ? }

    }

    如果想看細節(jié),請查看以下兩篇文章:

    async和await的理解

    原生JS面試題:async和await_原生js代碼的await-CSDN博客

    原生JS面試題:什么是async,什么是await,async和await的區(qū)別,async和await的理解

    原生JS面試題:什么是async,什么是await,async和await的區(qū)別,async和await的理解_js await和async-CSDN博客

    選擇器沖突解決方案

    一、命名空間 BEM

    BEM(Block, Element, Modifier)是由Yandex團隊提出的一種前端命名規(guī)范。其核心思想是將頁面 拆分成一個個獨立的富有語義的塊(blocks)。在某種程度上,BEM和OOP是相似的。 Block:代表塊(Block):也是模塊的意思 Element:元素(Element): Modifier: 修飾符(Modifier) 無論是什么網站頁面,都可以拆解成這三部分

    滑動驗證頁面

    二、模塊化

    import 變量 ?from './css/xx.module.css'

    ?

    ?

    //配置1 (腳手架默認配置上了)

    //webpack配置 "style-loader!css-loader?modules" | module:true

    //問題:所有css都需要模塊化使用

    ?

    //配置2

    //改名xx.css -> xx.module.css

    //需要模塊化的才修改,不影響其他非模塊化css寫法

    三、scss

    1、安裝: node-sass

    2、sass文件

    /*定義scss*/

    $bg-color: #399;

    ?

    .box{

    ?background: $bg-color;

    }

    ?

    引入sass

    ?

    //1)、普通引入

    import 'xx/xx.scss'

    ?

    ?

    ?

    ?

    ?

    //2)、模塊化

    import style from xx.module.scss

    ?

    ?

    ?

    引入全局的(公共的)scss

    局部scss文件內部: @import './全局.scss' webpack配置一次,局部scss內部直接使用

    //1. 安裝插件 : sass-resources-loader

    //2. 配置修改webpack.config.js

    ?

    {

    ?test:sassRegex,

    ?...

    ?use: [

    ? {loader:'style-loader'},

    ? {loader:'css-loader'},

    ? {loader:'sass-loader'},

    ? {

    ? ? ?loader: 'sass-resources-loader',

    ? ? ?options:{

    ? ? ? ?resources:'./src/xx/全局主題.scss'

    ? ? }

    ? }

    ]

    }

    ?

    注意: loader:'css-loader?modules' ?modules 模塊化時需要添加 resources 指向作用域在項目環(huán)境下

    react hooks(重中之重)

    Hooks 介紹

    Hook 簡介 – React

    react hooks是v16.8新增的特性, 他允許你 在不寫類組件(即:函數式組件)的情況下操作state 和react的其他特性(如:生命周期的鉤子函數)。

    hooks 只是多了一種寫組件的方法,使編寫一個組件更簡單更方便,同時可以自定義hook把公共的邏輯提取出來,讓邏輯在多個組件之間共享。

    Hooks 是什么? Hook 是一個特殊的函數,它可以讓你“鉤入” React 的特性。例如,useState 是允許你在 React 函數組件中添加 state 的 Hook。

    以前,函數式組件里面沒有state,所以,無狀態(tài)組件我們用函數寫,或者說函數式組件是無狀態(tài)組件。而現在有了Hook后,函數式組件里,也可以使用state了。當然還有其它Hook。

    使用規(guī)則

    Hook可讓您在不編寫類(組件)的情況下使用狀態(tài)(state)和其他React功能 只能在頂層調用Hooks 。不要在循環(huán),條件或嵌套函數中調用Hook 只能在functional component或者自定義鉤子中使用Hooks 鉤子在類內部不起作用,沒有計劃從React中刪除類

    useState (使用狀態(tài)):

    格式:

    1、定義狀態(tài):

    const [狀態(tài)名,更新狀態(tài)的函數] = React.useState(初始值|函數);

    ?

    如:

    1)、基本類型的狀態(tài)

    聲明一個新的叫做 “count” 的 state 變量,初始值為0 。

    ?

    const [count, setCount] = React.useState(0); //useState函數返回的是數組

    const [name, setName] = React.useState('鄭天鴻'); //useState函數返回的是數組

    ?

    ?

    相當于類組件中的

    this.state={

    ? ?count :0

    }

    ?

    2)、引用類型的狀態(tài)

    const [person, setPerson] = React.useState({name: '張三瘋', age: 18,sex:"女"})

    const [person, setPerson] = React.useState(() => ({name: '張三瘋', age: 18,sex:"女"}))

    ?

    2、讀取值:

    {count}

    {person.name} ? {person.age}

    ?

    3、修改值: ?

    ?setCount(5);

    ?

    ?//對于引用類型,不能局部更新(即:不能只改某個屬性),所以,需要使用擴展運算符先拷貝以前所有的屬性

    ?setPerson({

    ? ? ...person, //拷貝之前的所有屬性

    ? ? age:person.age+1,

    ? ? name: '張四瘋' //這里的name覆蓋之前的name

    })

    注意:

    首先,需要知道,函數式組件重新渲染時,會執(zhí)行函數體里的所有代碼,

    那么,當函數式組件重新渲染時,會不會再次把狀態(tài)的值恢復成初始值呢?答案是:不會。后續(xù)組件重新渲染時,會使用最后一次更新的狀態(tài)值

    【官網解釋: React 會確保 setState 函數的標識是穩(wěn)定的,并且不會在組件重新渲染時發(fā)生變化 】

    示例代碼:

    import React,{useState} from 'react';

    ?

    function App() {

    // 聲明一個叫 "count" 的 state 變量

    ?const [count,setCount] = useState(0); //在App組件重新后,useState 返回的第一個值將始終是更新后最新的 count。

    ?

    ?return (

    ? ?

    ? ? ?

    {count}

    ? ? ?{setCount(count+1)}} />

    ? ?

    );

    }

    對應的函數class組件:

    class App extends React.Component {

    ?state = {

    ? ? ?count:0

    }

    ?render = () => (

    ? ?

    ? ? ?

    {this.state.count}

    ? ? ?

    onClick={()=>this.setState({count:this.state.count+1})} />

    ? ?

    )

    }

    我們之前把函數式的組件叫做“無狀態(tài)組件”。但現在我們?yōu)樗鼈円肓耸褂?React state 的能力

    再如:

    ?

    function App() {

    ?const [person, setPerson] = React.useState({name: '張三瘋', age: 18})

    ?const onClick = () =>{

    ? ?//setPerson不可以局部更新,如果只改變其中一個,那么整個數據都會被覆蓋,所以,需要使用擴展運算符先拷貝以前所有的屬性

    ? ?setPerson({

    ? ? ? ?...person, //拷貝之前的所有屬性

    ? ? ? ?age:person.age+1,

    ? ? ? ?name: '張四瘋' //這里的name覆蓋之前的name

    ? })

    }

    ?

    ?return (

    ? ?

    ? ? ? ?

    name:{person.name}

    ? ? ? ?

    age:{person.age}

    ? ? ? ?

    ? ?

    );

    }

    useEffect 處理副作用 (生命周期鉤子函數)

    可以使得你在函數組件中執(zhí)行一些帶有副作用的方法,天哪,“副作用”(大腦中無數個????)。

    每當 React組件更新之后,就會觸發(fā) useEffect,在第一次的render 和每次 update 后,useEffect都會觸發(fā),不用再去考慮“初次掛載”還是“更新”。React 保證了每次運行 effect 的同時,DOM 都已經更新完畢。

    你可以把 useEffect Hook 看做 componentDidMount,componentDidUpdate 和 componentWillUnmount 這三個函數的組合。

    我們在函數式組件里,沒有 componentDidMount,componentDidUpdate 和 componentWillUnmount,用useEffect。即:當數據發(fā)生變化后,渲染到組件上,組件渲染完畢后,就會調用useEffect。

    格式:

    useEffect(回調函數,[依賴]); //在render之后觸發(fā)useEffect,進一步調用回調函數

    1、useEffect的無條件執(zhí)行(只有一個參數)

    import React,{useState,useEffect} from 'react';

    ?

    function App() {

    ?const [count,setCount] = useState(0);

    ?

    ?//useEffect:相當于 componentDidMount,componentDidUpdate

    ?useEffect(()=>{

    ? ? ?console.log("userEffect");

    ? ? ?document.title = count;

    });

    ?

    ?return (

    ? ?

    ? ? ?

    {count}

    ? ? ?{setCount(count+1)}} />

    ? ?

    );

    }

    2、useEffect的條件執(zhí)行(useEffect的第二個參數)

    當useEffect只有一個參數時,會無條件執(zhí)行,那么,當發(fā)送請求時(頁面的初始數據來自后端),一旦把請求放在useEffect里,就會無休止的執(zhí)行。因為,當請求的數據回來后,引起組件的更新,組件更新后,再次觸發(fā)useEffect,再次發(fā)送請求,再次組件更新………………,陷入到了無限的死循環(huán)。怎么辦呢?可以使用useEffect的第二個參數。

    第二個參數表示:useEffect是否再次觸發(fā) 是 依賴于某個狀態(tài)的變化。當為空數組時,表示不會二次觸發(fā)。即:componentDidMount時會觸發(fā),componentDidUpdate不會觸發(fā)。

    換種解釋方式:

    1、這是格式: useEffect(回調函數,依賴的數組)

    2、數組里是回調函數是否執(zhí)行的條件,(即:數組里的狀態(tài)發(fā)生變化時,才調用回調函數)

    3、如果是空數組,表示任何狀態(tài)發(fā)生變化,都不會調用回調函數。相當于componentDidMount

    如下代碼,由于依賴是空,所以,useEffect只表示componentDidMount。

    useEffect( async ()=>{

    ? ? ?let data = await getBooks(); ?//發(fā)送請求的代碼已經封裝 ? ?

    ? ? ?setBooks(data); ? ? ?

    },[]);

    如下代碼,表示componentDidMount,和 count變化后引起的componentDidUpdate。

    useEffect( async ()=>{

    ? ? ?let data = await getBooks(); ?//發(fā)送請求的代碼已經封裝 ? ?

    ? ? ?setBooks(data); ? ? ?

    },[count]);

    useContext(使用狀態(tài)樹傳參)

    Context狀態(tài)樹的使用,比較復雜,特別是使用Consumer時。

    useContext這個hook能讓Context使用起來變得非常簡單。不需要再使用Consumer。使用useContext就能拿到context狀態(tài)樹里的值。

    const value = useContext(context對象);

    useContext函數的解釋:

    參數: context 對象(React.createContext 的返回值)

    返回值: context對象的當前值(由上層組件中距離當前組件最近的 的 value prop 決定)

    當組件上層最近的 Provider 更新時,該 Hook 會觸發(fā)重新渲染 。

    示例:

    //context/index.js 創(chuàng)建context對象。

    ?

    import {createContext} from "react";

    ?

    export default createContext({count:0});

    ?

    //主入口文件 index.js

    ?

    import ReactDOM from 'react-dom';

    import App from './App';

    import Context from "./context"

    ?

    let count = 10;

    ?

    ReactDOM.render(

    ? ?

    ? ?

    ? ?,

    ?document.getElementById('root')

    );

    ?

    ?

    //孫子組件 App-->User-->UserAdd.js

    ?

    import myContext from "../../../context";

    import {useContext} from "react";

    ?

    export default (props)=>{

    ? ?let context = useContext(myContext);

    ? ?console.log(context);

    ? ?return (

    ? ? ? ?

    ? ? ? ? ? ?

    我是用戶添加頁面

    ? ? ? ? ? ?

    count:{context}

    //此處使用比起consumer是不是簡單的多得多得多了呢?

    ? ? ? ? ? ?

    ? ? ? ?

    ? );

    }

    useCallBack

    一、概念和作用

    1、memo高階函數:

    memo解決的是函數式組件的無效渲染問題,當函數式組件重新渲染時,會先判斷數據是否發(fā)生了變化。相當于類組件的PureComponent(默認提供ShouldComponentUpdate)

    2、useCallback:

    1)、useCallback會返回一個函數的memoized(記憶的)值

    2)、在依賴不變的情況下,多次定義(如:函數)的時候,返回的值是相同的 。

    3)、格式:

    let 新的函數 = useCallback(曾經的函數, [依賴的值])

    二、使用場景:

    1、memo高階函數的使用場景:

    不論父組件是什么類型的組件,子組件是否渲染 :

    1)、 如果子組件是類組件(繼承自PureComponent)

    那么是否渲染由props和state是否改變決定;

    2)、如果子組件是函數式組件

    只要父組件渲染,子組件會無條件渲染。如下是代碼示例:

    //父組件:

    ?

    import { useState } from "react";

    import SonFn from "./SonFn";

    ?

    export default () => {

    ? ?console.log("父組件");

    ? ?const [count, setCount] = useState(1);

    ?

    ? ?let changeCount = () => {

    ? ? ? ?setCount(count + 1);

    ? }

    ?

    ? ?return (

    ? ? ? ?<>

    ? ? ? ? ? ?

    useCallback

    ? ? ? ? ? ?

    {count}

    ? ? ? ? ? ?

    ? ? ? ? ? ?


    ? ? ? ? ? ?

    ? ? ? ?

    ? )

    }

    ?

    //子組件:

    ./SonFn.js

    ?

    export default ()=>{

    ? ?console.log("子組件");

    ? ?return (

    ? ? ? ?

    ? ? ? ? ? ?

    子組件(函數式組件)

    ? ? ? ?

    ? )

    } ?

    只要點擊按鈕"修改count”,父組件就會刷新,而子組件SonFn也會無條件渲染(這是無效的渲染)。

    3)、解決方案:

    把子組件用高階函數memo進行包裹,就能解決子組件的無條件渲染問題,即:子組件的渲染就會由props和state決定,有點像類組件繼承自PureComponent的感覺。

    如下是代碼(只需要把子組件的代碼進行修改就行):

    //子組件:

    import React,{memo} from 'react'

    ?

    const SonFn = ()=>{

    ? ?console.log("子組件");

    ? ?return (

    ? ? ? ?

    ? ? ? ? ? ?

    子組件(函數式組件)

    ? ? ? ?

    ? )

    }

    ?

    export default memo(SonFn);

    2、useCallback的使用場景:

    父組件是函數式組件,子組件也是函數式組件(并且用memo包裹)

    1)、子組件的屬性是數據:

    如果數據不變化,那么子組件不渲染,如果數據發(fā)生變化,那么子組件渲染。這里就沒有性能問題。

    //父組件

    ?

    import { useState,useCallback } from "react";

    import SonFn from "./SonFn";

    ?

    export default () => {

    ? ?console.log("父組件UseCallback");

    ? ?const [count, setCount] = useState(1);

    ?

    ? ?let changeCount = () => {

    ? ? ? ?setCount(count + 1);

    ? }

    ?

    ? ?return (

    ? ? ? ?<>

    ? ? ? ? ? ?

    useCallback1

    ? ? ? ? ? ?

    {count}

    ? ? ? ? ? ?

    ? ? ? ? ? ?


    ? ? ? {/*此處給子組件傳入了數據count,count只要發(fā)生變化,子組件就會重新渲染*/}

    ? ? ? ? ? ?

    ? ? ? ?

    ? )

    }

    ?

    //子組件:

    ./SonFn.js

    ?

    import React,{memo} from 'react'

    ?

    const SonFn = ({count})=>{

    ? ?console.log("子組件");

    ? ?return (

    ? ? ? ?

    ? ? ? ? ? ?

    子組件(函數式組件)

    ? ? ? ? ? ?

    {count}

    ? ? ? ?

    ? )

    }

    ?

    export default memo(SonFn);

    ?

    ?

    2)、子組件的屬性是函數(其實,只要是引用類型)時,就會出現問題(無效渲染):

    父組件刷新(重新渲染)了,子組件依然會刷新(重新渲染)。因為,父組件(函數式)每次刷新時,函數都會重新定義,那么傳給子組件的函數屬性必然會發(fā)生變化。所以子組件會刷新,如下是示例代碼:

    //父組件:

    ?

    import { useState } from "react";

    import SonFn from "./SonFn";

    ?

    export default () => {

    ? ?console.log("父組件");

    ? ?const [count, setCount] = useState(1);

    ?

    ? ?let changeCount = () => {

    ? ? ? ?setCount(count + 1);

    ? }

    ?

    ? ?let increment = ()=>{

    ? ? ? ?console.log("increment");

    ? }

    ?

    ? ?return (

    ? ? ? ?<>

    ? ? ? ? ? ?

    useCallback

    ? ? ? ? ? ?

    {count}

    ? ? ? ? ? ?

    ? ? ? ? ? ?


    ? ? ? ? ? ?

    ? ? ? ?

    ? )

    }

    ?

    //子組件:

    ./SonFn.js

    ?

    import React,{memo} from 'react'

    ?

    const SonFn = ()=>{

    ? ?console.log("子組件");

    ? ?return (

    ? ? ? ?

    ? ? ? ? ? ?

    子組件(函數式組件)

    ? ? ? ?

    ? )

    }

    ?

    export default memo(SonFn);

    ?

    3)、解決方案:把傳給子組件的函數屬性,用useCallback包裹。

    格式:

    let 新的函數 = useCallback(曾經的函數, [依賴的值])

    如下是修改后的代碼(只需要修改父組件的代碼):

    以下代碼把increment函數進行了包裹

    export default () => {

    ? ?console.log("父組件");

    ? ?const [count, setCount] = useState(1);

    ?

    ? ?let changeCount = () => {

    ? ? ? ?setCount(count + 1);

    ? }

    ?

    ? ?let increment = useCallback(()=>{

    ? ? ? ?console.log("increment");

    ? },[]) // 該函數永遠不會重新定義(第二個參數空數組表示,任何數據發(fā)生變化,回調函數都不會再次定義)

    ? ?

    ? ?/*

    ? let increment = useCallback(()=>{

    ? ? ? console.log("increment");

    ? },[count]) // 當count的值發(fā)生變化是,該函數才會重新定義

    */

    ? ?

    ? ?return (

    ? ? ? ?<>

    ? ? ? ? ? ?

    useCallback

    ? ? ? ? ? ?

    {count}

    ? ? ? ? ? ?

    ? ? ? ? ? ?


    ? ? ? ? ? ?

    ? ? ? ?

    ? )

    }

    三、總結:

    1、“萬惡之源” :函數式組件每次重新渲染時,都會把函數體里的所有代碼執(zhí)行一遍。

    2、useCallback解決的是 防止函數式組件里的 子函數(閉包) 多次被定義。既就是:useCallback是保證函數式組件重新渲染時,組件里的函數(閉包)只被定義一次。

    useCallback 記憶函數(詳細版的)

    react hooks系列_useCallback_react hooks usecallback-CSDN博客

    useMemo 記憶組件

    ?//格式

    ?useMemo(函數,數組);

    ?

    ?//意思:

    ?// 當數組中的其中一個元素,發(fā)生變化時,就會 調用 函數 。

    如: const nameStr = useMemo(()=>genName(name),[name])

    表示,當name發(fā)生變化時,才會調用 ()=>genName(name)函數

    如: const nameStr = useMemo(()=>genName(name),[name,age])

    表示,當name或者age發(fā)生變化時,都會調用 ()=>genName(name)函數

    以下代碼中,如果不使用useMemo,當我們點擊“修改年齡”的按鈕時,也調用了函數genName()。這其實是性能的損耗。

    import React,{useState,useMemo} from "react";

    import './App.css';

    ?

    ?

    function Person({ name, age}) {

    ? ?

    ?console.log("Person函數");

    ? ?

    ?function genName(name) {

    ? ?console.log('genName')

    ? ?return '姓名:'+name;

    }

    ?

    ?let nameStr = genName(name); ?//沒有使用useMemo

    ? ?// 以下代碼有點vue中的watch的感覺:當name發(fā)生變化時,才調用回調函數.

    // const nameStr = useMemo(()=>genName(name),[name]) //此處使用 useMemo

    ?

    ?return (

    ? ?<>

    ? ? ?

    {nameStr}

    ? ? ?


    ? ? ?

    年齡:{age}

    ? ?

    )

    }

    ?

    ?

    function App() {

    ?const [name, setName] = useState('張三瘋')

    ?const [age, setAge] = useState(12)

    ?

    ?return (

    ? ?<>

    ? ? ?

    ? ? ?

    ? ? ?


    ? ? ?

    ? ?

    )

    }

    ?

    export default App;

    區(qū)分useMemo和useCallback:

    useCallback:解決的是:防止無效的函數定義

    useMemo:解決的是:防止無效的函數調用

    useRef 保存引用值

    https://reactjs.bootcss.com/docs/hooks-reference.html#useref

    useRef 返回一個可變的 ref 對象,其(ref 對象) .current 屬性被初始化為傳入的參數(initialValue)。返回的 ref 對象在組件的整個生命周期內保持不變。

    import {useRef} from "react";

    ?

    let refContainer = useRef(initialValue) ?

    ?

    ?

    refContainer.current.dom操作

    一個常見的用例便是命令式地訪問子組件:

    function TextInputWithFocusButton() {

    ? ?//定義了一個ref變量:inputEl

    ?const inputEl = useRef(null);

    ?

    ?const onButtonClick = () => {

    ? ?// `current` 指向已掛載到 DOM 上的文本輸入元素

    ? ?inputEl.current.focus();

    };

    ? ?

    ?return (

    ? ?<>

    ? ? ?

    ? ? ?

    ? ?

    );

    }

    擴展:

    為什么react選擇了函數式組件(剖析原理)_react 為什么推薦函數組建-CSDN博客

    為什么react選擇了函數式組件(剖析原理)

    面試題:

    //請問以下組件里,在執(zhí)行時,會打印的

    function App() {

    ?console.log("app");

    ?const [count,setCount] = React.useState(0);

    ?

    ?useEffect(()=>{

    ? ?setInterval(()=>{

    ? ? ? ?setCount(5)

    ? ? }

    ? ,1000)

    },[]);

    ?

    ?console.log("app:",count);//此次會打印多少:??

    ?

    ?return (

    ? ?

    ? ? ?

    {count}

    ? ?

    );

    }

    ?

    后面的hooks,以后的以后有余力,再看吧

    useLayoutEffect 同步執(zhí)行副作用

    路由相關的hooks

    在非路由跳轉的組件里,要獲取路由上下文對象,除了可以使用高階組件withRouter外,react-router-dom里還提供了hooks。

    useHistory useLocation useParams useRouteMatch

    useHistory():

    useHistory 返回一個 路由上下文上的history 對象

    import { useHistory } from "react-router-dom";

    ?

    export default (props)=>{

    ? ?let history = useHistory();

    ? ?console.log("我是用戶添加頁面:props",props);

    ? ?return (

    ? ? ? ?

    ? ? ? ? ? ?

    我是用戶添加頁面

    ? ? ? ? ? ?history.push("/")} />

    ? ? ? ?

    ? );

    }

    useLocation()

    useLocation() 返回一個路由上下文的 location 對象。

    用戶管理

    ?

    import { useHistory,useLocation } from "react-router-dom";

    ?

    export default ()=>{

    ? ?let location = useLocation();

    ? ?let history = useHistory();

    ? ?console.log("location",location);

    ? ?return (

    ? ? ? ?

    ? ? ? ? ? ?

    我是用戶添加頁面

    ? ? ? ? ? ?history.push("/")} />

    ? ? ? ?

    ? );

    }

    useParams()

    useParams() 返回當前匹配的路徑上的 params

    用戶管理

    import { useHistory,useLocation,useParams } from "react-router-dom";

    export default ()=>{ let location = useLocation(); let history = useHistory(); let params = useParams();

    console.log("location",location);

    console.log("params",params);

    ?

    return (

    ?

    ? ? ?

    我是用戶添加頁面

    ? ? ? history.push("/")} />

    ?

    );

    }

    useRouteMatch

    useRouteMatch 可以有一個參數 path,如果什么都不傳,會返回當前 context 上的 match 的值,一定是 true。如果傳了 path,會比較這個 path 和當前 location 是否 match。

    視情況手寫redux

    狀態(tài)管理(重中之重)

    思想:flux 實現:vuex redux

    redux

    1. Redux是為javascript應用程序提供一個可預測(給一個相同的輸入,必然會得到一個相同的結果)的狀態(tài)容器??梢赃\行于服務端,客戶端,原生應用,從Flux演變而來。簡單容易上手。

    集中的管理react中多個組件的狀態(tài) redux是專門作狀態(tài)管理的js庫,并不是react的插件庫,也可以用在其他js框架中,例如vue,但是基本用在react中

    可以同一個地方查詢狀態(tài),改變狀態(tài),傳播狀態(tài),用在中大項目,如下場景:組件狀態(tài)需要共享,在任何地方都可以拿到,組件需要改變全局狀態(tài),一個組件需要改變另外一個組件的狀態(tài)。創(chuàng)建store實例,其它組件導入并共享這個store實例

    redux成員

    成員作用類型createStore(reducer,state)創(chuàng)建store實例(reducer:對數據的操作,state:倉庫存放的數據)函數combineReducers合并多個reducer函數applyMiddleware安裝中間件,改裝增強redux函數

    store成員

    成員作用類型subscribe(回調函數)訂閱state變化函數dispatch(action)發(fā)送action 給 reducer函數getState()獲取一次state的值函數replaceReducer一般在 Webpack Code-Splitting 按需加載的時候用函數

    數據流動

    component(views)actionreducerstatecomponent(views)展示state轉發(fā)的動作,異步業(yè)務同步業(yè)務處理邏輯, 修改state,并且返回新的state狀態(tài)收集store.dispatch---》-------------》《--subscribe《--getState

    安裝:

    yarn add redux

    示例代碼:

    1)、組件從store中獲取state(數據)):

    //1、創(chuàng)建一個store,

    //創(chuàng)建store需要reducer和state。即就是:創(chuàng)建倉庫時,需要說清楚倉庫中存儲的數據(state),以及對數據的操作(reducer)

    ?

    ./src/store/index.js

    ?

    import { createStore} from "redux";

    import state from "../store/state";

    import reducer from "../store/reducer";

    ?

    //1、 創(chuàng)建倉庫,并且說清楚,倉庫里存儲的數據,以及,對倉庫數據的操作

    // createStore :創(chuàng)建倉庫的

    // state:倉庫里存儲的數據

    // reducer:對倉庫數據的操作

    let store = createStore(reducer,state);

    ?

    export default store;

    ?

    //2、創(chuàng)建state

    //state就是倉促存儲的數據(對象)

    ./src/store/state.js

    ?

    export default {

    ? ?count:0

    }

    ?

    //3、創(chuàng)建reducer

    //對倉庫數據進行操作的函數(函數)。

    //要求:傳入舊的state,返回新的state。

    ?

    ./src/store/reducer.js

    ?

    // reducer要求是個純函數(在函數內部不能修改函數的參數(輸入),要有返回值),它的功能是:傳入舊的state,根據action對state做操作,返回新的state。

    // 參數:

    // state:原始的state

    // action:要做的事情,動作的類型

    // 返回值:必須要有,是新的state(修改后的state)。getState()函數會調用reducer

    ?

    // 因為是純函數,所以,在函數內部,不能修改state和action。

    let reducer = (state,action)=>{

    ? ?if(action.type){

    ? ? ?//對state的操作

    ? }

    ? ?switch(action.type){

    ? ? ? ?case 添加:……………………return 新的state;break; ? ? ?

    ? ? ? ?case 刪除:……………………return 新的state;break; ? ? ?

    ? }

    ? ?return state;//返回新的state

    }

    ?

    export default reducer;

    ?

    ?

    //組件

    ./src/App.js

    import store from "./store/index";

    ?

    class App extends React.Component {

    ?state = {

    ?

    }

    ?render = () => (

    ? ?

    ? ? ?

    ? ? ? ?

    {store.getState().count}

    ? ? ?

    ? ?

    )

    }

    注意:getState()函數內部也調用了reducer。即:getState()獲取的值是reducer的返回值。

    2)、通過組件修改store中的state(數據)):

    注意:修改store中的state(數據)后,還需要把數據響應到組件上,就需要使用 subscribe,并使用組件中狀態(tài)(否則,組件不會重新渲染)。

    修改reducer

    ./src/store/reducer.js

    ?

    let reducer = (state,action)=>{

    ? ?let {type,payload} = action;

    ? ?switch(type){

    ? ? ? ?case "INCREMENT":{

    ? ? ? ? ? ?console.log("加一");

    ? ? ? ? ? ?return {

    ? ? ? ? ? ? ? ?...state,

    ? ? ? ? ? ? ? ?count:state.count+payload

    ? ? ? ? ? }

    ? ? ? }

    ? ? ? ?default:return state;

    ? }

    }

    ?

    //修改App組件的代碼

    ./src/App.js

    class App extends React.Component {

    ?constructor(props){

    ? ?super(props);

    ? ?this.state = {

    ? ? ? ? count:store.getState().count

    ? }

    ?

    ? ?store.subscribe(()=>{

    ? ? ? ?this.setState({

    ? ? ? ? ?count:store.getState().count

    ? ? ? });

    ? })

    }

    ?

    ? ?inc(){

    ? ? ? ?store.dispatch({type:"INCREMENT",payload:10});

    ? }

    ?

    ? ?render = () => (

    ? ? ?

    ? ? ? ?

    ? ? ? ? ?

    {this.state.count}

    ? ? ? ? ?{this.inc()}} /> ?

    ? ? ? ?

    ? ? ?

    ? )

    }

    操作流程總結

    安裝:

    yarn add redux

    ?

    import {createStore} from 'redux'

    ?

    //一、創(chuàng)建reducer

    //reducer:對倉庫(數據)的操作

    //參數:

    // state:傳入的舊數據(原始數據)

    // action:對數據的操作

    //返回值:操作后的新的數據

    ?

    const reducer = (state,action)=>{

    ?let {type,payload}=action ? ?

    ?swtich (type){

    case XXXXX :{

    ? ?//數據的邏輯處理

    ? ? return {

    ? ? ? ? ?...state,

    ? ? ? ? ?屬性名:新的值

    ? ? }

    } ?

    default:

    ? ?return state

    }

    }

    ?

    //二、創(chuàng)建state對象

    // 倉庫里的數據

    export default {

    ? ?count:0

    }

    ?

    //三、創(chuàng)建store對象(倉庫)

    //使用createStore(對倉庫的操作,倉庫的數據)

    store = createStore(reducer,state)

    export default store;

    ?

    //四、在組件內部使用倉庫(如:獲取倉庫的數據,修改倉庫的數據,添加,刪除)

    ?

    import store from '...'

    ?

    store.getState() //獲取狀態(tài),執(zhí)行一次

    ?

    store.dispatch({type:xxx,payload:ooo}) //發(fā)送action給reducer type是必傳參數

    ?

    store.subscribe(回調) ?//訂閱 state 更新state時觸發(fā)

    提取并定義 Action

    ./src/store/actions.js

    ?

    export const increment = payload =>({

    ? ?type:"INCREMENT",

    ? ?payload

    })

    ?

    export const decrement = payload =>({

    ? ?type:"DECREMENT",

    ? ?payload

    })

    ?

    App.js組件

    ./src/App.js

    ?

    import { increment } from "./store/actions";

    ?

    store.dispatch(increment(10));

    action里處理異步

    需要安裝中間件 redux-thunk ,redux-thunk可以增強dispatch函數的功能:讓dispatch可以接受一個函數作為參數。

    ./src/store/index.js

    ?

    //安裝中間件改裝 redux

    import {createStore,applyMiddleware} from 'redux'

    import thunk from 'redux-thunk'

    ?

    let store = createStore(reducer,state,applyMiddleware(thunk));

    ?

    ./src/store/actions.js

    ?

    //處理異步

    export let ADD =()=>((dispatch)=>{

    ? ?axios({

    ? ? ? ?url:"http://localhost:3000/inc",

    ? })

    ? .then(res=>{

    ? ? ? ?dispatch({

    ? ? ? ? ? ?type:"INCREMENT",

    ? ? ? ? ? ?payload:res.data.num

    ? ? ? })

    ? })

    })

    ?

    ?

    App組件

    ./src/App.js

    ?

    import { increment,decrement,ADD } from "./store/actionCreators";

    ?

    store.dispatch(ADD());

    combineReducers提取reducer

    當應用邏輯逐漸復雜的時候,我們就要考慮將巨大的 Reducer 函數拆分成一個個獨立的單元,這在算法中被稱為 ”分而治之“,拆分后的reducer 在 Redux 中實際上是用來處理 Store 中存儲的 State 中的某一個數據,一個 Reducer 和 State 對象樹中的某個屬性對應。 ? 把一個大的reducer拆成若干個小的。注意:把大的state也分開,并且放在每個reducer。也就是說,每個reducer里面寫上它要操作的數據,這樣的話,在一個reducer里包含了,數據(state)和數據的操作(reducer)。

    // ./src/plugins/myRedux.js

    ?

    import {createStore,applyMiddleware,combineReducers} from 'redux'

    import thunk from 'redux-thunk'

    import count from "../store/reducers/count";

    import todos from "../store/reducers/todos";

    ?

    let rootReducer = combineReducers({

    ? ?todos:todos,

    ? ?count:count

    });

    //去掉了第二個參數state(因為state拆分到了每個reducer里了)

    export default createStore(rootReducer,applyMiddleware(thunk));

    ?

    ?

    // ./src/store/reducers/count.js

    ?

    // 是當前reducer要操作的數據

    let initCount = 8;

    ?

    const count = (count=initCount,action)=>{ ? ?

    ? ?switch(action.type){

    ? ? ? ?case "INCREMENT":{

    ? ? ? ? ? ?return count+action.payload;

    ? ? ? }

    ? ? ? ?case "DECREMENT":{

    ? ? ? ? ? ?return count-action.payload;

    ? ? ? }

    ? ? ? ?default: return count;

    ? }

    }

    ?

    export default count;

    ?

    ?

    // ./src/store/reducers/todos

    let initState=[] //當前reducer所操作的數據,放在里自己的模塊里。

    ?

    const todos = (todos, action) => {

    ?switch (action.type) {

    ? ?case "ADD_TODO": {

    ? ? ?return [

    ? ? ? ?...todos,

    ? ? ? {

    ? ? ? ? ?id: action.id,

    ? ? ? ? ?text: action.text,

    ? ? ? ? ?completed: false

    ? ? ? }

    ? ? ]

    ? }

    ?

    ? ?case "REMOVE_TODO": {

    ? ? ?const { id } = action;

    ? ? ?todos.map((item,index) => item.id ===id && todos.splice(index, 1));

    ? ? ?return [...todos]

    ? }

    ?

    ? ?case "CHECK_TODO": {

    ? ? ?const { id } = action;

    ? ? ?todos.map((item,index) => item.id ===id && (todos[index].completed=!todos[index].completed));

    ? ? ?return [...todos]

    ? }

    ?

    ? ?default:

    ? ? ?return todos;

    }

    };

    ?

    export default todos;

    ?

    //刪除掉state文件。

    ?

    //組件里:

    寫法基本上不變

    store.getState().todos

    state數據不寫在組件內部訂閱,可以寫在主入口文件 訂閱store數據的更新

    let render = ()=>{

    ? ?ReactDOM.render(

    ? ? ?,

    ? ? ?document.getElementById('root')

    ? )

    };

    render();

    store.subscribe(render);

    react-redux

    基于redux,專門為react使用redux而生,react-redux是連接redux和react組件的橋梁

    react-redux做了哪些事情?

    首先,看看redux里的問題:

    1、組件中出現了大量的store對象

    2、 在redux里,凡是使用state里數據的組件,必須加上 store.subscribe() 函數,否則,數據不是響應式的

    react-redux的API

    (react-redux僅有2個API)

    組件:可以讓組件拿到state(不需要使用傳統(tǒng)的subscribe()來監(jiān)聽state重繪組件) ./src/index.js

    ?

    import {Provider} from "react-redux";

    import store from './redux/store'

    ?

    ReactDOM.render((

    ? ?

    ? ? ? ?

    ? ?

    ), document.getElementById('root'));

    connect(): 鏈接 ,(返回值)是個高階組件,用來鏈接react組件和redux(組件狀態(tài)要從redux中獲?。?/p>

    connect([mapStateToProps], [mapDispatchToProps], [mergeProps], [options])

    功能:把store和react組件聯(lián)系在一起。只要store發(fā)生了變化就會調用mapStateToProps方法。Connect方法(的返回值)就是個高階組件。

    參數1:mapStateToProps是個函數,

    功能: 把倉庫里的state合并到當前組件里的props上。給mapStateToProps函數傳入所有state,它返回指定的state數據(需要合并到組件props中的state)。返回的state與組件的 props 合并(聯(lián)系的體現)。另外,當store發(fā)生變化時,mapStateToProps方法就會更新組件里的props,那么組件就更新了(因為props變了)。

    參數:

    state:所有的state

    返回值:

    指定的state(組件里需要的state)。

    示例代碼:

    const mapStateToProps = (state)=>{

    ? ? ?return {

    ? ? ? ? ? count:state.count

    ? ? }

    }

    參數2:mapDispatchToProps函數

    功能:

    把dispatch和props聯(lián)系起來。傳入dispatch,返回綁定好的action方法。

    更改數據必須要觸發(fā)action,所以,mapDispatchToProps把 action 作為組件的props 進行綁定(聯(lián)系的體現),要派發(fā)的函數名,可以是多個函數。mapDispatchToProps 就是用于建立組件跟store.dispatch(是action)的映射關系。

    參數:

    dispatch: 派發(fā)

    ownProps:當前組件的props,即使用標簽時,傳入的props

    返回值:

    對象:表示所有dispatch的對象

    示例代碼:

    import React from "react";

    import './App.css';

    import {ADD,REDUCE} from "./store/action";

    import {connect} from "react-redux";

    ?

    class App extends React.Component {

    ?

    ?add(){

    ? ?this.props.add();

    }

    ?

    ?reduce(){

    ? ?this.props.reduce();

    }

    ?

    ?render = () => {

    ? ? ?return (

    ? ? ? ? ?

    ? ? ? ? ? ?

    {this.props.count}

    ? ? ? ? ? ?this.add()} />

    ? ? ? ? ? ?this.reduce()} /> ? ? ? ? ? ?

    ? ? ? ? ?

    ? ? ? )

    ? }

    }

    ?

    let mapStateToProps= state=>{

    ?return {

    ? ?count:state.count

    }

    }

    ?

    let mapDispatchToProps = dispatch=> ({

    ?add: () => dispatch(ADD()),

    ?reduce: () => dispatch(REDUCE()) ?

    })

    ?

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

    react-redux的思路:

    1)、用Provider包裹最頂層的組件,提供一個store屬性。這樣在任何組件里都可以使用store了。

    2)、使用connect()函數來鏈接react的組件和redux的store。記?。篶onnect不能單獨使用,必須要有Provider

    最佳實現(完整的代碼)

    安裝:npm install --save react-redux

    ?

    //1、主入口文件 index.js

    import {Provider} from 'react-redux'

    import store from './plugins/redux'

    ?

    ?

    ?

    //2、容器組件里:App組件

    import {connect} from "react-redux";

    class App extends React.Component {

    ?add(){

    ? ?//直接用props來調用dispatch,而不需要store

    ? ?this.props.dispatch({

    ? ? ?type:"INCREMENT",

    ? ? ?payload:2

    ? });

    }

    ? ?

    ?render = () => (

    ?

    ? ? ? ?

    {this.props.count}

    // 使用props可以直接拿到state里的數據,而不需要store

    ? ? ? ?this.add()} />

    ? ?

    ? ) ? ?

    }

    ?

    //容器組件對外開放時,(把redux里的state轉到props)

    export default connect((state)=>{

    ?return {

    ? ?count :state.count ? ?

    }

    })(App);

    在react-redux里,把組件進行拆分(容器組件和UI組件)

    容器組件:處理業(yè)務邏輯,有狀態(tài)(在redux里存放)組件,也叫智能組件

    UI組件:只做展示,就是無狀態(tài)組件,也叫木偶組件

    ui組件 ant-design Ant-Design-Mobile element-ui (react)等

    ant-design

    immutable

    Immutable.js 介紹

    引用類型的變量的優(yōu)點是節(jié)約內存,我們稱這樣的方式是Mutable(可變的)。但是當一個項目越來越復雜的時候,Mutable帶來的內存優(yōu)勢,消失殆盡。雖然我們可以進行deepCopy(深拷貝),但是這樣會造成CPU和內存的浪費。Immutable就是來解決這樣的問題的。

    Immutable( 不可改變的 ) Data 就是一旦創(chuàng)建,就不能再被更改的數據。對 Immutable 對象的任何修改或添加刪除操作都會返回一個新的 Immutable 對象。Immutable 實現的原理是 Persistent Data Structure(持久化數據結構),也就是使用舊數據創(chuàng)建新數據時,要保證舊數據同時可用且不變。同時為了避免 deepCopy 把所有節(jié)點都復制一遍帶來的性能損耗,Immutable 使用了Structural Sharing(結構共享),即如果對象樹中一個節(jié)點發(fā)生變化,只修改這個節(jié)點和受它影響的父節(jié)點,其它節(jié)點則進行共享 。

    (1)Immutable優(yōu)點:

    減少內存的使用(深拷貝消耗內存)

    并發(fā)安全

    降低項目的復雜度

    (2)Immutable缺點:

    庫的大?。ńㄗh使用seamless-immutable)

    對現有項目入侵嚴重

    容易與原生的對象進行混淆

    深拷貝與淺拷貝的關系

    面試題:深拷貝和淺拷貝(超級詳細,有內存圖)

    面試題:深拷貝和淺拷貝(超級詳細,有內存圖)_深copy和淺copy面試-CSDN博客

    Immutable 中常用類型(Map,List)

    Map()

    作用:復制一個對象(鍵值對)

    參數:json對象

    返回值:Map對象(經過immutable包裝了的Map對象) ,該Map對象也可以使用set,get方法。但是set方法調用后會產生一個新的Map對象

    Immutable.Map(json對象);

    示例:

    npm i --save immutable

    ?

    function f(){

    ? ?//定義一個對象obj1

    ? ?var obj1 = {

    ?? ??? ?id:"007",

    ?? ??? ?name:"張三瘋",

    ? ? ? ?address:{

    ? ? ? ? ? ?province:"陜西省",

    ? ? ? ? ? ?city:"西安市"

    ? ? ? }

    ?? ?};

    ? ?//淺復制:let obj2 = obj1; 兩個對象會共享同一塊內存區(qū)域

    ? ?//深拷貝:兩個對象的內存是獨立的,

    ? ?

    ? ?//使用Immutable.Map()進行復制,相同的數據會共享。

    ? ?let obj2 = Immutable.map(obj1).toJS();

    ? ?//修改數據時,只把改動數據及其父級數據部分進行復制,其它部分不做復制,依然共享,節(jié)約了內存。

    ? ?obj1.address.province="北京";

    ?? ?console.log(obj1);//obj1.address.province是北京

    ?? ?console.log(obj2);//obj2.address.province是陜西

    }

    Map對象的set函數會產生一個新的對象

    示例:

    ?

    function f(){

    ?var obj1 = {

    ?? ??? ?id:"007",

    ?? ??? ?name:"張三瘋"

    ?? ?};

    ?

    ?let obj2 = Immutable.Map(obj1); ?

    ? ?

    ?let obj3 = obj2.set("name","李思峰"); ?

    ?let obj4 = obj2.set("sex","男");

    ?

    ?console.log(obj2.toJS()); ? //{id: "007", name: "張三瘋"}

    ?console.log(obj3.toJS()); ? ?//{id: "007", name: "李思峰"}

    ?console.log(obj4.toJS()); ? ?//{id: "007", name: "張三瘋", sex: "男"}

    }

    List

    可重復的有序列表。對應原生JS的 Array

    作用:復制一個數組

    參數:數組

    返回值:List。

    是有序的索引密集集合,類似于JavaScript數組,針對List類型有set和get方法,分別表示設置和獲取

    function f(){

    ? ?

    ?const persons = ['芙蓉姐姐','春哥','犀利哥']

    ?let ipersons = Immutable.List(persons);

    ?

    ?let ipersons2 = ipersons.set( 1, '干露露');

    ?

    ?console.log("ipersons",ipersons);//List對象

    ?console.log("ipersons.toJS()",ipersons.toJS())

    ?

    ?console.log("ipersons2",ipersons2);//List對象

    ?console.log("ipersons2.toJS()",ipersons2.toJS())

    }

    Immutable 中常用方法(fromJS,toJS,is())

    // (1) Immutable.fromJs(),把一個js對象(數組)轉化為Immutable對象。

    ?

    ? ?let map = Immutable.fromJS({ a: 1,b: 1,c: 1 });

    ? ?console.log(map);

    ?

    ? ?let list = Immutable.fromJS(['芙蓉姐姐','春哥','犀利哥']);

    ? ? console.log(list);

    ?

    // (2) immutable對象.toJS(),把一個Immutable對象轉化為js對象(數組)。

    let o = ?map.toJS();

    ? ? console.log(o);

    let arr2 = list.toJS();

    ? ? ?console.log(arr2);

    ?

    // (3) Immutable.is():比較兩個Map(或List)的值是否相等

    ? var map1 = Immutable.Map({ a: 1,b: 1,c: 1 });

    ? var map2 = Immutable.Map({ a: 1,b: 1,c: 1 });

    ? console.log(Immutable.is(map1, map2));//true

    Immutable + Redux 的開發(fā)方式

    如果我們希望,redux中的state也是用immutable的方式。那就需要使用redux-immutable提供的combineReducers來代替。

    //創(chuàng)建倉庫

    ?

    import {combineReducers} from 'redux-immutable';

    import { createStore } from "redux";

    ?

    import reducer from "../store/reducer";

    ?

    ?

    let rootReducers = combineReducers({reducer});

    ?

    let store = createStore(rootReducers);

    ?

    export default store;

    reducers中的state也需要使用immutable

    //reducer

    ?

    // 初始化reducer中的initialState時建議用List方法而不是fromJS方法,效率更高

    import Immutable from "immutable";

    ?

    let initState=Immutable.List([

    ? {

    ? ? ? ?text:"吃飯",

    ? ? ? ?isComplete:false

    ? },

    ? {

    ? ? ? ?text:"睡覺",

    ? ? ? ?isComplete:true

    ? }

    ])

    ?

    export default (state=initState,action)=>{

    ? ?//let state1 = state.toJS();

    ? ?let {type,payload} = action;

    ? ?switch(type){

    ? ? ? ?case "ADDTODO":{

    ? ? ? ? ? ?let obj = {

    ? ? ? ? ? ? ? ?text:payload,

    ? ? ? ? ? ? ? ?isComplete:false

    ? ? ? ? ? }

    ? ? ? ? ? ?return state.set(state.size,obj]);

    ? ? ? }

    ? ? ? ?default:return state;

    ? }

    }

    組件里面,獲取到數據后,就需要使用toJS()進行轉換。或者使用immutable的函數進行操作。

    組件:

    class App extends React.Component {

    ?constructor(props){

    ? ?super(props);

    ? ?console.log("store.getState()",store.getState().toJS().reducer);

    ? ?this.state ={

    ? ? ? ?todos:store.getState().toJS().reducer,

    ? ? ? ?text:""

    ? }

    }

    ?

    ?componentDidMount(){

    ? ?store.subscribe(()=>{

    ? ? ?this.setState({

    ? ? ? ?todos:store.getState().toJS().reducer

    ? ? })

    ? });

    }

    …………………………

    mobx(視情況介紹一下就行)

    1.1. MobX 要點 · MobX 中文文檔

    介紹

    一款可以與redux媲美的數據流方案。Flux思想單向數據流方案,以 Redux 為代表;Reactive響應式數據流方案,以 Mobx 為代表;

    單向數據流實現:redux + react-redux + react-thunk 響應式數據流實現:mobx + mobx-react

    MobX 的理念是通過觀察者模式對數據做出追蹤處理,在對可觀察屬性作出變更或者引用的時候,觸發(fā)其依賴的監(jiān)聽函數,整體的store注入機制采用react提供的context來進行傳遞

    適用場景可以是react vue angular mpvue 小程序 taro

    裝飾器Decorator

    是個函數,用來裝飾類或者類成員 ,是Object.defineProperty的語法糖

    //給對象添加或修改屬性

    Object.defineProperty(target, prop, desc)

    //target 需要定義屬性的當前對象

    //prop 當前需要定義的屬性名 類型:字符

    desc默認值說明configurablefalse描述屬性是否可以被刪除,默認為 falseenumerablefalse描述屬性是否可以被for...in或Object.keys枚舉,默認為 falsewritablefalse描述屬性是否可以修改,默認為 falsegetundefined當訪問屬性時觸發(fā)該方法,默認為undefinedsetundefined當屬性被修改時觸發(fā)該方法,默認為undefinedvalueundefined屬性值,默認為undefined

    //定義裝飾器(以下代碼會被轉成Object.defineProperty的寫法)

    function 裝飾器名 (target,prop,descriptor){

    ?descriptor.writable=false;//writable屬性是否可以寫入

    }

    ?

    //使用裝飾器

    @裝飾器名 類

    @裝飾器名 實例屬性|靜態(tài)屬性(類的屬性)

    @裝飾器名 實例方法|靜態(tài)方法(類的方式)

    ?

    //使用場景

    mobx / angluarTs / vueTs / reactTs / java ...

    ?

    配置

    create-react-app腳手架 不支持裝飾器語法,需要小配一下

    yarn add @babel/plugin-proposal-decorators --save

    package.json

    babel: {

    ?"presets":...

    ?

    ?+

    ?"plugins": [

    ? ? ["@babel/plugin-proposal-decorators", { "legacy": true }],

    ? ]

    ?

    ?....

    }

    vscode編輯配置

    去掉vscode中使用裝飾器寫法時的,波浪線提示

    vscode->文件->首選項-->設置->搜索:experimentalDecorators->勾上 ?

    //webstrom 無需設置

    如果提示:

    Parsing error: Using the export keyword between a decorator and a class is not allowed. Please use `export @dec class` instead.

    ?

    請安裝:

    npm install --save-dev babel-plugin-transform-decorators-legacy

    ?

    模塊的版本:

    "@babel/core": "7.12.3",

    "@babel/plugin-proposal-decorators": "^7.12.12",

    "@pmmmwh/react-refresh-webpack-plugin": "0.4.2",

    "@svgr/webpack": "5.4.0",

    "@testing-library/jest-dom": "^5.11.4",

    "@testing-library/react": "^11.1.0",

    "@testing-library/user-event": "^12.1.10",

    "@typescript-eslint/eslint-plugin": "^4.5.0",

    "@typescript-eslint/parser": "^4.5.0",

    "antd": "^4.10.0",

    "axios": "^0.21.1",

    "babel-eslint": "^10.1.0",

    "babel-jest": "^26.6.0",

    "babel-loader": "8.1.0",

    "babel-plugin-named-asset-import": "^0.3.7",

    "babel-preset-react-app": "^10.0.0",

    "bfj": "^7.0.2",

    "camelcase": "^6.1.0",

    "case-sensitive-paths-webpack-plugin": "2.3.0",

    "css-loader": "4.3.0",

    "dotenv": "8.2.0",

    "dotenv-expand": "5.1.0",

    "echarts": "^5.0.0",

    "eslint": "^7.11.0",

    "eslint-config-react-app": "^6.0.0",

    "eslint-plugin-flowtype": "^5.2.0",

    "eslint-plugin-import": "^2.22.1",

    "eslint-plugin-jest": "^24.1.0",

    "eslint-plugin-jsx-a11y": "^6.3.1",

    "eslint-plugin-react": "^7.21.5",

    "eslint-plugin-react-hooks": "^4.2.0",

    "eslint-plugin-testing-library": "^3.9.2",

    "eslint-webpack-plugin": "^2.1.0",

    "file-loader": "6.1.1",

    "fs-extra": "^9.0.1",

    "html-webpack-plugin": "4.5.0",

    "http-proxy-middleware": "^1.0.6",

    "identity-obj-proxy": "3.0.0",

    "immutable": "^4.0.0-rc.12",

    "jest": "26.6.0",

    "jest-circus": "26.6.0",

    "jest-resolve": "26.6.0",

    "jest-watch-typeahead": "0.6.1",

    "mini-css-extract-plugin": "0.11.3",

    "mobx": "^6.0.4",

    "mobx-react": "^7.0.5",

    "optimize-css-assets-webpack-plugin": "5.0.4",

    "pnp-webpack-plugin": "1.6.4",

    "postcss-flexbugs-fixes": "4.2.1",

    "postcss-loader": "3.0.0",

    "postcss-normalize": "8.0.1",

    "postcss-preset-env": "6.7.0",

    "postcss-safe-parser": "5.0.2",

    "prompts": "2.4.0",

    "react": "^17.0.1",

    "react-app-polyfill": "^2.0.0",

    "react-dev-utils": "^11.0.1",

    "react-dom": "^17.0.1",

    "react-redux": "^7.2.2",

    "react-refresh": "^0.8.3",

    "react-router-dom": "^5.2.0",

    "redux": "^4.0.5",

    "redux-thunk": "^2.3.0",

    "resolve": "1.18.1",

    "resolve-url-loader": "^3.1.2",

    "sass-loader": "8.0.2",

    "semver": "7.3.2",

    "style-loader": "1.3.0",

    "terser-webpack-plugin": "4.2.3",

    "ts-pnp": "1.2.0",

    "typescript": "^4.1.3",

    "url-loader": "4.1.1",

    "web-vitals": "^0.2.4",

    "webpack": "4.44.2",

    "webpack-dev-server": "3.11.0",

    "webpack-manifest-plugin": "2.2.0",

    "workbox-webpack-plugin": "5.1.4"

    示例:

    定義一個只讀裝飾器(其實就是個函數),設定屬性是只讀的。

    function ?readonly(target,prop,desc) {

    ?desc.writable = false;

    }

    ?

    class Dog{

    ?//使用裝飾器,修飾dark屬性為只讀的

    ? @readonly dark="wangwang";

    }

    ?

    let d = ?new Dog();

    d.dark="mie"; //這個是不能修改成功的

    console.log(d.dark);

    mobx成員

    observable 裝飾類和其成員

    @observable 裝飾store類的成員,讓store中的成員成為被觀察者,即:在組件里可以訂閱該數據的變化。

    Mbox的思想是:把state和reducer都放在一個類中。state就是數據,reducer就是方法。

    示例:創(chuàng)建倉庫

    ./src/mobxstore/index.js

    ? ?

    import { observable } from 'mobx';

    ?

    //1)、state:

    //(在對象外面套上observable()函數,就可以,讓對象里的數據可以被訂閱)

    let appStore = observable({

    ? ?count:0

    });

    ?

    //2)、reducer:

    appStore.increment = function () {

    ? ?console.log("increment");

    ? ?appStore.count += 1;

    }

    ?

    appStore.decrement = function () {

    ? ?console.log("increment");

    ? ?appStore.count -= 1;

    }

    ?

    export default appStore;

    mobx-react成員

    Provider inject observer

    Provider,頂層提供appStore的服務

    ?

    inject:在組件里使用倉庫對象(appStore)。 Provider和inject結合起來,就可以讓頂層提供的倉庫對象(appStore)在組件里(props)使用

    @inject('appStore')

    class Index extend React.Component{

    ? ? render(){

    ? ? ? ?

    ? ? ? ? {this.props.appStore.屬性名}

    ? ? ? ?

    ? ? }

    }

    observer:能夠監(jiān)聽到數據的變化,并響應到組件里。即:在組件里訂閱了store中的數據,當store中的數據發(fā)生變化時,就會發(fā)布給組件。

    @observer

    class Index extend React.Component{

    ? ? render(){

    ? ? ? ?

    ? ? ? ? {this.props.appStore.屬性名}

    ? ? ? ?

    ? ? }

    }

    Mobx 的使用

    1、安裝:npm install mobx --save 或者 yarn add mobx 2、創(chuàng)建倉庫

    文件名: ./mobxstore/index.js

    ?

    import { observable } from 'mobx';

    //1)、定義狀態(tài)并使其可觀察

    //state:(在對象外面套上observable()函數,就可以,讓對象里的數據可以被訂閱)

    ?

    const appStore = observable({

    ? ?count: 0

    });

    ?

    appStore.increment = function () {

    ? ?console.log("increment");

    ? ?appStore.count += 1;

    }

    ?

    export default appStore;

    3、入口文件:

    在頂層提供 倉庫對象(appStore)

    //index.js

    ?

    import appStore from './mobxstore';

    import { Provider } from "mobx-react";

    ?

    ReactDOM.render(

    ?

    ? ?

    ? ? ?

    ? ?

    ?,

    ?document.getElementById('root')

    );

    4、使用數據的組件

    需要使用 observer,inject

    ?

    import { Component } from "react";

    import {observer,inject} from "mobx-react";

    ?

    @inject('appStore') ?// 使用appStore對象,對應著頂層組件的Provider提供的appStore,同時,把appStore映射到當前組件的props上了

    @observer ? //監(jiān)聽appStore中數據的變化,即:只要appStore中的數據發(fā)生變化,就會重新渲染該組件

    class Index extends Component { ? ?

    ? ?//這個不能用箭頭函數 ? ?

    ? ?render(){

    ? ? ? ?return(

    ? ? ? ? ? ? ? ?

    ? ? ? ? ? ? ? ? ? count:{this.props.appStore.count} ? ? ? ? ? ? ? ? ? ?

    ? ? ? ? ? ? ? ?

    ? ? ? ? ? )

    ? }

    }

    ?

    export default Index;

    ?

    5、修改數據的組件:

    需要使用 inject

    import {observer,inject} from "mobx-react";

    ?

    @inject('appStore') ?// 使用appStore對象,對應著頂層組件的Provider提供的appStore

    export default class Layout extends Component {

    ?

    ? ?incCount(){

    ? ? ? ?this.props.appStore.increment();

    ? }

    ? ?

    ? ?render = () => (

    ? ? ? ?

    ? ? ? ? ? ? ? ? ? ? ? ? ?

    ? ? ? ?

    ? )

    }

    React17

    React 17版本不同尋常,因為它沒有添加任何面向開發(fā)人員的新功能。取而代之的是,該發(fā)行版主要致力于簡化React本身的升級。

    生命周期

    react17 ,不建議使用componentWillMount,componentWillUpdate,這些生命周期鉤子函數,因為,這些鉤子函數函數有安全問題

    事件代理更改

    在React 16和更早的版本中,React將對大多數事件代理執(zhí)行document.addEventListener()。 React 17將在后調用rootNode.addEventListener()。

    ………………

    滑動驗證頁面

    react+typescript

    搭建項目(支持typescript):

    create-react-app 項目名稱 --typescript

    不同

    文件擴展名:js變成了ts,jsx變成了tsx

    代碼示例:

    1)、類組件里使用ts

    //類組件里使用ts

    ?

    import React from "react";

    ?

    interface IProps {

    ? ?name:string,

    ? ?age?:number

    }

    ?

    interface IState {

    ? ?color:string

    }

    ?

    export default class Home extends React.Component{

    ? ?constructor(props:IProps){

    ? ? ? ?super(props);

    ? ? ? ?this.state = {

    ? ? ? ? ? ?color:"red"

    ? ? ? }

    ? }

    ?

    ? ?changeColor=()=>{

    ? ? ? ?this.setState({

    ? ? ? ? ? ?color:"blue"

    ? ? ? });

    ? }

    ?

    ? ?render=()=>{

    ? ? ? ?return (

    ? ? ? ?

    ? ? ? ? ? ?

    姓名:{this.props.name}

    ? ? ? ? ? ?

    姓名:{this.props.age || 0}

    ? ? ? ? ? ?

    ? ? ? ?

    ? ? ? )

    ? }

    }

    2)、函數式組件

    //Counter組件代碼:

    ?

    import React from "react";

    ?

    interface IProps {

    ? ?count:number,

    ? ?increment:()=>void

    }

    const Counter=(props:IProps) =>{

    //const Counter=({count,increment}:IProps) =>{ ? ? ?

    ? ?return(

    ? ? ? ?<>

    ? ? ? ? ? ?

    {props.count}

    ? ? ? ? ? ?

    ? ? ? ?

    ? )

    }

    ?

    export default Counter;

    ?

    ?

    App組件代碼:

    ?

    import React from 'react';

    import logo from './logo.svg';

    import './App.css';

    import Home from './pages/Home/Home';

    import Counter from './pages/Counter/Counter';

    ?

    interface IProps {

    }

    ?

    interface IState {

    ?count:number

    }

    ?

    export default class App extends React.Component{

    ?constructor(props:IProps){

    ? ? ?super(props);

    ? ? ?this.state = {

    ? ? ? ? ?count:0

    ? ? }

    }

    ?

    ?increment=()=>{

    ? ?this.setState({

    ? ? ?count:this.state.count+1

    ? });

    }

    ?

    ?render=()=>{

    ? ?return (

    ? ? ?

    ? ? ? ? ?

    ? ? ? ? ?

    ? ? ?

    ? ? );

    ? }

    }

    高階組件:

    import React from 'react';

    ?

    class News extends React.Component{

    ?

    ?render=()=>{

    ? ?return (

    ? ? ? ? ? ? ?

    ? ? ? ? ? ? ? ? ?

    我是新聞,我拍誰

    ? ? ? ? ? ? ? ? ?

    我是新聞,我拍誰

    ? ? ? ? ? ? ? ? ?

    我是新聞,我拍誰

    ? ? ? ? ? ? ?

    ? ? ? ? ? );

    ? }

    }

    ?

    //高階組件

    const withCopyRight = (WrappedCom:React.ComponentType)=>{

    ? ?return class extends React.Component{

    ? ? ? ?render=()=>(

    ? ? ? ? ? ? ? ?<>

    ? ? ? ? ? ? ? ? ? ?

    ? ? ? ? ? ? ? ? ? ?

    ? ? ? ? ? ? ? ? ? ? ? @copyright:版權所有 2002A

    ? ? ? ? ? ? ? ? ? ?

    ? ? ? ? ? ? ? ?

    ? ? ? ? ? );

    ? }

    }

    ?

    export default withCopyRight(News);

    事件處理,傳遞事件對象(React.mouseEvent)

    定義 Event組件

    ?

    import React, { ReactNode } from "react";

    ?

    interface IProps{

    ? ?name:string,

    ? ?click:(e:React.MouseEvent)=>void

    }

    ?

    export default class Event extends React.Component{

    ?

    ? ?render=()=>{

    ? ? ?return (

    ? ? ? ?

    ? ? ? ? ? ?

    ? ? ? ?

    ? ? );

    ? ? }

    }

    ?

    //引用Event組件

    import React from 'react';

    import logo from './logo.svg';

    import './App.css';

    import Event from './pages/Event/Event';

    ?

    interface IProps {

    }

    ?

    ?

    export default class App extends React.Component{

    ?constructor(props:IProps){

    ? ? ?super(props);

    ? }

    ?

    ?

    ?handClick=(e:React.MouseEvent)=>{

    ? ?console.log(e);

    ? ?console.log(e.target);

    }

    ?

    ?render=()=>{

    ? ?return (

    ? ? ?

    ? ?

    ? ? ? ? ?

    ? ? ?

    ? );

    }

    }

    // export default App; ?

    umi

    官網 項目

    介紹:

    UmiJS - 插件化的企業(yè)級前端應用框架

    Umi,中文可發(fā)音為烏米,是可擴展的企業(yè)級前端應用框架。Umi 以路由為基礎的,同時支持配置式路由和約定式路由,保證路由的功能完備,并以此進行功能擴展。然后配以生命周期完善的插件體系,覆蓋從源碼到構建產物的每個生命周期,支持各種功能擴展和業(yè)務需求。

    Umi 是螞蟻金服的底層前端框架,已直接或間接地服務了 3000+ 應用,包括 java、node、H5 無線、離線(Hybrid)應用、純前端 assets 應用、CMS 應用等。他已經很好地服務了我們的內部用戶,同時希望他也能服務好外部用戶。

    安裝umi

    1)、安裝:

    yarn add global umi@2.12.9 (react 整理推薦用yarn ? 瑕疵node-sass)

    ?

    npm i -g umi@2.12.9

    2)、驗證

    umi -v

    順便補充:yarn和npm 全局安裝的文件在哪兒呢?

    yarn全局位置: yarn global bin

    npm全局位置:npm config list

    手工(命令)搭建項目

    創(chuàng)建項目目錄

    mkdir demoprj01

    創(chuàng)建頁面

    //命令的方式創(chuàng)建頁面。

    ?

    //創(chuàng)建組件index

    umi g page index

    //創(chuàng)建組件users

    umi g page users

    啟動項目

    umi dev

    用命令umi dev啟動項目 后,大家會發(fā)現 項目目錄 下多了個 .umi 的目錄。這是啥?這是 umi 的臨時目錄,可以在這里做一些驗證,但請不要直接在這里修改代碼,umi 重啟或者 pages 下的文件修改都會重新生成這個文件夾下的文件。

    這個文件夾里有自動生成的路由配置(.umi/core/routes.ts)

    約定式路由

    umi 里約定默認情況下 pages 下所有的 js 文件即路由 。

    https://github.com/umijs/umi/blob/umi%402.12.9/packages/umi/src/link.js

    1)、直接使用react-router-dom

    ?

    import {Link} from 'react-router-dom';

    ?

    go to /users

    ?

    2)、使用umi的link模塊

    ?

    ? ?在umi的腳手架里使用

    ?

    腳手架創(chuàng)建項目

    創(chuàng)建項目目錄:mkdir project

    轉換項目目錄:cd project

    使用yarn產生umi的項目:yarn create umi

    安裝所有的依賴:yarn install

    運行項目:yarn start

    項目結構

    |-public 本地數據資源

    |-mock umi支持mock數據,無需代理

    |-src

    ?|-assets 開發(fā)資源

    ?|-components 通用組件

    ?|-layouts 為根布局,根據不同的路由選擇return不同的布局,可以嵌套一些和布局相關的組件

    ?|-pages 頁面 約定式路由的頁面

    ?|-global.css 全局樣式

    |-umirc 編譯時配置

    路由

    約定式路由

    umi里使用的約定式(固定規(guī)則)路由。 規(guī)則如下:

    |-pages

    ?|-index.js ? "/" 路由 頁面

    ?|-index/index.js "/" 路由 頁面

    ?

    ?|-goods.js ?// /goods 路由

    ?|-goods/index.js ?// /goods 路由頁

    ? ?//goods 路由的默認頁 return null 代表沒有默認頁

    ?

    ?|-goods/$id.js ?// /goods/1 路由頁

    ?|-goods/$id$.js ?// /goods 路由。 goods下沒有index 時 展示了 /goods 或 /goods/2 代表id可選

    ?|-goods/_layout.js ?// /goods 路由頁 有自己的展示區(qū) {props.children} 不然會在父的展示區(qū)展示

    ?

    ?|-goods/category.js ? // /goods/category 路由

    ?|-goods/category/index.js // /goods/category 路由

    ?

    ?|-404.js 生產模式下的404頁,開發(fā)模式默認umi的

    注意:

    一定要關閉默認路由配置,否則,目錄自動生成路由(約定式路由)失效,

    如何關閉:修改.umirc.js文件:注釋routes鍵數據即可

    當在地址欄輸入: http://localhost:8000/ 時,

    會找layouts/index.js組件(布局)。在layouts/index.js里的{props.children}里會顯示pages/index.js

    路由跳轉

    聲明式

    商品 002

    編程式

    import router from 'umi/router';

    router.push('/login')

    ?

    router.push({

    ?pathname:'/goods/1',

    ?query:{a:11,b:22},

    ?search:'a=111&b=222'

    })

    // search:'a=111&b=222' 如果傳遞了search 會覆蓋query,query只是對search的封裝引用了search的值

    ?

    props.history.xx() //可用

    傳接參

    props.match.params

    props.location.query 返回對象

    案例需求

    基礎路由 - 練習:創(chuàng)建文章模塊列表、添加

    動態(tài)路由 - 練習:文章查看詳情

    嵌套路由 - 練習:面包屑(列表、添加、查看詳情都要)

    全局layout - 練習:后臺三欄布局(頭部、左側、右側(內容))

    404路由 - 練習:搞404(留心坑) 、不同的全局 layout

    實現步驟

    1、創(chuàng)建文章模塊列表、添加(基礎路由 )

    1)、列表頁面

    umi g page arts/index

    運行:yarn start

    2)、添加頁面

    umi g page arts/create

    運行:yarn start

    2、文章詳情(動態(tài)路由)

    umi g page 'arts/$id' --less

    在產生的$id.js文件里,寫如下代碼:

    Page {props.match.params.id}

    //獲取路由上的id的值

    運行:yarn start,

    在地址欄輸入 localhost:8000/arts/0001

    3、面包屑 (嵌套路由)

    后臺項目文章模塊,列表、添加詳情都需要面包屑 ,我們做一個公共的父組件 _layout

    普及:公共的父組件不管是在后端、還是前端專業(yè)術語叫l(wèi)ayout

    語法命令:umi g page arts/_layout --less

    在arts下創(chuàng)建的文件 _layout.js,如果訪問,http://localhost:8000/arts 開頭的路徑,都會默認找 arts文件夾下的 _layout 組件。所以,在該組件里配置子路由,子路由對應的頁面顯示在{props.children}里。

    在產生的_layout.js文件里,修改代碼:

    export default function(props) {

    return (

    ?

    ? ?

    Page _layout

    ? ?

    {props.children}

    ?

    );

    }

    在地址欄輸入: http://localhost:8000/arts/

    那么默認,會找arts文件夾下_layout.js文件,在

    {props.children}
    顯示index.js文件。

    如果在地址輸入: http://localhost:8000/arts/01001

    會找arts下_layout.js文件,在

    {props.children}
    顯示$id.js文件。

    4、后臺管理系統(tǒng)常見的三欄布局(頭部,下(左邊導航,右邊頁面))

    1)、總體布局

    //src/index.js

    import styles from './index.less';

    ?

    function BasicLayout(props) {

    ?return (

    ? ?

    ? ? ?

    頭部

    ? ? ?

    ? ? ? ?

    導航

    ? ? ? ?

    ? ? ? ? {props.children}

    ? ? ? ?

    ? ? ?

    ? ? ?

    ? ?

    );

    }

    ?

    export default BasicLayout;

    src/layout/index.less

    ?

    .normal {

    ?width: 100%;

    ?height: 100%;

    ?display: flex;

    ?flex-direction: column;

    ?

    ?.header{

    ? ?width: 100%;

    ? ?height: 120px;

    ? ?background-color: skyblue;

    }

    ?

    ?.main{

    ? ?flex: 1;

    ? ?width: 100%;

    ? ?background-color: red;

    ? ?display: flex;

    ?

    ? ?.left{

    ? ? ?width: 200px;

    ? ? ?height: 100%;

    ? ? ?background-color: blue;

    ? }

    ? ?

    ? ?.right{

    ? ? ?flex: 1;

    ? ? ?height: 100%;

    ? ? ?background-color: #ccc;

    ? }

    }

    }

    5、404 頁面

    約定 pages/404.js 為 404 頁面,需返回 React 組件。

    創(chuàng)建404頁面

    umi g page 404 --less // 這個不行,404不能產生,因為,在命令行里要求文件名不能以數字開頭。

    比如:

    export default () => {

    ?return (

    ? ?

    I am a customized 404 page

    );

    };

    注意:開發(fā)模式下,umi 會添加一個默認的 404 頁面來輔助開發(fā),但你仍然可通過精確地訪問 /404 來驗證 404 頁面。上線后就不會這樣了。

    另外,需要增加判斷,才能讓整個頁面都顯示404頁面。

    修改 src/layout/index.js文件的內容

    function BasicLayout(props) {

    ?if(props.location.pathname==="/404"){

    ? ?return props.children

    }

    ?return (

    ? ?

    ? ? ?

    頭部

    ? ? ?

    ? ? ? ?

    導航

    ? ? ? ?

    ? ? ? ? {props.children}

    ? ? ? ?

    ? ? ?

    ? ? ?

    ? ?

    );

    }

    dva

    官網:

    介紹 | DvaJS

    擴展

    契合antd的富文本編輯器braft-editor

    vue和react的區(qū)別

    reactJsvueJs控制器--過濾器-√指令-√模板語法-√服務--組件√√jsx√2.0之后加入響應式原理必須調用setState函數真正的響應式

    柚子快報邀請碼778899分享:前端 React 學習筆記

    http://yzkb.51969.com/

    參考鏈接

    評論可見,查看隱藏內容

    本文內容根據網絡資料整理,出于傳遞更多信息之目的,不代表金鑰匙跨境贊同其觀點和立場。

    轉載請注明,如有侵權,聯(lián)系刪除。

    本文鏈接:http://m.gantiao.com.cn/post/19233460.html

    發(fā)布評論

    您暫未設置收款碼

    請在主題配置——文章設置里上傳

    掃描二維碼手機訪問

    文章目錄