react學習一篇就夠了

webstrom自動格式化代碼

命令css

js框架 MVC
安裝
npm install create-react-app -g
生成項目(項目名npm發包包命名規範 /^[a-z0-9_-]$/)
create-react-app 項目名字
查看全局安裝目錄
npm root -g

文件html

public 存放的是當前項目的HTML頁面(單頁面應用放index.html便可)
html 導入的地址應該寫成絕對路徑  %PUBLIC_URL%   public的文件夾
    不能用相對路徑

src  項目結構最主要的目錄,後期的js,路由組件都放這裏
     index.js 是當前目錄的入口文件
react-scripts 是webpack的全部配置
  "scripts": {
    "start": "react-scripts start",
    "build": "react-scripts build",
    "test": "react-scripts test",
    "eject": "react-scripts eject"
  }
  執行命令 npm run start /yarn start

React腳手架的深刻剖析vue

暴露webpack配置項
爲告終構的美化,把全部的webpack配置等都隱藏到了node_modules中(react-script)
yarn eject 
首先會提示確認是否執行eject操做,操做是不可逆轉的,一旦暴露出來配置項,就沒法再隱藏回去了
報錯信息
Remove untracked files, stash or commit any changes, and try again.

若是當前的項目基於git管理,在執行eject的時候,若是尚未提交到歷史區內容,須要先提交歷史區,而後再eject才能夠,不然報錯
* 須要在vcs 裏面commit 進行git操做
再進行 yarn eject

以後多個兩個文件夾
config  存放的是webpack的配置文件
    webpack.config.dev.js 開發環境下的配置項(yarn start)
    webpack.config.prod.js 生產環境下的配置項(yarn build)
scripts   存放的是可執行腳本的js文件
    start.js  yarn start執行的就是這個js
    build.js  yarn  build 執行的就是這個就是
    
配置less
yarn add less less-loader
less 開發和生產都須要改
參考
https://www.cnblogs.com/esofar/p/9631657.html
https://www.cnblogs.com/jayxiangnan/p/9116663.html

set HTTPS=true&&npm start  開啓HTTPS協議模式
set PORT=63341  修改端口號

配置sass

yarn add node-sass sass-loader -D
若是yarn不能安裝就用cnpm
less相似同樣的
    {
                  test:/\.scss$/,
                  loaders:['style-loader','css-loader','sass-loader'],
              },

npm install less less-loader --save-dev

去掉webstorm 報灰色線node

 Editor -> Colors & Fonts -> General , 在Errors and Warnings裏把Wadk Warning裏的 兩項勾選去掉。react

撤銷工做區的修改webpack

已修改,未暫存(撤銷工做區的修改)ios

git reset --hard

react & react-domgit

漸進式框架
咱們應該把框架的功能進行拆分,用戶想用什麼,讓其本身自由組合便可
全家桶
漸進式框架N多部分的組合
VUE全局桶(vue-cli/vue/vue-router/vuex/axios(fetch)/vue element(vant))
REACT全家桶: create-react-app/react/react-dom/react-router/redux/react-redux/axios/ant/sage/mobx
react:REACT框架的核心部分,提供了Component類能夠共咱們進行組件開發,提供了鉤子函數(生命週期函數)
全部的生命週期函數都是基於回調函數完成的
react-dom :把JSX語法(REACT特有的語法)渲染爲真實DOM(可以展現的結構都叫真實的DOM)

* 不推薦jsx的容器是body
* 只能出現一個根元素
* 給元素設置樣式類用的是className而不是class
* style={{}}
* jsx元素設置屬性 ,屬性值對應大括號中 對象,函數均可以放(也能夠放js表達式)
將數據嵌入jsx中,能夠嵌入變量或者直接的數據值
let name='xxx';
ReactDOM.render(<div>
                <h1>{name}</h1>
                <h2>{'haha'}</h2>
                </div>)
不能嵌入對象(代指: {} /^$/ 日期對象 函數  數據中的某一項是前面也不行)    
能夠嵌入基本類型值(null/undefined/布爾值都是空元素,也就是不顯示任何內容)

把JSX(虛擬DOM) 變爲真實的DOM(不太理解)--(8,9)

循環建立jsx元素須要設置標識key,而且在當前循環的時候,這個key須要惟一
let name='珠峯培訓',
    data=[{id:1,title:'xxx'},{id:2, title: 'xxx'}];
ReactDOM.render(<ul style={{color:'red'}} className={'box clearfix'} OnClick={(ev)=>{
    console.log(ev);
}}>
    {data.map((item,index)=>{
        return <li key={index}>
            {item.id}
            {item.title}
        </li>
    })}
</ul>,root);
使用三元運算符解決判斷操做(if和swich是不能夠的)

React是如何把jsx元素轉換爲真實的DOM元素而且添加到頁面中web

基於babel/babel-loader: 把jsx語法編譯爲react.create-Element這種模式vue-router

create-Element 至少有兩個參數,沒有上限

第一個: 標籤名(字符串)

第二個:屬性(沒有給元素設置null)

其餘:當前元素的全部子元素內容

執行create-Element 把傳遞的參數最後處理成爲一個對象

React 組件

組件/模塊管理 ,就是把一個程序劃分爲一個個組件來單獨處理

優點

  • 有助於多人協助開發
  • 提升複用性

React建立組件有兩種方式

  • 函數聲明式組件
  • 基於繼承component類來建立組件

src->component 這個文件夾存放開發的組件

create-element 在處理的時候,遇到一個組件,返回的對象中type就再也不是字符串,而是一個函數(類),可是屬性仍是props中

