目錄javascript
static getDerivedStateFromProps (props){}
的用法react diff算法 -> fiber算法css
小貼士:小圖標庫 https://emojipedia.org/html
npx create-react-app test-react
//經過npx 使用create-react-app腳手架建立項目test-react //npx 臨時安裝一個模塊
//此時若是執行 npm run eject
,則執行解包,把構建配置寫入package.json,及生成config和scripts文件夾等,且解包後不能再打包回去了。解包以前的package.json裏的scripts腳本命令也自動改變。 使用中根據須要選擇是否解包配置。
package.json文件dependencies中react-dom用於建立前端組件(app用react-native),react-scripts用於構建(底層封裝的webpack)前端
① 函數方式:java
import React from 'react' import ReactDom from 'react-dom' //函數方式原理: // 一、初探: 將xml格式虛擬DOM直接賦值給一個變量 // const app = <h1> welcome React 16.8 </h1> // 二、深刻:是否能夠靈活的動態改變虛擬dom的屬性或子元素內容呢? // 2.1 定義成一個方法傳參進去,而後返回該虛擬dom. // const render = (props)=> <h1>Welcome {props.title}</h1> // 2.2 而後,建立組件時傳入參數。 // const app = render({ // title:'React 16.8' // }) const App = (props)=>{ return ( <div> {/* jsx 虛擬dom元素不要加引號*/} {/* 只要是jsx裏要插入js代碼,加一層花括號(包括註釋),兩種語法格式且可嵌套交替使用*/} <h1 title={props.title}>Welcome {props.title}!!!</h1> </div> ) } //將虛擬dom組件渲染到網頁真正dom樹上 ReactDom.render( <App title='ksfg' />, document.querySelector('#root') //渲染到public/index.html // ,callback )
② 類繼承React.Component: 經常使用(由於其能夠構建狀態和管理、聲明週期設置等)python
import React, {Component} from 'react' import { render} from 'react-dom' class App extends Component { render(){ //React.Component提供的方法 <div> <h1>類組件</h1> <p>{this.props.title}</p> </div> } } //將虛擬dom組件渲染到網頁真正dom樹上 render( <App title='類組件繼承自React.Component' /> document.querySelector('#root') )
① 建立虛擬dom樹對象react
const vdom = { tag: 'div', attrs:{ className: ['active',''], id:'root' }, children:[ { tag:'', attrs:{}, children:[ {...} ] } ] }
② 調用 React.createElement(type,attrs={},[...children])
方法 把vdom樹渲染到頁面;
③ React.createElement()
方法內部根據 children 遞歸調用該方法把全部dom元素渲染出來。webpack
① 添加內聯樣式web
const color = { color:'#F00'} <p style={color}>添加樣式1</p> 或 <p style={{color:'#F00'}}>添加樣式1</p> //JS語句寫入大括號內
② 添加外部引入css文件算法
import './index.css' <p className="color-red">添加樣式1</p>
③ 動態(是否)添加樣式 (npm i classnames -S
)
import classnames from 'classnames' <p className={classnames('a',{'b':true,'c':false})}>添加樣式1</p> {/*添加了'a'和'b'樣式*/}
④ styled-components插件標籤方式添加樣式 (npm i styled-components -S
)
import styled from 'styled-components' const Title = style.h1` color:#F00 ` <Title>添加樣式1</Title>
⑤ styled-jsx 插件 jsx方式添加樣式
① 有狀態 state 時;
② 要使用生命週期鉤子時;
③ 須要使用 ref 時(由於ref要掛到實例上,函數式組件不能建立實例,但你能夠在函數組件內部使用 ref 屬性,只要它指向一個 DOM 元素或 class 組件);
.....
在受控組件上指定 value 的 prop 能夠防止用戶更改輸入。若是指定了 value,但輸入仍可編輯,則多是意外地將value 設置爲 undefined 或 null。
① 不要直接修改 State ,不會從新渲染組件 ,而是應該使用 setState() 。構造函數是惟一能夠給 this.state 賦值的地方。
② State 的更新多是異步的,出於性能考慮,React 可能會把多個 setState() 調用合併成一個調用 。 由於 this.props 和 this.state 可能會異步更新,因此你不要依賴他們的值來更新下一個狀態 ,要解決這個問題,可讓 setState() 接收一個函數而不是一個對象。這個函數用上一個 state 做爲第一個參數,將這次更新被應用時的 props 作爲第二個參數 。
③ State 的更新會被合併 , 當你調用 setState() 的時候,React 會把你提供的對象合併到當前的 state。
<button onClick={(e) => this.deleteRow(id, e)}>Delete Row</button> <button onClick={this.deleteRow.bind(this, id)}>Delete Row</button>
上述兩種方式是等價的,分別經過箭頭函數和 Function.prototype.bind 來實現。
① Refs 提供了一種方式,容許咱們訪問 DOM 節點或在 render 方法中建立的 React 元素。
② 回調 Refs :
function CustomTextInput(props) { return ( <div> <input ref={props.inputRef} /> </div> ); } class Parent extends React.Component { render() { return ( <CustomTextInput inputRef={el => this.inputElement = el} /> ); } }
① 把該組件改成 PureComponent 先進行前比較(通常就不會再重複屢次渲染了) ② 使用 shouldComponentUpdate 進行再比較
shouldComponentUpdate ( nextProps,nextState){ return (nextProps.xxx !== this.props.xxx) || (nextState.yyy !== this.state.yyy) }
好比:下面這樣在TodoList中某個 ListItem 改變就不會渲染全部的 ListItem 了。
import React, { PureComponent } from 'react' const noop = ()=>{} export default class ListItem extends pureComponent { handleCheckboxChange = ()=>{ /* this.props.completedChange && this.props.completedChange(this.props.id) */ const { completedChange=noop, id } = this.props /* completedChange && completedChange(id) */ completedChange(id) } handleDeleteItem = ()=>{ this.props.deleteItem(this.props.id) } render() { return ( <li> <input type="checkbox" checked={this.props.isCompleted} onChange={ this.handleCheckboxChange} /> <span> {this.props.title} {this.props.isCompleted?'已完成😂':'未完成😢'} </span> <button onClick={ this.handleDeleteItem} >刪除 </button> </li> ) } }
static getDerivedStateFromProps (props){}
的用法示例:
static getDerivedStateFromProps (props){ return { completedText: props.isCompleted ? '完成' : '未完成' } }
import React, { useState, useEffect} from 'react' // useState和 useEffect 是 hook 常見的兩個API const Counter = ()=> { const [count,setCount] = useState(0) //useState是一個方法,該方法的參數就是默認值,結果是一個數組,數組的第一個參數就是state,第二個就至關於setState const [title,setTitle] = useState('數字遞增或遞減') //一個函數式組件能夠有多個state useEffect(()=>{ // useEffect 的參數是一個回調函數,無論組件掛載仍是更新,都會觸發這個回調,相似於 componentDidMount 和 componentDidUpdate 的結合 console.log('渲染了') document.title = '當前的數是${count}' }) return ( <div> <p>{title}</p> { /* 這裏的setCount就是useState所生成的方法,注意和setState不一樣的地方在於參數,這裏的參數就是一個新值便可 */} <button onClick={()=>{ setCount(count-1)}}> - </button> <span>{count}</span> <button onClick={()=>{ setCount(count+1)}}> + </button> </div> ) }
示例以下:
// createContext 是react 提供的用於跨多級組件傳值的方法 import React , {Component, createContext} from 'react' import {render} from 'react-dom' // createContext 這個方法的結果是一個對象,裏面有兩個組件:Provider 和 Consumer , 其中 Provider 用於提供狀態, Consumer 用於接收狀態 const { Provider, Consumer: CounterConsumer // 解構出來的 Consumer 從新賦值爲 CounterConsumer 組件 } = createContext() // 封裝 Provider ,由於直接使用 Provider 不方便管理狀態 class CounterProvider extends Component { constructor(){ super() // 這裏的狀態是共享的,任何 CounterProvider 的後代組件均可以經過 CounterConsumer 來接收 Provider提供的 value 值。 this.state = { count: 100 } } //下面的兩個方法也能夠 傳遞下去 incrementCount = () =>{ this.setState({ count: this.state.count + 1 }) } decrementCount = () =>{ this.setState({ count: this.state.count - 1 }) } render (){ return ( // 使用Provider 組件,必需要有一個 value 值,用於向後代組件傳值,通常是一個對象,由於對象比較靈活。 <Provider value={{ this.state.count, onIncrementCount: this.incrementCount onDecrementCount: this.decrementCount }}> { this.props.chidren} </Provider> ) } } class Counter extends Component { render (){ return ( // Provider 的後代組件經過 Consumer 組件來接收 Provider 提供的 value 值 <CounterConsumer> { // 注意! Consumer 的 children 必須是一個方法,且該方法接收的參數便是 Provider 的 value 值 ({count})=>{ // 解構出 count return <span>{count}</span> } } </CounterConsumer> ) } } class CountBtn extends Component { render (){ return ( { ({onIncrementCount,onDecrementCount}) => { const handlerClick = this.props.type === 'increment' ? onIncrementCount : onDecrementCount return <Button onClick ={ handlerClick}> {this.props.children} </Button> } } <Button > {this.props.chidren} </Button> ) } } class App extends Component { render (){ return ( <> <CountBtn type="decrement" /> <Counter /> <CountBtn type="increment" /> </> ) } } render( // Provider 組件 向後代傳值的形式 <CounterProvider> <App /> </CounterProvider>, document.getElementById('root') )
import React ,{ Component} from 'react' const withCopyright = (YourComponent) =>{ return class WithCopyright extends Component { render (){ return ( <> // 高階組件攔截了 YourComponent 傳入參數,再把參數還給 YourComponent(本身理解的 ) <YourComponent {...this.props} /> <div> © 2019   千鋒教育 </div> </> ) } } } @ withCopyright class YourComponent extends Component { render(){ return ( <div name="傳遞給高階組件的值"> 要添加高階組件的組件 </div> ) } }
讓 create-react-app 支持@ 裝飾器寫法 ,須要作的配置: 一、無論你是要配置什麼,咱們最好的方式是使用 react-app-rewired 這個包來對cra 建立 的項目進行略微 的配置調整, `npm install customize-cra --save-dev` 二、 安裝好以後,在package.json 裏把 scripts 裏的 react-scripts 替換成 react-app-rewired 三、 在根目錄下建立一個 config-overrides.js
module.exports = (config) =>{ // 若是沒使用customize-cra,在這裏配置 return config }
四、 固然若是想要配置更方便,能夠再安裝 customize-cra,`npm install customize-cra --save-dev`,而後把 config-overrides.js 改爲這樣
const {override,addDecoratorsLegacy} = require('customize-cra') module.exports = override( addDecoratorsLegacy() )
五、 `npm i @babel/plugin-proposal-decorators -D`
① Flux 架構思想
* view 視圖層
* ActionCreator(動做創造者) : 視圖層發出的消息(好比 mouseClick)
* Dispatcher(派發器): 用來接收 Actions 、 執行回調函數
* Store(數據層): 用來存放應用的狀態,一旦發生變更,就提醒 Views 要更新頁面
② Flux 的流程:
1. 組件獲取到store中保存的數據掛載在本身的狀態上
二、用戶產生了操做,調用actions的方法
三、actions接收到用戶的操做,進行一系列的邏輯判斷、異步操做
四、而後actions會建立出對應的action,action帶有標識性的屬性
五、actions調用dispatcher的dispatch方法,將action傳遞給dispatcher
六、dispatcher接收到action並根據標識信息判斷以後調用store的更改數據的方法
七、store的方法被調用後,更改狀態,並觸發本身的某一個事件
八、store更改狀態後事件被觸發,該事件的處理程序會通知view去獲取最新的數據
③ Redux: Flux架構思想的一種實現
Redux 使用的三大原則:
一、single Source of Truth(惟一的數據源)
二、State is read-only (狀態是隻讀的)
三、Changes are made with pure function(數據的改變必須經過純函數完成)
④ redux 原理
<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <meta name="viewport" content="width=device-width,initial-scale=1.0"> <meta http-equiv="X-UA-Compatible" content="ie=edge"> <title> Redux 原理 </title> </head> <body> <div> <button onClick="dispatch({type:"DECREASEMENT",n:2})">-</button> <span id="countDisplay">10</span> <button onClick="dispatch({type:"INCREASEMENT",n:1})">+</button> </div> <script> const countDisplay = document.querySelector('#countDisplay') const countState = { count: 5 } const renderCount = (state) =>{ countDisplay.innerHTML = state.count } renderCount(countState) const reducer = (state,action)=>{ if(!state){ return countState } switch(action.type){ case 'DECREASEMENT': return { ...state, count: state.count - action.n } case 'INCREASEMENT': return { ...state, count: state.count + action.n } default: break } } const createStore = (reducer)=>{ let state = null const getState = ()=> state const listeners = [] const subscribe = (listener)=>listeners.push(listener) const dispatch = (action)=>{ reducer(state,action) listeners.forEach(listener =>listener()) } return { getState, subscribe, dispatch } } const store = createStore(reducer) store.subscribe(renderCount) </script> </body> </html>
store=createStore(reducer)
-> 頂層組件經過Provider使用store,<Provider store={store}>
-> 後代組件經過 connect 接收store,connect(mapStateToProps,{...actionCreators})(YourComponent)
-> 建立 action -> 根據action.type 配置actionCreatorsimport React from 'react' import { render } from 'react-dom' import { BrowerRouter as Router, Route } from 'react-router-dom' import App from './App' render( {/* Router 組件只用在頂層,Route組件用在各個要跳轉的後代組件上*/} <Router> <Route component={App} path='/' /> </Router>, document.querySelector('#root') )
App.js:
import React, {Component} from 'react' import { Home,Article,User,NotFound} from './views' import {ArticleDetail} from './Article/ArticleDetail' // NavLink 組件比Link組件多了一個active的class,Redirect組件要搭配Switch使用 import {Route, NavLink as Link, Redirect, Switch } from 'react-router-dom' export default class App extends Component { render(){ return ( <div> <ul> <li> <Link to='/home'>首頁</Link> </li> <li> <Link to='article'>文章</Link> </li> <li> <Link to='/user'>用戶</Link></li> </ul> {/* Switch 組件包含的路由匹配成功一個,便返回再也不往下匹配*/} <Switch> {/* Route包含的組件默認都有history、props等屬性 */} <Route component={Home} path='/home' /> {/* 加入exact屬性,表示不匹配子路由,默認是匹配該路徑和其子路徑的 */} <Route component={Article} path='/article' exact /> {/* 匹配成功的話,這裏的文章詳情將覆蓋Article */} <Route component={ArticleDetail} path="/article/:id" /> {/* 用render渲染組件傳遞參數 */} <Route render={(routeProps)=>{ return this.state.isLogin ? <User {...routeProps} /> : <div>請登陸</div>}} path='/user' /> <Redirect to="/home" from="/" exact /> <Route Component={NotFound} path="/404" /> <Redirect to="/404" /> </Switch> </div> ) } }
views/articles/index.js:
import React,{Component} from 'react' import { Link, Route } from 'react-dom' //import ArticleDetail from './Article/ArticleDetail' import {* as Home} from './backhome' export default class Article extends Component { render(){ return ( <div> {/* query傳參 */} <Link to="/article/1?from=article"> 文章1 </Link> {/* 隱式傳參,好比能夠作埋點 */} <Link to={{pathname:'artical/2',state:{ from:'article'}}}> 文章2 </Link> {/* <Route component={ArticleDetail} path="/article/:id" /> */} <Home /> </div> ) } } goHome = ()=> { this.props.history.push({ pathname:'/home', state:{ id: this.props.match.params.id } }) }
backhome.js :
import React,{Component} from 'react' import {withRouter} from 'react-router-dom' class BackHome extends Component { handleClick = () => { this.props.history.push({ pathname: '/home', state:{ id: this.props.match.params.id } }) } render(){ return ( <button onClick={this.handleClick}>返回首頁</button> ) } } export default withRouter(BackHome)
<button onclick="boldFont()">加粗<button> <button onclick="italicFont()">傾斜<button> <button onclick="redFont()">字體紅色<button> <div contenteditable style="border:1px solid #dedede; width:400px; min-height:300px;"> <img src="http://img5.imgtn.bding.com/it/u=394753335xxxxxxx&gp=0.jpg"> </div> <!-- 老瀏覽器用<iframe designmode="on" /> --> <script> var boldFont = function(){ document.execCommand('bold') } var italicFont = function(){ document.execCommand('italic') } var redFont = function(){ document.execCommand('foreColor',null,'#f00') } </script>
一、canvas 位圖 二、 svg 矢量圖 三、 三維 webgl √四、echart 五、highcharts 六、d3 dataV antV egret 遊戲 rapheal.js等
import {cloneDeep} from 'lodash' const state = { name:'可經過assign方法淺拷貝的基本數據類型的數據,拷貝後的數據改變不影響源數據中該項的數據', items:['經過淺拷貝後實際是拷貝過去的該複雜數據的地址','拷貝後的數據中該複雜數據項改變的話,源數據跟着變化'] } // const newState = JSON.parse(JSON.stringify(state)) //該深拷貝方式不能拷貝方法 const newState = cloneDeep(state) console.log(newState === state)
import {Map} from 'immutable' const state={ name:'map', value:['對象','方法','數組'] } const imState = Map(state) //比較兩個數據結構的不一樣 console.log(state,imState) //改變後必需要接收新的map對象 const newImState = imState.set('name','複雜數據做爲鍵') //map數據只能經過get獲取 console.log(imState.get('name'),newImState.get('name')) const map1 = Map({a:1,b:2}); const map2 = Map({a:1,b:2}); const map3 = map1.set('b',2); const mapCopy = map1; map1.equals(map2); // true is(map1,map2); // true map1 === map2; // false map1 === map3; // true
import {List} from 'immutable' const list1 = List([1,2]); const list2 = list1.push(3,3,4); console.log(list1.get(4),list2.get(4)) // undefined 3
import {fromJS,toJS} from 'immutable' const state = { name: 'qf', courses:['h5','java','python'], obj:{ x:1, y:{ z:1 } } } //複雜的JS數據類型轉換爲immutable,很經常使用 const imState = fromJS(state) console.log(imState.get('courses').get(0)) console.log(imState.getIn(['courses',0])) console.log(imState.getIn(['obj','x','y'])) const newImState = imState.setIn(['obj','y','z'],100) const newImState = imState.updateIn(['obj','y','z'],v=>v+1) const jsState = newImState.toJS().obj.y.z
使用 immutable、react-immutable改造redux中reducer的state數據類型:
一、 `npm i immutable redux-immutable -S` 二、reducers文件夾counter.js中修改 import {combineReducers} from 'redux' ---> import {combineReducers} from 'redux-immutable' 三、reducers文件夾counter.js中修改 const initState = { count:10 } ---> import { fromJS} from 'immutable' const initState = fromJS({ count:10 }) 四、reducers文件夾counter.js中修改 case 'INCREAMENT': return { ...state, count:state.count+1 } ---> case 'INCREAMENT': return state.updateIn(['count'],v=>v+1) case 'DECREAMENT': return { ...state, count:state.count-1 } ---> case 'DECREAMENT': return state.update('count',v=>v-1) 五、在調用conncect的組件中修改 const mapStateToProps = state =>{ return { count:state.counter.count ---> count: state.getIn(['counter','count']) } }
一、npm i mobx mobx-react -S
二、store.js:
import {observable,computed,action} from 'mobx' class Counter { name = 'Counter App' @observable count = 100 @computed get doubleCount(){ return this.count*2 } @action.bound increment(){ this.count += 1 } } const counterState = new Counter() export default counterStore
index.js:
import React from 'react' import ReactDom from 'react-dom' import {Provider} from 'mobx-react' import App from '/App' import couterStore from './store' ReactDom.render( <Provider counter={counterStore}> <App /> </Provider>, document.getElementById('root') )
App.js:
import React,{Component} from 'react' import {inject,observer} from 'mobx-react' @inject('counter') @observer export default class App extends Component{ render(){ return ( <div> <CounterDisplay counter={this.props.counter} /> <button onClick={this.props.counter.increment}>+</button> </div> ) } } @inject((store)=>{ return { count:store.counter.count, doubleCount:store.counter.doubleCount } }) @observer class CounterDisplay extends Component{ render(){ return ( <span>原值:{this.props.count}</span> <span>二倍值:{this.props.doubleCount}</span> ) } }