npm install -g creat-react-app # 建立一個項目 create-react-app jianshu cd jianshu npm run start # 安裝styled-components 全局統一管理css npm install --save styled-components # 全局樣式引入 https://meyerweb.com/eric/tools/css/reset/
background-size: contain;
組件:就是將整個UI拆分爲不少小的,可重用的UI,每個小的UI獨立作本身的事情。html
一個組件須要繼承React.Component
重寫render()
函數,返回JSX
react
注意點:ios
1.只要有JSX的地方必須引入
React
git2.返回值中只能有一個元素包裹,
div
包裹整個JSX,以下是錯誤的返回方式es6return ( <div> hello world </div> <div> hello world </div> );
import React, { Component } from 'react'; class App extends Component { render() { return ( <div> hello world </div> ); } } export default App;
引用官方的一句話:
Don’t be afraid to split components into smaller components.
github儘量的讓每個組件都能分工明確,減小重複,加大可重用。例如表格組件,就必須被拆分爲一個單獨的組件,它可能在各個地方被用到。web
組件拆分優勢:讓開發顯得更加清晰明瞭。ajax
未拆分組件看起來就沒有食慾算法
return ( <div className="Comment"> <div className="UserInfo"> <img className="Avatar" src={props.author.avatarUrl} alt={props.author.name} /> <div className="UserInfo-name"> {props.author.name} </div> </div> <div className="Comment-text"> {props.text} </div> <div className="Comment-date"> {formatDate(props.date)} </div> </div> );
將首頁拆分爲Header, Right,以下定義一個首頁組件,在首頁組件中分別引入Header和Right組件,這就是一個簡單的組件拆分和組合.
<React.Fragment />
是一個虛擬的組件,僅僅是爲了包裹其中的元素,並不會出如今頁面上
Index.js
import React from 'react' import Header from './Header' import Right from './Right' class Index extends React.Component { render() { return ( <React.Fragment> <Header /> <Right /> </React.Fragment> ); } } export default Index
Header.js
import React from 'react' class Header extends React.Component { render() { return ( <React.Fragment> <div>i am header component</div> </React.Fragment> ); } } export default Header
Right.js
import React from 'react' class Right extends React.Component { render() { return ( <React.Fragment> <div>i am right component</div> </React.Fragment> ); } } export default Right
組件傳值分爲如下幾種狀況:1. 父組件傳值到子組件;2.子組件向父組件傳值;3.隔代組件傳值
注意點: props是隻讀的
例如Index.js
向Header.js
組件傳值
{/* Index.js 傳遞title屬性 */} return ( <React.Fragment> <Header title="Header Title"/> <Right /> </React.Fragment> ); {/*Header.js 接受值*/} return ( <React.Fragment> <div>{this.props.title}</div> </React.Fragment> );
state能夠用來存儲數據,記性狀態管理。
it is private and fully controlled by the component.
render函數執行:組件state和props發生改變,render函數會被執行,當父組件從新渲染時候,子組件render函數也會被從新執行
class Index extends React.Component { constructor(props) { super(props) this.state = { title: 'Header title' } } render() { return ( <React.Fragment> <Header title={this.state.title}/> <Right /> </React.Fragment> ); } } export default Index
當組件之間傳值的時候,接收一方每每須要對參數進行校驗,這就是PropTypes的做用
import PropTypes from 'prop-types' class ReactUI extends React.Component { render() { return ( ) } } //屬性類型 ReactUI.propTypes = { //表示name必須是string類型 name: PropTypes.string //屬性必傳 id: PropTypes.element.isRequired }
默認值
The
propTypes
typechecking happens afterdefaultProps
are resolved
class Greeting extends React.Component { static defaultProps = { name: 'stranger' } render() { return ( <div>Hello, {this.props.name}</div> ) } }
在頁面加載的過程當中,特定時間點自動執行的函數稱爲生命週期函數。
生命週期的四個階段:
這一階段主要加載props和state
componentWillMount
:在組件即將被掛載到頁面的時刻執行
render
:
componentDisMount
:在組件掛載完成以後會被執行
使用場景:用於發送ajax請求
componentWillReceiveProps
:
子組件從父組件接受屬性,第一次存在父組件,render函數執行,該函數不會被執行,當父組件render函數從新被執行,就會執行這個函數
shouldComponentUpdate
:組件是否須要被更新,返回boolean值
使用場景:shouldComponentUpdate(nextProps, nextState);這兩個參數來接受即將改變的props和state的值;演示:
shouldComponentUpdate(nextProps, nextState) { if (nextProps.title !== this.props.title) { //放生改變,則須要從新渲染 return true } else { return false } }
componentWillUpdate
: 當組件被更新以前被執行,可是取決於shouldComponentUpdate
的返回結果,
render
componentDidUpdate
:組件更新完成以後會被執行
shouldComponentUpdate
componentWillUpdate
componentDidUpdate
:
componentWillUnmount
: 組件從頁面移除的時候會被執行
無狀態組件: 就是組件中只含有render()函數的組件,
import React from 'react' class Header extends React.Component { render() { return ( <React.Fragment> <div>xx {this.props.title}</div> </React.Fragment> ); } } export default Header
這種組件能夠被簡化爲無狀態組件
export default (props, context) => { return ( <React.Fragment> <div>xx {props.title}</div> </React.Fragment> ); }
案例:使用List集合渲染一個頁面
key的做用:
Keys help React identify which items have changed, are added, or are removed. Keys should be given to the elements inside the array to give the elements a stable identity
這個主要和虛擬DOM有關,能夠提升虛擬DOM性能。
兄弟姐妹之間的key必須獨一無二
import React from 'react' class Event extends React.Component { constructor(props) { super(props) this.state = { data: [1, 2, 3, 4, 5, 6, 7] } this.handClick = this.handClick.bind(this); } render() { return ( <div> <ul> { this.state.data.map((item) => ( <li key={item}>{item}</li> )) } </ul> </div> ) } } export default Event
JSX
既不是string也不是html,是React獨有的一種語法,jsx中須要注意的點:
class
如:<div class='show'></div>
會和ES6關鍵字衝突會使用className代替<label></label>
for屬性也會使用htmlFor
代替虛擬DOM就是一個JS對象,用來描述真實DOM
jsx轉換成js對象
<div className='show'>hello world</div> React.createElement('div',{className: 'show'}, 'hello world');
流程講解
- state數據
- jsx模板
- 數據 + 模板生成虛擬DOM
- 使用虛擬DOM來來生成真實DOM, 顯示在頁面上
- state發生改變
- 數據 + 模板生成新的虛擬DOM
- 比較原始的虛擬DOM和新的虛擬DOM之間的區別,找到不一樣之處
- 操做DOM,改變不一樣的地方
diff算法
上面的第七步驟是如何比對不一樣之處呢,這裏使用了diff算法。
同層比對:第一層有差別,則不會再進行比對,直接向下所有渲染,若是第一層相同,則向下繼續比較。
根據Key作關聯來進行比對: 同層的key必須固定不變,這樣才能再進行比對的時候準確的找到原始的dom節點。假如使用index做爲key值,當刪除一個元素,那個被刪除元素後面的全部標籤key值都會被改變,那麼進行比對的時候,就會出現性能消耗。
React events are named using camelCase, rather than lowercase.
例如
onClick
,onBlur
...
import React from 'react' import './event.css' class Event extends React.Component { constructor(props) { super(props) this.state = { isShow: true } } render() { return ( <div> <button onClick={this.handClick.bind(this)}>切換</button> <div className={this.state.isShow ? 'show':'hidden'}>你好</div> </div> ) } handClick() { this.setState((state) => ({ isShow: !state.isShow })) } } export default Event
傳遞參數
render() { return ( <div> <button onClick={() => this.handClick(this.state.isShow)}>切換</button> <div className={this.state.isShow ? 'show':'hidden'}>你好</div> </div> ) } handClick(isShow) { this.setState((state) => ({ isShow: !isShow })) }
傳遞參數的幾種方式
<button onClick={(e) => this.deleteRow(id, e)}>Delete Row</button> <button onClick={this.deleteRow.bind(this, id)}>Delete Row</button> <SearchSwitch onClick={() => switchItem(page, totalPage)}>換一批</SearchSwitch>
https://redux.js.org/introduction/getting-started
# 安裝 npm install --save redux
Actions
: 就像一個快遞,裏面包含地址(行爲),以及物品(數據)
Reducers
:就像一個快遞員,負責分發不一樣的快遞,根據地址(行爲)派送到不一樣地方
Store
: 就像一個總站,能夠存儲這些快遞,最後返回給用戶。
與物流之間的區別就是:store不可變,只能夠返回數據,而原先的數據一直保留在store裏面
第一步:建立Store index.js
這裏藉助redux createStore方法建立store,而且引入reducer
import { createStore } from 'redux' import reducer from './Reducers' const store = createStore( reducer, // 這個可使用Chrome redux 插件進行調試 window.__REDUX_DEVTOOLS_EXTENSION__ && window.__REDUX_DEVTOOLS_EXTENSION__() ) export default store
第二步:建立一個reducer
reducer 裏面保存數據的初始狀態,
defaultState
解析action的行爲
const defaultState = { list: ['hello', 'world'] } const reducer = (state = defaultState, action) => { switch (action.type) { case 'add_list_action': return {list: action.data} default: return state } } export default reducer
第三步:使用redux
- this.state = store.getState() 從store中獲取數據
- store.subscribe(this.handSubscribe)訂閱數據,監聽數據的變化
handClick()
方法中主要定義action,而後利用store進行分發
import React from 'react' import store from './store' class ReduxUI extends React.Component { constructor(props) { super(props) this.state = store.getState() this.handClick = this.handClick.bind(this) this.handSubscribe = this.handSubscribe.bind(this) store.subscribe(this.handSubscribe) } render() { return ( <div> <button onClick={this.handClick}>點擊我</button> <ul> { this.state.list.map((item) => { return <li key={item}>{item}</li> }) } </ul> </div> ) } handClick() { //建立一個Action const addListAction = () => ({ type: 'add_list_action', data: ['1', '2', '3'] }) //這是一個異步操做 store.dispatch(addListAction()) } handSubscribe() { this.setState(store.getState()) } } export default ReduxUI
改造1>1.1 中的代碼
nmp install --save react-redux
新增長一個父組件,統一管理store,
這個組件擁有store,
<Provider store={store}>
表示改下面的全部元素均可以使用store
中的數據
import React, { Component } from 'react'; import store from './pages/redux/store' import { Provider } from 'react-redux' import ReduxUI from './pages/redux/ReduxUI' class App extends Component { render() { return ( <Provider store={store}> <ReduxUI/> </Provider> ); } } export default App;
步驟三中的修改
第一步:添加
connect
方法第三步:映射state中的屬性,定義分發的方法
第四步:鏈接組件
import React from 'react' import {connect } from 'react-redux' class ReduxUI extends React.Component { render() { //從react-redux中接收store中的信息,以及分發action的方法 const { list, handClick } = this.props return ( <div> <div> <button onClick={handClick}>點擊我</button> <ul> { list.map((item) => { return <li key={item}>{item}</li> }) } </ul> </div> </div> ) } } //state就是store中state const mapStateToProps = (state) => ({ list: state.list }) //這裏定義分發action的方法,dispatch 就是store的dispatch方法 const mapDispatchProps = (dispatch) => ({ handClick() { //建立一個Action const addListAction = () => ({ type: 'add_list_action', data: ['1', '2', '3'] }) dispatch(addListAction()) } }) //鏈接組件,而且把屬性和action行爲傳遞給組件 export default connect(mapStateToProps, mapDispatchProps)(ReduxUI)
Redux Thunk middleware allows you to write action creators that return a function instead of an action
npm install redux-thunk
to enable Redux Thunk, use
applyMiddleware()
這裏配合redux-devtools-extension調試工具一塊兒使用
import { createStore, compose, applyMiddleware } from 'redux' import reducer from './reducer' import thunk from 'redux-thunk' // 這個就是讓調試插件配合中間件使用 const composeEnhancers = window.__REDUX_DEVTOOLS_EXTENSION_COMPOSE__ || compose const store = createStore( reducer, composeEnhancers(applyMiddleware(thunk)) ) export default store
// 定義一個方法,處理鼠標聚焦行爲 //而後dispatch分發action handFocus(list) { (list.size === 0) && dispatch(actionCreator.getSearchItems()) dispatch(actionCreator.inputFocusAction()) } //另一個文件 //=================================== // 這裏就是上面的action,這個action是一個方法,在該方法裏面發送ajax異步請求, //異步請求以後繼續分發另一個action export const getSearchItems = () => { return (dispatch) => { axios.get("/api/item.json").then((res) => { dispatch(searchItemsAction(res.data.data)) }) } }
正如官方文檔所說:redux-thunk容許你分發一個方法類型的action
asynchronous things like data fetching and impure things like accessing the browser cache
npm install --save redux-saga
import { createStore, compose, applyMiddleware } from 'redux' import reducer from './Reducers' import createSagaMiddleware from 'redux-saga' import reduxSaga from './reduxSaga' const sagaMiddleware = createSagaMiddleware() const composeEnhancers = window.__REDUX_DEVTOOLS_EXTENSION_COMPOSE__ || compose const store = createStore( reducer, composeEnhancers(applyMiddleware(sagaMiddleware)) ) //運行本身的reduxSaga sagaMiddleware.run(reduxSaga) export default store
reduxSaga.js
import { takeEvery, put } from 'redux-saga/effects' import axios from 'axios' function* getJson() { const res = yield axios.get('./data.json') yield put({type:"add_list_action", data: res.data}) //繼續派發action } function* reduxSaga() { // 當store分發'add_list_action' 這個action的時候,會調用getJson方法 yield takeEvery("add_list__pre_action", getJson) } export default reduxSaga
immutable能夠保證數據一旦建立,就不會被改變
npm install --save immutable
//目的:將store變成不可變,這是一個簡單reducer.js import * as constants from './constant' //es6語法 import { fromJS } from 'immutable' //這裏將defaultStore變成一個immutable對象 const defaultStore = fromJS({ focused: false, mouseIn: false, list: [], page: 1, totalPage: 1 }) export default (state = defaultStore, action) => { switch (action.type) { case constants.input_focused: //這裏的set immutable對象的一個值,其實並非改變了state,而是返回一個新的對象 //經過set能夠簡化咱們的操做,不用從新構建所有的對象 return state.set('focused', true) default: return state } } // 如何獲取一個immutable對象呢? const mapStateToProps = (state) => ({ //這裏表示獲取header組件下的一個immutable對象 //這裏是 redux-immutable 獲取數據的形式 // state.header.get('focused') 爲immutable獲取數據的格式 focused: state.getIn(['header', 'focused']), //state.get('header').get('focused') mouseIn: state.getIn(['header', 'mouseIn']), list: state.getIn(['header', 'list']), page: state.getIn(['header', 'page']), totalPage: state.getIn(['header', 'totalPage']) }) //經過action傳遞參數的時候也應該是一個immutable對象 export const searchItemsAction = (data) => ({ type: constants.render_list, data: fromJS(data), totalPage: Math.ceil(data.length / 10) })
redux-immutable
is used to create an equivalent function of ReduxcombineReducers
that works with Immutable.jsstate.
咱們常常面臨不少不少組件,可是若是把全部組件的store,reducer, action 維護在一個目錄下,那將是慘目忍睹的,因此一般狀況下咱們會分開管理redux,而後將全部的reducer組合在一塊兒
npm install --save redux-immutable
//import { combineReducers } from 'redux' import { combineReducers } from 'redux-immutable' //能夠管理immutale對象 import { reducer as headerReducer} from '../header/store' const reducer = combineReducers({ header: headerReducer }) export default reducer
React Router is a collection of navigational components that compose declaratively with your application.
npm install --save react-router-dom
import { BrowserRouter, Route } from "react-router-dom" ... <BrowserRouter> {/* 表示路由到一個組件 */} <Route path="/home" exact component={Home} /> </BrowserRouter>
Route
: 表示要路由的到哪個組件
Link
: 表示一個鏈接,跳轉到某個頁面
一個項目,不一樣的頁面應該按需加載,而不是當首頁出來以後加載全部的組件,例如咱們訪問首頁的時候只會加載首頁相關的資源,訪問詳情頁的時候加載詳情頁的代碼,這樣能夠提升首頁訪問速度以及用戶體驗
npm install --save react-loadable
案例演示
import React from 'react' import Loadable from 'react-loadable' const LoadableComponent = Loadable({ //這裏`reactUI`是一個組件,表示這個組件將會被按需加載 loader: () => import('./reactUI'), // 這裏是一個函數,能夠是一個組件,表示頁面加載前的狀態 loading: () => <div>正在加載</div> }) //返回無狀態組件 export default () => { return <LoadableComponent/> }
npm install --save styled-components
定義全局樣式
import { createGlobalStyle } from 'styled-components' export const ResetCSS = createGlobalStyle` body { line-height: 1; } .... ` // 引用全局樣式 import { ResetCSS } from './style' ... render() { return ( <ResetCSS /> ) }