({
    type:Dialog,
    props:{
        lx:1,
        con:'xxx',
        children:一個值或者一個數組
    }
}
首先判斷type是什麼類型,若是是字符串就建立一個元素標籤,若是函數或者類,就把函數執行,把props中的每一項(包含children傳遞給函數)
React.Children.map

基於繼承component類建立組件

基於create-element 把jax 轉換爲一個對象,當react渲染這個對象的時候,遇到type是一個函數或者類,不是直接建立元素,而是先把方法執行:
    * 若是是函數式聲明的組件,就把它看成普通方法執行(方法中的this是undefiend),把函數返回的jsx元素進行渲染
    * 若是是類聲明式的組件,會把房錢類new它執行,建立類的一個實例(當前本次調用組件就是它的實例)執行constructor以後,會執行this.render(),把render中返回的jsx拿過來渲染,因此 類聲明式組件,必須有一個render的方法,方法中須要一個jsx元素
    可是無論是哪種方式,最後都會拿解析出來的props屬性對象做爲實參給對應的函數或者類

建立組件有兩種方式"函數式","建立類式"

函數式

  • 簡單
  • 能實現的功能也很簡單,只是簡單的調取和返回jsx而已
ReactDOM.render(<div>
                    /*單閉合*/
                <Vote  title={xxx}/>
                    /*雙閉合*/
                <Vote>
                <p>11111111</p>
                </Vote>
                </div>)

import React from 'react';
exprot default function Vote(props){
  return <div className={'penel panel-default'}>
   {props.Children}
    {props.Children.map(props,children,item=>{return item;})}
  </div>
}

建立類式

  • 操做相對複雜一些,可是也能夠實現更爲複雜的業務功能

  • 可以使用生命週期函數操做業務

  • 函數式能夠理解爲靜態組件(組件中的內容調取的時候就已經固定了,很難在修改,而這種類的組件能夠基於組件內部的狀態來動態更新渲染的內容

  • import React from 'react';
    
    //ref是react操做DOM的方案
    //* 給須要操做的元素設置ref(保持惟一性,不然會衝突覆蓋)
    //* 在實例上掛載了refs屬性,他是一個對象,存儲了全部設置ref的元素(ref值:元素對象)
    export default class Vote extends React.Component{
        constructor(props){
            super(props); //React.Component.call(this)能夠把component中的私有實例繼承過來,this.props/this.state(this.setState)/this.content/this.refs/this.updater
            //初始化狀態
            this.state={
                n:0,
                m:0,
            }
        }
        //狀態處理方式
        render(){
            let {title,children}=this.props,
                {n,m}=this.state;
            return <div>
                支持:<span>{m}</span>
                反對: <span>{n}</span>
                </div>
        }
        //DOM處理方式
            render(){
            let {title,children}=this.props,
                {n,m}=this.state;
            return <div>
                支持:<span ref={'AA'}>0</span>
                反對: <span ref={'BB'}>0</span>
                平局值: <span ref={'CC'}>0</span>
                </div>
        }
        suport=ev=>{
            this.refs.AA.innerHTML++;
            this.computed();
        }
           suport=ev=>{
            this.refs.BB.innerHTML++;
            this.computed();
        }
           computed=()=>{
               let {AA,BB}=this.refs;
               //而後再進行操做
           }
        supprot=ev=>{
            this.refs.AA.innerHTML++;
            //使用箭頭函數式爲了保證方法中this永遠是實例自己(不管在哪執行這個方法)
            //ev.target獲取當前操做的事件源(dom元素)
            this.setState({
                //修改狀態信息而且通知render從新渲染(異步操做:若是有其餘代碼執行,先執行其餘代碼,而後再通知狀態修改)
                n:this.state.n+1
            },()=>{
                //回調函數通常不用,當通知狀態修改完成,而且頁面從新渲染完成後,執行回調
            })
        }
    }
yarn add prop-types
基於這個插件咱們能夠給組件傳遞的屬性設置規則
設置的規則不會影響頁面的,可是會控制檯報錯

  1. 顯示當前文件的最新版本信

  2. 返回上一個版本

生命週期函數(鉤子函數)

  • 描述一個組件或者程序從建立到銷燬的過程,咱們能夠再過程當中基於鉤子函數完成一些本身的操做

  • 生命週期

  • 基本流程
    constructor 建立一個組件
    componentWillMout  第一次渲染以前
    render 第一次渲染
    componentDidMout 第一次渲染以後
    
    修改流程:當組件的狀態數據發生改變(setState)或者傳遞給組件的屬性發生改變
    shouldComponentUpdate 是否容許組件從新渲染(容許則執行後面函數,不容許直接結束便可)
    componentWillUpdate 從新薰染以前
    render 第二次之後從新渲染
    componentDidUpdate 從新渲染以後
    
    屬性改變:
    componentWillReceiveProps(nextProps/nextState):父組件把傳遞給組組建的屬性發生改變後出發的鉤子函數
    接受最新屬性以前,基於this.props.xxx 獲取的是原有的屬性信息,nextProps存儲的是最新傳遞的屬性信息
    shouldComponentUpdate
      是否容許組件更新, 返回true是容許,返回false 是不在繼續走
      componentWillUpdate:
      更新以前: 和should同樣,方法中經過this.state.xxx 獲取的仍是更新前的狀態信息,方法有兩個參數:nextProps/nextState存儲的是最新的屬性和狀態
      render 更新
      componentDidUpdate  更新以後
    
    卸載
    componentWillUnmount :卸載組件以前(通常不用)

React是face-Book 公司開發的一款MVC版js 框架

MVC Model (數據層) View(視圖層) controller(控制層)

核心思想: 經過數據的改變來影響視圖的渲染(數據)

屬性的屬性是隻讀的:只能調用組件時候傳遞進來,不能本身在組建內部修改(可是能夠設置默認值和規則)

複合組件

  1. 複合組件:父組件嵌套子組件

傳遞信息的方式

  • 父組件須要把信息傳遞給子組件

    屬性傳遞:調取子組件的時候,把信息基於屬性的方式傳遞給子組件(子組件props中存儲傳遞的信息),這種方式只能父組件把信息傳遞給子組件,子組件沒法直接的把信息傳遞給父組件,也就是屬性傳遞信息是單向傳遞的;

    export default class Vote extends React.Component {
        static defaultProps = {
            title: '標題不知道,隨便投',
        };
    
        constructor(props) {
            super(props);
        }
        render() {
            let {title} = this.props;
            return   <VoteHead title={title}/>

    雙下文傳遞:父組件先把須要給後代元素(包括孫子元素)使用的信息都設置好(設置在上下文中),後代組件須要用到父組件中的信息,主動去父組件中調用使用便可

    /*  在父組件中
             設置子組件上下文屬性類型
             static childContextTypes={}
             獲取子組件的上下文(設置子組件的上下文屬性信息)
             getChildContext(){}
             */
        static childContextTypes = {
            //設置上下文信息值的類型
            n: PropTypes.number,
            m: PropTypes.number,
        };
    
        getChildContext() {
            //return 是啥就是想當於給子組件設置下上文
            let {count: {n = 0, m = 0}} = this.props;
            return {
                n,
                m,
            }
        }
      /*
        * 子組件設置使用傳遞進來的上下文類型
        * 設置那個的類型,子組件上下文中才有那個屬性,不設置是不容許使用的
        * this.context.xxx
        * 指定的上下文屬性類型須要和父組件指定的類型保持一致,不然報錯
        * */
        static contextTypes={
            n:PropTypes.number,
            m:PropTypes.number
        };
        constructor(props,context){
            super(props,context);
            this.context.n=1000
        }

    屬性VS 上下文

    1. 屬性操做起來簡單,子組件是被動接受傳遞的值(組件內的屬性是隻讀的),只能父傳子(子傳父不行,父傳孫也須要處理:子傳子,子再傳孫)

    2. 上下文操做起來相對複雜一些,子組件是主動獲取信息使用的(子組件是能夠修改獲取的上下文信息,可是不會影響到父組件中的信息,其餘組件不受影響),一旦父組件設置了上下文信息,他後代組件均可以直接拿到,不須要不層層的傳遞

    其實子組件也能修改父組件中的信息

    1. 利用回調函數機制: 父組件把一個函數經過屬性或者上下文的方式傳遞給子組件,子組件要把這個方法執行便可

      (也就是子組件中執行了父組件方法,還能夠傳遞一些值過去),這樣組件在這個方法中,想把本身的信息改爲啥就改爲啥

    /*  在父組件中
             設置子組件上下文屬性類型
             static childContextTypes={}
             獲取子組件的上下文(設置子組件的上下文屬性信息)
             getChildContext(){}
             */
        static childContextTypes = {
            n: PropTypes.number,
            m: PropTypes.number,
            callBack: PropTypes.func
        };
        getChildContext() {
            //return 是啥就是想當於給子組件設置下上文
            //只要render從新渲染,就會執行這個方法,從新更新父組件中的上下文信息,若是父組件上下文
            //信息更改了,子組件在從新調取的時候,會使用最新的上下文信息(render=>context=>子組件調取渲染)
            let {n, m} = this.state;
            return {
                n,
                m,
                callBack: this.updateContext
            }
        }
       updateContext = type => {
            //type :'support'/'against'
            if (type === 'support') {
                this.setState({n: this.state.n + 1});
                return;
            }
            return this.setState({m:this.state.m-1})
        };
    
    //子組件
     <button className={'btn btn-danger'} onClick={
                    ()=>{
                        callBack('against');
                    }
                }>反對</button>
  1. 平行組件:兄弟組件或者毫無關係的兩個組件
  • 讓兩個平行組件擁有一個共同的父組件
  • 基於redux來進行狀態管理,實現組件之間的信息傳遞
  1. redux能夠應用在任何項目中(vue/jq/react的均可以),react-redux纔是專門給react項目提供的方案
yarn add redux react-redux
//index.js

//建立一個容器: 須要把reducer 傳遞進來(登記了全部狀態更改的信息)
import {createStore} from 'redux';
/*reducer 做用:
    1.  記錄了全部狀態修改的信息(根據行爲標識走不一樣的修改任務)
    2.  修改容器中的狀態信息
    [參數]
        state:容器中原有的狀態信息(若是第一次使用,沒有原有狀態,給一個廚師默認值
        action: dispatch 任務派發的時候傳遞的行爲對象(這個對象中必有一個type屬性,是操做的行爲標識,
        reducer 就是根據這個行爲標識來識別修改狀態信息
* */
let reducer = (state = {n: 0, m: 0}, action) => {
    switch (action.type) {
        case 'VOTE_SUPPORT':
            //vote_support
            state = {...state, n: state.n + 1};
            break;
        case 'VOTE_AGAINST':
            //vote_against
            state = {...state, m: state.m + 1};
            break;
    }
    return state;// 只有把最新的state返回,原有的狀態纔會修改
};
let store = createStore(reducer);
/*
* 建立store 提供三個方法:
*   dispatch: 派發行爲(傳遞一個對象,對象中有一個type屬性,通知reducer修改狀態信息
*   subscribe: 事件池追加方法
*   getState: 獲取最新管理的狀態信息
* */
<Vote title={'英格蘭對戰巴拿馬,協力必勝'}
          count={{
              n: 100,
              m: 78
          }}
          store={store}/>
//Vote.js
import React from 'react';
import PropTypes from 'prop-types';
import VoteHead from './VoteHead';
import VoteBody from './VoteBody';
import VoteFooter from "./VoteFooter";


export default class Vote extends React.Component {
    static defaultProps = {
        title: '',
        count: {
            n: 0,
            m: 0,
        }
    };

    constructor(props) {
        super(props);
    };

    render() {
        let {store} = this.props;
        return <section className={'panel-heading'} style={{width: '50%', height: '20px auto'}}>
            <VoteHead title={this.props.title}/>
            <VoteBody store={store}/>
            <VoteFooter store={store}/>
        </section>
    }
}
//VoteBody.js

import React from 'react';
import PropTypes from 'prop-types';


export default class VoteBody extends React.Component {
    constructor(props) {
        super(props);
        //init state
        let {store: {getState}} = this.props,
            {n, m} = getState();
        this.state = {n, m};
    }

    componentDidMount() {
        let {store: {getState, subscribe}} = this.props;
        let unsubscribe = subscribe(() => {
            let {n, m} = getState();
            this.setState({
                n,
                m
            })
        });
        //unsubscribe(): 當前追加的方法移出,接觸綁定的方式
    }

    render() {
        let {n, m} = this.state,
            rate = (n / (n + m)) * 100;
        if (isNaN(rate)) {
            rate = 0;
        }


        return <div className={'panel-body'}>
            支持人數: <span>{n}</span>
            <br/>
            反對人數: <span>{m}</span>
            <br/>
            支持比率: <span>{rate.toFixed(2) + '%'}</span>
        </div>;
    }
}
//VoteFooter.js
import React from 'react';
import PropTypes from 'prop-types';

export default class VoteFooter extends React.Component {

    constructor(props, context) {
        super(props, context);
    }

    render() {
        let {store: {dispatch}} = this.props;
        return <div className={'panel-footer'}>
            <button className={'btn btn-success'}
                    onClick={() => {
                        dispatch({
                            type: 'VOTE_SUPPORT'
                        })
                    }}
            >支持
            </button>
            &nbsp;&nbsp;
            <button className={'btn btn-danger'} onClick={
                () => {
                    dispatch({
                        type: 'VOTE_AGAINST'
                    })
                }
            }>反對
            </button>
        </div>
    }
}

Redux

redux:進行狀態統一管理的類庫(適用於任何技術體系的項目)

  • 只要兩個或者多個組件之間想要實現信息的共享,均可以基於redux解決,把共享的信息到redux容器中進行管理
  • 還可使用redux作臨時存儲:頁面加載的時候,把從服務器獲取的數據信息存儲到redux中,組件渲染須要的數據,redux中,這樣只要頁面不刷新,路由切換的時候,再次渲染組件不須要從新從服務器拉取數據,直接從redux中獲取便可:頁面刷新,從頭開始(這套方案代替了localStorage本地存儲來實現數據緩存)

Redex管理文件夾

store REDUX管理文件夾
 *   action 派發行爲集合
 *          vote.js
 *          ...
 *          index.js 全部分模塊行爲的彙總
 *        
 *   reducer 管理員集合
 *          vote.js
 *          ...
 *          index.js 全部管理員的集合彙總
 *
 *   action-types.js 全部行爲標識
 *   index.js 建立REDUX容器

Redex工程化案例

store/index.js

/*
* store
*   reducer: 存放每個模塊 reducer
*       vote.js
*       personal.js
*       ...
*       index.js 把每個模塊reducer最後合併成reducer
*   action  存放每個模塊須要進行的派發任務(ActionCreator)
*
* */
import {createStore} from 'redux';
import reducer from './reducer';

let store = createStore(reducer);

export default store;

store/action-types.js

/*
* 管控當前項目中全部redux任務派發中須要的行爲標識action-type
* */
//vote_support
export const vote_support = 'vote_support';
export const vote_against = 'vote_against';
//personal
export const personal_init = 'personal_init';

store/action/

index.js
======
/*
* 合併全部的action-creator,相似於reducer合併,爲了防止衝突,合併後的對象是以版塊名稱單獨劃分管理
* */
import vote from './vote';
import personal from './personal';

let action ={
    vote,
    personal
};
export default action;

personal.js
======
import * as TYPE from '../action-types';

let personal = {};
export default personal;

vote.js
======
/*
* 每一個版塊單獨的action-creator :就是把dispatch派發時候須要傳遞的action對象進一步統一封裝處理
* react-redux中會體驗到他的好處
* */
import * as TYPE from '../action-types';
let vote={
    support(){
        //dispatch 派發的時候須要傳遞啥就返回啥便可
        return {
            type: TYPE.vote_support
        };
    },
    against(){
        return {
            type: TYPE.vote_against
        }
    }
};
export default vote;

store/reducer

index.js
=====
/*
* 把每個模塊可是設置的reducer函數最後合併成爲總的reducer
* 爲了保證合併reducer過程當中,每一個模塊管理的狀態信息不會相互衝突
* redux在合併的時候容器中的狀態進行分開管理(一合併reducer時候設置的屬性名做爲狀態
* 劃分的屬性名,把各個版塊管理的狀態放到本身的屬性下
* state={
*   vote:{
        n:0,
        m:0
*       },
*   personal:{
*       baseInfo:{}
*       }
*   }
*   store.get-state().vote.n之後獲取狀態信息的時候,也須要把vote加上
* */
import {combineReducers} from 'redux';
import vote from './vote';
import personal from './personal';

let reducer=combineReducers({
    vote,
    personal
});
export default reducer;    

personal.js
======
import * as TYPE from '../action-types';
export default function vote(state = {
    baseInfo:{}
}, action) {
   //...
    return state;
}   

vote.js
=====
//vote版塊的reducer
//      state: 原始redux管理的狀態管理(設置初始值)
//      action: dispatch派發的時候傳遞的行爲對象(type,)

import * as TYPE from '../action-types';
//把模塊中全部導出的內容所有導出,並從新命名爲type
export default function vote(state = {
    n: 0,
    m: 0,
}, action) {
    switch (action.type) {
        case TYPE.vote_support:
            state = {...state, n: state.n + 1};
            break;
        case TYPE.vote_against:
            state = {...state, m: state.m + 1};
            break;
    }
    return state;
}

src/index.js

import React from 'react';
import ReactDOM from 'react-dom';
import 'bootstrap/dist/css/bootstrap.css'

import Vote from './component/Vote/Vote';

import store from './store';


ReactDOM.render(<main>
    <Vote title={'英格蘭對戰巴拿馬,協力必勝'}
          count={{
              n: 100,
              m: 78
          }}
          store={store}/>
</main>, document.querySelector('#root'));

component/Vote

Vote.js
=====
    render() {
        let {store} = this.props;
        return <section className={'panel-heading'} style={{width: '50%', height: '20px auto'}}>
            <VoteHead title={this.props.title}/>
            <VoteBody store={store}/>
            <VoteFooter store={store}/>
        </section>
    }
    
VoteBody.js
=====
import React from 'react';
import PropTypes from 'prop-types';


export default class VoteBody extends React.Component {
    constructor(props) {
        super(props);
        //init state
        let {n, m} = this.props.store.getState().vote;
        this.state = {n, m};
    }

    componentDidMount() {
        this.props.store.subscribe(() => {
            let {n, m} = this.props.store.getState().vote;
            this.setState({
                n,
                m
            })
        });
        //unsubscribe(): 當前追加的方法移出,接觸綁定的方式
    }

    render() {
        let {n, m} = this.state,
            rate = (n / (n + m)) * 100;
        if (isNaN(rate)) {
            rate = 0;
        }


        return <div className={'panel-body'}>
            支持人數: <span>{n}</span>
            <br/>
            反對人數: <span>{m}</span>
            <br/>
            支持比率: <span>{rate.toFixed(2) + '%'}</span>
        </div>;
    }
}    

VoteFooter.js
=====
    import React from 'react';
import PropTypes from 'prop-types';
import action from '../../store/action';

export default class VoteFooter extends React.Component {

    constructor(props, context) {
        super(props, context);
    }

    render() {
        let {store: {dispatch}} = this.props;
        return <div className={'panel-footer'}>
            <button className={'btn btn-success'}
                    onClick={() => {
                       this.props.store.dispatch(action.vote.support())
                    }}
            >支持
            </button>
            &nbsp;&nbsp;
            <button className={'btn btn-danger'} onClick={
                () => {
                    this.props.store.dispatch(action.vote.against())
                }
            }>反對
            </button>
        </div>
    }
}

react-redux

react-redux 是把redux進一步封裝,適配react項目,讓redux操做更簡潔,store文件夾中內容和redux如出一轍

在組件調取使用的時候能夠優化一些步驟

Provider 根組件

  • 當前整個項目都在Provider組件下,做用就是把建立的store能夠供內部組件使用(基於上下文)
  • Provider 組件中只容許出現一個組件
  • 把建立的store基於屬性傳遞給Provider(這樣後代組件中均可以使用這個store)

connect 高階組件

  • 相對傳統的redux,咱們作的步驟優化

  • 導出的不在是咱們建立的組件,而是基於connect構造後的高階組件

```js
 export default connect([mapStateToProps],[mapDispatchToProps])(本身建立的組件)
```

之前咱們須要本身基於subscribe向事件池追加方法,以達到容器狀態信息改變,執行咱們追加的方法,從新渲染組件的目的,可是如今不用了,react-redux幫咱們作了這件事:'全部用到redux容器狀態信息的組件,都會向事件池中追加一個方法,當狀態信息改變,通知方法執行,把最新的狀態信息做用屬性傳遞給組件,組件的屬性值改變值改變了,組件也會從新渲染

////把redux容器中的狀態信息遍歷,賦值給當前組件的屬性(state)
    let mapStateToProps=state=>{
        //state就是redux容器中狀態信息
        //咱們返回的是啥,就把它掛載到當前組件的屬性上(redux存儲不少信息,咱們想用啥就返回啥便可)
        return {
            ...state.vote
        }

    };
    //把redux的dispatch 派發行爲遍歷,也複製給組件的屬性(ActionCreator)
    let mapDispatchToProps=dispatch=>{
        //dispatch:store中存儲的dispatch方法
        //返回的是啥,就想當於把啥掛載到組件的屬性上(通常咱們掛載一些方法,這
        // 些方法完成dispatch派發信息
        return {
            init(initData) {
                dispatch(action.vote.init(initData));
            }
        }
    };
    export default connect([mapStateToProps],[mapDispatchToProps])(VoteBase)
    =======================
    export default connect({state=>({...state.vote})},action.vote)(VoteBase)
    react-redux把action-creator中編寫方法(返回action對象的方法),自動構造dispatch派發任務的方法,也就是mapDispatchToProps這種格式把redux容器中的狀態信息遍歷,賦值給當前組件的屬性(state)

todo實例

單頁面應用(SPA)多頁面應用(MPA)

  • 多頁面應用(MPA)

    一個項目由不少頁面組成,使用這個產品,主要就是頁面之間的跳轉(pc端多頁面應用居多);

    基於框架開發的時候,須要在webapck中配置多入口,每個入口對應一個頁面;

  • 單頁面應用(SPA)

    只有一個頁面,全部須要展現的內容都在這一個頁面中實現切換,webapck只須要配置一個入口便可

    移動端單頁面應用居多或者pc端系統類也是單頁面應用爲主

  • 如何實現單頁面應用

    弊端: 因爲首頁中的內容包含了全部模塊的信息,因此第一次加載速度很慢

    解決vue/react 實現模塊化組件化開發,基於他們提供的路由實現SPA單頁面應用,基於webpack打包

路由

yarn add react-router-dom

BrowerRouter

瀏覽器路由

基於H5中history API(pushState,replaceState,popstate)來保持url和ui的同步

真實項目中應用很少,通常只有當前是基於服務器渲染的,咱們纔會使用瀏覽器路由

<BrowserRouter basename="/calendar">
  <Link to="/today" />
</BrowserRouter>
//最後呈現的地址  /calendar/today

basename:string

  • 全部位置的基準URL,basename的正確格式是前面有一個斜槓,可是尾部不能有

getUserConfirmation:func (這個有點不太懂)

  • 用於確認導航函數,默認使用window.confirm

    // 使用默認的確認函數
    const getConfirmation = (message, callback) => {
      const allowTransition = window.confirm(message)
      callback(allowTransition)
    }
    <BrowserRouter getUserConfirmation={getConfirmation}/>

    keyLength:number

    • location.key的長度,默認是6
    • <BrowserRouter keyLength={12} />

    children:node

    單個子元素

HashRouter

哈希路由

真實項目中(先後端分離的項目:客戶端渲染),咱們常用哈希路由來完成的

基於原生js構造了一套相似於history API的機制,每一次路由切換都是基於window.location.hash 完成的

  • hash-router只能出現一個子元素

route

  • path:設置匹配地址,可是默認不是嚴格匹配,當前頁面哈希地址只要包含完整的它(內容是不變的,都能匹配上)
    • path='/' 和它匹配的只有要斜杆就能夠
  • component :一旦哈希值和當前router的path相同了,則渲染component執行的組件
  • exact: 讓path的匹配嚴謹一些
  • strict:不經常使用,可是要要加/
  • render: 權限校驗,渲染組件以前驗證是否存在權限,不存在作一些特殊處理
Switch組件能夠解決這個問題,和switch case 同樣,只要有一種校驗成功,就再也不向後校驗  
<HashRouter>
        <Switch>
            <Route path={'/'} exact component={A}>AAA</Route>
            <Route path={'/user'} component={B}/>
          <Route path={'/pay'}  render={() => {
                let flag = localStorage.getItem('flag');
                if (flag && flag === 'safe') {
                    return <C/>
                }
                return '權限不安全';
            }}/>
          //上述都設置完成後,會在末尾設置一個匹配:以上都不符合的狀況下,咱們路由地址是違法的
          //(不設置path就是匹配全部的地址規則)
            <Route render={ ()=>{
            return <div>404</div>
            }}/>
          //重定向
          //to [string]
          //
          <Redirect to='/'/>
        </Switch>
    </HashRouter>

basename:string 全部位置的基準URL

Link

react-router提供的路由切換組件

原理: 基於Link組件渲染,渲染後的結果就是A標籤,To對應的信息最好變成href中的內容

to:string 一個字符串鏈接

to:object 一個對象的時候,下面屬性

  • pathname - 要連接到的路徑
  • search - 查詢參數
  • hash - URL 中的 hash,例如 #the-hash
  • state - 存儲到 location 中的額外狀態數據
<Link to={{
  pathname: '/courses',
  search: '?sort=name',
  hash: '#the-hash',
  state: {
    fromDashboard: true
  }
}} />

replace:bool(默認是false) 替換history stack中當前的地址(true),還能夠追加一個新的地址(false)

react-router中提供的組件必須在HashRouter或者BrowerRouter裏面

NavLink

跟link相似,都是爲了實現路由切換跳轉的,不一樣在於,nav-link組件在當前頁面hash和組件對應地址相吻合的時候,會默認給組件加一個active樣式,讓其選中態

Nav-Link不是點擊誰,誰有選中的樣式(可是能夠路由切換),當前頁面哈希後的地址和Nav-Link中的to進行比較,哪一個匹配了,哪一個纔有選中的樣式

to和replace等屬性都有,用法同樣

activeClassName:把默認的active樣式改成本身設定的

activeSyle:把匹配的這個nav-link設置行內樣式

exact & strict控制匹配的時候是不是嚴格匹配

isActive: 匹配後執行對應的函數

with-Router

把一個非路由管控的組件,模擬成爲路由管控的組件

export default withRouter(connect()(組件));
先把nav基於connect高階一下,返回的是一個代理組件proxy,
把返回的代理組件受路由管控

受路由管控組件的一些特色:

  1. 只有當前頁面的哈希地址和路由指定的地址匹配,纔會把對應的組件渲染(with-router是沒有地址匹配,都被模擬成爲受路由管控的)
  2. 路由切換的原理,凡是匹配的路由,都會把對應的組件內容,從新添加到頁面中,相反,不匹配的都會在頁面中移出掉,下一次從新匹配上,組件須要從新渲染到頁面中,每一次路由切換的時候(頁面的哈希路由由地址改變),都會從一級路由開始從新校驗一遍

  1. 全部受路由管控的組件,在組建的屬性props上默認添加了三個屬性

    history

    • push 向池子中追加一條新的信息,達到切換到指定路由地址的目的

      this.props.history.push('/plan')js中實現路由切換

    • go 跳轉到指定的地址(傳的是數字 0當前 -1上一個 -2上兩個...)

    • go-back =>go(-1) 回退到上一個地址

    • go-forward =>go(1) 向前走一步

    location 獲取當前哈希路由渲染組件的一些信息

    • pathname: 當前哈希路由地址
    • search : 當前頁面的問號傳參值
    • state: 基於redirect/link/nav-link中的to,傳遞的是一個對象,對象中編寫state,就能夠再location.state中獲取

    match :獲取當前路由匹配的一些結果

    • params: 若是當前路由匹配的是地址路徑參數,則這裏能夠獲取傳遞參數的值

Redirect

重定向3xx

to:string 要重定向的地址

to:object 要重定向的位置

<Redirect to={{
  pathname: '/login',
  search: '?utm=your+face',
  state: {
    referrer: currentLocation
  }
}} />

push 重定向會將新的位置推入歷史記錄

<Redirect push to="/somewhere/else" />

<Redirect from={'/custom'} to={'/custom/list'}/>
當請求的是/custom的時候,直接跳轉到/custom/list的路由地址

qs

yarn add qs

問號傳參

OA和ERP

OA:企業辦公管理系統(偏向於有助於平常辦公)

ERP: 企業戰略資源管理系統(偏向於有管理思想)

  • 釘釘
  • TAPD
  • 今目標
  • 紛享銷客

CRM:客戶管理系統

CMS:內容管理系統(內容分發平臺)

IM:即時通訊系統

redux中間件

redux-logger:可以在控制檯清晰的展現當前redux操做的流程和信息(原有狀態,派發信息,修改後的狀態)

redux-thunk: 處理異步的dispatch派發

redux-promise: 在dispatch派發的時候支持promise操做

yarn add redux-logger redux-thunk redux-promise

store/index.js
=====
import {createStore, applyMiddleware} from 'redux';//applyMiddleware導入中間件
import reduxLogger from 'redux-logger';
import reduxThunk from 'redux-thunk';
import reduxPromise from 'redux-promise';
import reducer from './reducer';

let store = createStore(reducer, applyMiddleware(reduxLogger, reduxThunk, reduxPromise));
export default store;

    //=>PROMISE中間件的語法
    create(payload) {
        return {
            type: TYPES.CUSTOM_CREATE,
            //=>傳遞給REDUCER的PAYLOAD須要等待PROMISE成功,把成功的結果傳遞過去
            payload: new Promise(resolve => {
                setTimeout(() => {
                    resolve(payload);
                }, 3000);
            })
        }
    }

 create(payload) {
        //=>THUNK中間件的使用語法:在指定執行派發任務的時候,等待3000MS後在派發
        return dispatch => {
            //=>DISPATCH都傳遞給咱們了,咱們想何時派發,本身搞定便可
            setTimeout(() => {
                dispatch({
                    type: TYPES.CUSTOM_CREATE,
                    payload
                });
            }, 3000);
        };
    }

路由問號傳參案例

Ant Desion UI框架

yarn add antd;

複習

{React.createElement('a',{href:'http://www.baidu.com'},'Hello')}
//標籤  屬性   子元素
class HelloMessage extends Component{
    render(){
        let child=React.createElement('li',{className:'ddd'},'我是子頁面');
        return <div>Hello {this.props.name}
            {React.createElement('a',{href:'http://www.baidu.com'},'Hello')}
            <br/>
            {React.createElement('ul',{className:'ccc'},child
                )}
        </div>
    }
}
style 屬性應該由CSS屬性構成的JS對象
* className=''
* style={{fontSize:50,backgroundColor:'red'}}   // zIndex  多峯命名
let styles={
        fontSize:50,
        fontWeight: 'bold',
        backgroundColor: 'red',
      };
{{styles}}

模擬 if
{this.state.tags.length === 0 && '等於0'}     //A爲真返回B
   renderTags(){
      if(this.state.tags.length===0) return <p>裏面沒有元素</p>
      return  <ul>
        {this.state.tags.map((tag,index)=><li key={index}>{tag}</li>)}
        <hr />
      </ul>
    }
{this.renderTags()}

props 屬性
state  組件的狀態  能夠經過 this.setState進行更改

無狀態組件
const Hellos = (props) => <div>Hello {props.name}</div>;
<Hellos name={'zhangsan'}/>
有狀態組件
//定義一個時間的方法,掛載前開始定時器執行這個方法,卸載後清除掛載前的那個定時器方法
export default class LikeButton extends React.Component {
    constructor(props) {
        super(props);
        //初始化狀態
        this.state = {
            data: new Date()
        }
    };
    componentDidMount(){
        this.timerId=setInterval(
            ()=>this.tick()
        )
    }
    //方法
    tick(){
        this.setState({
            data:new Date()
        })
    }
    componentWillUnmount(){
        clearInterval(this.timerId);
    }
    render() {
        return <div>
            <h3>{this.state.data.toLocaleTimeString()}</h3>
        </div>
    }
}

props 對於使用他的組件來講是隻讀的,只能經過父組件進行修改
state  能夠經過 this.setState({  }) 進行修改
注意不要在push pop shift unshift  splice 等方法修改
應該用concat  slice   filter  會放回一個新數組

原生事件
能夠再componentDidMount方法中經過addEventListener 綁定瀏覽器原生事件
componentWillUnmount 方法解除 removeEventListener

在dom 中 設置 ref屬性指定一個名稱    經過 this.refs.指定名稱獲取

組合組件

父組件 <Avatar username="pwh" />
    
const Avatar = (props) => {
  return (
    <div>
      //子組件   經過屬性傳遞
      <ProfilePic username={props.username} />
      <ProfileLink username={props.username} />
    </div>
  );
}

循環的時候必需要給key
// arr是在父組件裏面聲明的arr數組的屬性
<ul>{this.props.arr.map((v,i)=>{
                return <li key={i}>{v}</li>
            })}</ul>
組件標籤裏面包含的子元素經過 this.props.children

<LikeButton username={'pwh'} arr={[1,2,3,4]}>
        <span>12123</span>
        <p>我是一個p標籤</p>
    </LikeButton>
props.children一般是一個組件對象的數組,當 props.children是惟一的子元素,那就不是數組

點擊事件的內聯樣式
onClick={  ()=>(console.log(1))}
第二種方法
onClick={this.handleClick}  //記得不要再這裏帶(e)參數 會報錯的
<div onClick={this.handleClick.bind(this)}>${this.props.name}</div>
handleClick=(e)=>{
        console.log(e.target.innerHTML);
    };   //函數建議使用箭頭函數的寫法

經過  setState來修改state 
this.setState({
      count: this.state.count+1
    })
store有四個方法。
getState: 獲取應用當前 state。
subscribe:添加監聽器。
dispatch:分發 action。更新 state。
replaceReducer:替換 store 當前用來處理 state 的 reducer。

二者的關係是: state=store.getState()

經常使用的是dispatch,這是修改State的惟一途徑,使用起來也很是簡單。
import {createState} from 'redux';
function counter(state=0,action) {
    switch(action.type){
        case 'INCREMENT':
            return state + 1;
        case 'DECREMENT':
            return state-1
        default:
            return state
    }
}
let store=createState(counter);
store.subscribe(()=>{
    console.log(store.getState());
});
store.dispatch({type:"INCREMENT"});

action 惟一的約束僅僅就是包含一個type
store由redux的createStore(reducer)生成的

    state經過store.getState()獲取的

    action本質上是一個包含type屬性的普通對象

    改變 state必須dispatch一個action

    reducer 本質上action.type  來更新的

實際上state就是全部reducer返回的彙總

redux有五個API
  createStore(reducer,[])
  combineReducers(reducers)
  applyMiddleware(...middlewares)
  bindActionCreators(actionCreatore,dispatch)
  compose(...functions)

Redux 強調三大基本原則
* 惟一數據源
* 保持狀態只讀
* 數據改變只能經過純函數完成

過程總結

建立一個操做指令action ->建立一個reducer -> 經過createStore(reducer) 建立一個store

經過store dispath(action) 執行reducer 中更新操做,更新store中的數據

學習redux

咱們大多數人的學習過程通常是——一個按部就班、逐步迭代的過程,而redux的學習卻不是這樣,你不看概念,就無法理解示例demo,你不敲示例demo,你就沒法徹底理解redux的各類基礎概念。因此最終發現,redux的最好的學習方式就是,通讀一個個的概念,敲出示例demo,再根據示例demo,反過來理解概念,循環往復,總能學的懂!!

const todo={
    TOGGLE_TODO:'TOGGLE_TODO',
    GET_TODOS:'GET_TODOS',
    toggleTodo({ items,id }) {
        return {
            type: todo.TOGGLE_TODO,
            items:items,
            id:id
        }
    },
    getTodos({items}){
        return {
            type:todo.GET_TODOS,
            items:items
        }
    }
};
export default todo;

// ======

export default function(state = {默認值}, action) {
    switch(action.type){
        case GET_TODOS:
            return {todos:[...action.items]}
        case TOGGLE_TODO://deleted=true
            return {
                todos:action.items.map((i)=>{
                    if(i.id===action.id){
                        return {
                            ...i,
                            status:i.status===0?1:0
                        }
                    }else{
                        return {
                            ...i
                        }
                    }
                })
            }
        default:
            return state;
    }
}

const store = createStore(todo);

redux-hook

dva.js

npm install dva-cli -g

入門文檔

put 用於觸發action put({type:types.ADD})

call 用於調用異步邏輯,支持promise

call(第一個參數是一個方法,第二個是傳入這個方法的參數)

take 等待dispatch 匹配某個action

yield 方法(傳入方法的參數) //跟上面等同

run() 方法是添加功能 * yield

effect 都是一個簡單對象

select 用於從state裏獲取數據

定義路由

router/Products.js

import React from 'react';

const Products = (props) => (
  <h2>List of Products</h2>
);

export default Products;

添加路由
router.js
<Route path='/products' exact component={Products}/>
編寫組件

components/編寫組件.js

定義Model
model/..js
    export default {
      namespace:'count',
      state:0,
      reducers:{
        add(count){return count+1},
        minus(count){return count-1}
      }
    }
在indexjs 載入
    app.model(require('./models/products').default);

function IndexPage(props) {
  return (
    <div>
      <h3>      {props.count}</h3>
      <button key={'add'} onClick={()=>{props.dispatch({type:'count/add'})}}>+</button>
      <button key={'munus'} onClick={()=>{props.dispatch({type:'count/minus'})}}>-</button>
    </div>
  );
}

IndexPage.propTypes = {};

export default connect(({count}) => ({count}))(IndexPage);

react react-redux react-saga

react-sage是更好的處理異步

dva科普

function* g1() {
  yield 2;
  yield 3;
  yield 4;
}

function* g2() {
  yield 1;
  yield* g1();
  yield 5;
}

var iterator = g2();

console.log(iterator.next()); // { value: 1, done: false }
console.log(iterator.next()); // { value: 2, done: false }
console.log(iterator.next()); // { value: 3, done: false }
console.log(iterator.next()); // { value: 4, done: false }
console.log(iterator.next()); // { value: 5, done: false }
console.log(iterator.next()); // { value: undefined, done: true }
 yield* 表達式用於委託給另外一個generator 或可迭代對象。

yarn add roadhog

UMI.js

全局安裝umi cnpm install -g umi

路徑似路由

umi div 執行

 umi g 建立一些頁面

umi g page index    是根路徑
建立 umi 項目
yarn create 項目名
import React from 'react'
import dva from 'dva'
import Counter from './Counter'

//dva 是一個函數 經過執行它能夠拿到一個app 對象
let app = dva();

// function delay(ms) {
//     return new Promise(function (resolve, reject) {
//         setTimeout(function () {
//             resolve()
//         }, ms)
//     })
// }
//
// function getAmount() {
//     return fetch('http://localhost:3000/amount').then(res => res.json());
// }

//app.router app.start() app.model
//一個模板就是一個狀態,而後把reducer和狀態寫在一塊兒
//添加一個模型
app.model({
  //命名空間: 由於一個應用會有不少個模型,每個模型有一個對象
  namespace: 'counter',
  //此命名空間的默認狀態
  state: {current: 0, height: 0},
  //他是用來接收action ,修改倉庫狀態
  reducers: {
    //reducer接受老狀態和動做,返回新狀態
    //state老狀態(parameter) action:AnyAction
    add(state, action) {
      // let current = state.current + action.payload;
      // return {current, height: current > state.height ? current : state.height}
      return {current: state.current + 1}
    },
    // minus(state, action) {
    //     return {...state, current: state.current - action.payload}
    // }
  },
  //它是用來執行反作用的,好比說異步操做,調用api接口
  effects: {
    //表示這是一個generator effect=redux-saga/effects
    // * add(action, {call, put}) {
    //     //表示這是一個generator
    //     //amount  是接口中的變量  call 調用方法
    //     let {amount}=yield call(getAmount);
    //     //type;'方法'
    //     yield put({type:'add',payload:amount})
    //     // yield call(delay, 1000);
    //     // yield put({type: 'minus'})//能夠不加前綴,counter/minus/派發其餘的能夠寫
    // }
  },
});
//參數是一個函數,此應用自己就是要薰染函數的返回值
app.router(() => <Counter />);
//本質是啓動應用,就是經過app.router獲取組件,經過ReactDOM渲染到容器上
app.start('#root');

import React from 'react';
import {connect} from 'dva';
import './Counter.css';

class Counter extends React.Component {
    render() {
        return (<div>
            <div className={'container'}>
                <p>當前記錄{this.props.current}</p>
                <button onClick={() => this.props.dispatch({type: 'counter/add'})}>add</button>
            </div>
        </div>)
    }
}

export default connect(
    state => state.counter
)(Counter);
相關文章
相關標籤/搜索