命令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 修改端口號
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建立組件有兩種方式
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 基於這個插件咱們能夠給組件傳遞的屬性設置規則 設置的規則不會影響頁面的,可是會控制檯報錯
顯示當前文件的最新版本信
返回上一個版本
描述一個組件或者程序從建立到銷燬的過程,咱們能夠再過程當中基於鉤子函數完成一些本身的操做
基本流程 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(控制層)
核心思想: 經過數據的改變來影響視圖的渲染(數據)
屬性的屬性是隻讀的:只能調用組件時候傳遞進來,不能本身在組建內部修改(可是能夠設置默認值和規則)
傳遞信息的方式
父組件須要把信息傳遞給子組件
屬性傳遞:調取子組件的時候,把信息基於屬性的方式傳遞給子組件(子組件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 上下文
屬性操做起來簡單,子組件是被動接受傳遞的值(組件內的屬性是隻讀的),只能父傳子(子傳父不行,父傳孫也須要處理:子傳子,子再傳孫)
上下文操做起來相對複雜一些,子組件是主動獲取信息使用的(子組件是能夠修改獲取的上下文信息,可是不會影響到父組件中的信息,其餘組件不受影響),一旦父組件設置了上下文信息,他後代組件均可以直接拿到,不須要不層層的傳遞
其實子組件也能修改父組件中的信息
利用回調函數機制: 父組件把一個函數經過屬性或者上下文的方式傳遞給子組件,子組件要把這個方法執行便可
(也就是子組件中執行了父組件方法,還能夠傳遞一些值過去),這樣組件在這個方法中,想把本身的信息改爲啥就改爲啥
/* 在父組件中 設置子組件上下文屬性類型 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>
- 讓兩個平行組件擁有一個共同的父組件
- 基於redux來進行狀態管理,實現組件之間的信息傳遞
- 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> <button className={'btn btn-danger'} onClick={ () => { dispatch({ type: 'VOTE_AGAINST' }) } }>反對 </button> </div> } }
redux:進行狀態統一管理的類庫(適用於任何技術體系的項目)
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> <button className={'btn btn-danger'} onClick={ () => { this.props.store.dispatch(action.vote.against()) } }>反對 </button> </div> } }
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實例
多頁面應用(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/todaybasename: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-hashstate
- 存儲到 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, 把返回的代理組件受路由管控受路由管控組件的一些特色:
- 只有當前頁面的哈希地址和路由指定的地址匹配,纔會把對應的組件渲染(with-router是沒有地址匹配,都被模擬成爲受路由管控的)
- 路由切換的原理,凡是匹配的路由,都會把對應的組件內容,從新添加到頁面中,相反,不匹配的都會在頁面中移出掉,下一次從新匹配上,組件須要從新渲染到頁面中,每一次路由切換的時候(頁面的哈希路由由地址改變),都會從一級路由開始從新校驗一遍
全部受路由管控的組件,在組建的屬性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: 企業戰略資源管理系統(偏向於有管理思想)
CRM:客戶管理系統
CMS:內容管理系統(內容分發平臺)
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); }; }
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 惟一的約束僅僅就是包含一個typestore由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);
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是更好的處理異步
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);