React小技巧彙總

image.png

引言

使用 React.js 一段時間了,把使用過程遇到的小坑和小技巧記錄下來,但願可以幫助到其餘人。此文章是長篇大論你只有耐得住寂寞,禁得住誘惑纔會有所成長。javascript

image.png

1、工具篇

一、顯示 html

<div dangerouslySetInnerHTML={{ __html: LANG.auth_register_tips1 }}/>複製代碼

二、經常使用組件

  • axios(http請求模塊,可用於前端任何場景,很強大)
  • echarts-for-react(可視化圖表,別人基於react對echarts的封裝,足夠用了)
  • recharts(另外一個基於react封裝的圖表)
  • nprogress(頂部加載條,蠻好用)
  • react-draft-wysiwyg(別人基於react的富文本封裝,若是找到其餘更好的能夠替換)
  • react-draggable(拖拽模塊,找了個簡單版的)
  • screenfull(全屏插件)
  • photoswipe(圖片彈層查看插件,不依賴jQuery,仍是蠻好用)
  • animate.css(css動畫庫)
  • redux Web 應用是一個狀態機,視圖與狀態是一一對應的.全部的狀態,保存在一個對象裏面
  • redux-logger 日誌
  • Reselect 記憶組件
  • redux-thunk 爲了解決異步action的問題
  • redux-saga 爲了解決異步action的問題
  • react-router-redux 保持路由與應用狀態(state)同步
  • react-router-dom

三、react-devtools 調試工具

工具地址:github.com/facebook/re…css

全局安裝:html

yarn global add react-devtools複製代碼

配置:在package.json中配置上去:前端

"scripts": {
"devtools": "react-devtools"
}複製代碼

而後就能夠啓動了:yarn run devtoolsvue

image.png

2、組件通信篇

須要組件之進行通訊的幾種狀況

  • 父組件向子組件通訊
  • 子組件向父組件通訊
  • 跨級組件通訊
  • 沒有嵌套關係組件之間的通訊
  1. redux 架構
  2. 父組件向子組件 —— props
  3. 子組件向父組件 —— props.funciton 接收參數
  4. 利用事件機制

1. 父組件向子組件通訊

React數據流動是單向的,父組件向子組件通訊也是最多見的;父組件經過props向子組件傳遞須要的信息java

2. 子組件向父組件通訊

  • 利用回調函數
  • 利用自定義事件機制

子組件改變父組件的state

// 通常改變state值的一種方式
const { data } = this.state;
this.setState({ data: {...data, key: 1 } });
// 另一種能夠經過callback的方式改變state的值
this.setState(({ data }) => ({ data: {...data, key: 1 } }));
// 還能夠
this.setState((state, props) => {
return { counter: state.counter + props.step };
});複製代碼

3. 跨級組件通訊

  • 層層組件傳遞props
例如A組件和B組件之間要進行通訊,先找到A和B公共的父組件,A先向C組件通訊,C組件經過props和B組件通訊,此時C組件起的就是中間件的做用
  • 使用context
context是一個全局變量,像是一個大容器,在任何地方均可以訪問到,咱們能夠把要通訊的信息放在context上,而後在其餘組件中能夠隨意取到;可是React官方不建議使用大量context,儘管他能夠減小逐層傳遞,可是當組件結構複雜的時候,咱們並不知道context是從哪裏傳過來的;並且context是一個全局變量,全局變量正是致使應用走向混亂的罪魁禍首.

使用context

下面例子中的組件關係: ListItem是List的子組件,List是app的子組件react

ListItem.jswebpack

import React, { Component } from 'react';
import PropTypes from 'prop-types';
class ListItem extends Component {
    // 子組件聲明本身要使用context
    static contextTypes = {
        color: PropTypes.string,
    }
    static propTypes = {
        value: PropTypes.string,
    }
    render() {
        const { value } = this.props;
        return (
            <li style={{ background: this.context.color }}>
                <span>{value}</span>
            </li>
        );
    }
}
export default ListItem;複製代碼

List.jsios

import ListItem from './ListItem';
class List extends Component {
    // 父組件聲明本身支持context
    static childContextTypes = {
        color: PropTypes.string,
    }
    static propTypes = {
        list: PropTypes.array,
    }
    // 提供一個函數,用來返回相應的context對象
    getChildContext() {
        return {
            color: 'red',
        };
    }
    render() {
        const { list } = this.props;
        return (
            <div>
                <ul>
                    {
                        list.map((entry, index) =>
                            <ListItem key={`list-${index}`} value={entry.text} />,
                       )
                    }
                </ul>
            </div>
        );
    }
}
export default List;複製代碼

App.jsgit

import React, { Component } from 'react';
import List from './components/List';
const list = [
    {
        text: '題目一',
    },
    {
        text: '題目二',
    },
];
export default class App extends Component {
    render() {
        return (
            <div>
                <List
                    list={list}
                />
            </div>
        );
    }
}複製代碼

4. 沒有嵌套關係的組件通訊

  • 使用自定義事件機制
在componentDidMount事件中,若是組件掛載完成,再訂閱事件;在組件卸載的時候,在componentWillUnmount事件中取消事件的訂閱;以經常使用的發佈/訂閱模式舉例,借用Node.js Events模塊的瀏覽器版實現

使用自定義事件的方式

下面例子中的組件關係: List1和List2沒有任何嵌套關係,App是他們的父組件;

實現這樣一個功能: 點擊List2中的一個按鈕,改變List1中的信息顯示

首先須要項目中安裝events 包:

npm install events --save複製代碼

在src下新建一個util目錄裏面建一個events.js

import { EventEmitter } from 'events';
export default new EventEmitter();複製代碼

list1.js

import React, { Component } from 'react';
import emitter from '../util/events';
class List extends Component {
    constructor(props) {
        super(props);
        this.state = {
            message: 'List1',
        };
    }
    componentDidMount() {
        // 組件裝載完成之後聲明一個自定義事件
        this.eventEmitter = emitter.addListener('changeMessage', (message) => {
            this.setState({
                message,
            });
        });
    }
    componentWillUnmount() {
        emitter.removeListener(this.eventEmitter);
    }
    render() {
        return (
            <div>
                {this.state.message}
            </div>
        );
    }
}
export default List;複製代碼

List2.js

import React, { Component } from 'react';
import emitter from '../util/events';
class List2 extends Component {
    handleClick = (message) => {
        emitter.emit('changeMessage', message);
    };
    render() {
        return (
            <div>
                <button onClick={this.handleClick.bind(this, 'List2')}>點擊我改變List1組件中顯示信息</button>
            </div>
        );
    }
}複製代碼

APP.js

import React, { Component } from 'react';
import List1 from './components/List1';
import List2 from './components/List2';
export default class App extends Component {
    render() {
        return (
            <div>
                <List1 />
                <List2 />
            </div>
        );
    }
}複製代碼

自定義事件是典型的發佈訂閱模式,經過向事件對象上添加監聽器和觸發事件來實現組件之間的通訊

組件間通訊之onRef方法

組件間通訊除了props外還有onRef方法,不過React官方文檔建議不要過分依賴ref。本文使用onRef語境爲在表單錄入時提取公共組件,在提交時分別獲取表單信息。

下面demo中點擊父組件按鈕能夠獲取子組件所有信息,包括狀態和方法,能夠看下demo中控制檯打印。

// 父組件
class Parent extends React.Component {
  testRef=(ref)=>{
    this.child = ref
    console.log(ref) // -> 獲取整個Child元素
  }
  handleClick=()=>{
    alert(this.child.state.info) // -> 經過this.child能夠拿到child全部狀態和方法
  }
  render() {
    return <div>
      <Child onRef={this.testRef} />
      <button onClick={this.handleClick}>父組件按鈕</button>
    </div>
  }
}
// 子組件
class Child extends React.Component {
  constructor(props) {
    super(props)
    this.state = {
      info:'快點擊子組件按鈕哈哈哈'
    }
  }
  componentDidMount(){
    this.props.onRef(this)
    console.log(this) // ->將child傳遞給this.props.onRef()方法
  }
  handleChildClick=()=>{
    this.setState({info:'經過父組件按鈕獲取到子組件信息啦啦啦'})
  } 
  render(){
    return <button onClick={this.handleChildClick}>子組件按鈕</button>
  }
}複製代碼
原理:

當在子組件中調用onRef函數時,正在調用從父組件傳遞的函數。this.props.onRef(this)這裏的參數指向子組件自己,父組件接收該引用做爲第一個參數:onRef = {ref =>(this.child = ref)}而後它使用this.child保存引用。以後,能夠在父組件內訪問整個子組件實例,而且能夠調用子組件函數。

3、路由篇

使用React構建的單頁面應用,要想實現頁面間的跳轉,首先想到的就是使用路由。在React中,經常使用的有兩個包能夠實現這個需求,那就是react-router和react-router-dom。本文主要針對react-router-dom進行說明。

image.png

一、React Router中有三類組件:

  • router 組件(BrowserRouter,HashRouter)
  • route matching 組件(Route,Switch)
  • navigation 組件(Link)

基於React Router的web應用,根組件應該是一個router組件(BrowserRouterHashRouter)。 項目中,react-router-dom提供了和兩種路由。兩種路由都會建立一個history對象。若是咱們的應用有服務器響應web的請求,咱們一般使用<BrowserRouter>組件; 若是使用靜態文件服務器,則咱們應該使用<HashRouter>組件

二、HashRouter和BrowserRouter

其實就是路由的hash和history兩種模式(要是不瞭解這兩種模式之間的區別那就須要去惡補下啦)

而且這兩個組件是路由的容器,必須在最外層

// hash模式
ReactDom.render(
  <HashRouter>
        <Route path="/" component={Home}/>
    </HashRouter>
)
// history模式
ReactDom.render(
  <BrowserRouter>
        <Route path="/" component={Home}/>
    </BrowserRouter>
)複製代碼

下面說說HashRouter和BrowserRouter上的參數

  • basename 路由的基礎連接,用來部署到非根目錄下,好比你須要將項目部署到 www.xxxx.com/web 下,則設置basename="/web"
  • getUserConfirmation 用來攔截Prompt組件,而且決定是否跳轉
  • forceRefresh 用來設置是否強制瀏覽器總體刷新,默認值爲false
  • keLength 用來設置location.key的長度,默認是6,能夠自定義

三、Route

Route是路由的一個原材料,它是控制路徑對應顯示的組件

Route的參數

  • path 跳轉的路徑
  • component 對應路徑顯示的組件
  • render 能夠本身寫render函數返回具體的dom,而不須要去設置component
  • location 傳遞route對象,和當前的route對象對比,若是匹配則跳轉
  • exact 匹配規則,true的時候則精確匹配。
path url 是否開啓 匹配結果
/a /a/b false yes
/a /a/b true no
  • sensitive 是否區分path的大小寫
path url 是否開啓 匹配結果
/a /a true yes
/a /A true yes
  • strict 是否匹配後面的/
path url 是否開啓 匹配結果
/a /a/ true yes
/a /a/c true yes
/a /a true no

四、Router

低級路由,適用於任何路由組件,主要和redux深度集成,使用必須配合history對象

使用Router路由的目的是和狀態管理庫如redux中的history同步對接

<Router history={history}>
    ...
</Router>
複製代碼複製代碼

五、Link和NavLink

二者都是跳轉路由,NavLink的參數更多些

Link的api

  • to 有兩種寫法,表示跳轉到哪一個路由
  • 字符串寫法
<Link to="/a"/>
複製代碼複製代碼
  • 對象寫法
<Link to={{
  pathname: '/courses',
  search: '?sort=name',
  hash: '#the-hash',
  state: { fromDashboard: true }
}}/>
複製代碼複製代碼
  • replace 就是將push改爲replace
  • innerRef 訪問Link標籤的dom

NavLink的api

  • Link的全部api
  • activeClassName 路由激活的時候設置的類名
  • activeStyle 路由激活設置的樣式
  • exact 參考Route,符合這個條件纔會激活active類
  • strict 參考Route,符合這個條件纔會激活active類
  • isActive 接收一個回調函數,active狀態變化的時候回觸發,返回false則中斷跳轉
const oddEvent = (match, location) => {
  console.log(match,location)
  if (!match) {
    return false
  }
  console.log(match.id)
  return true
}
<NavLink isActive={oddEvent} to="/a/123">組件一</NavLink>
複製代碼複製代碼
  • location 接收一個location對象,當url知足這個對象的條件纔會跳轉
<NavLink to="/a/123" location={{ key:"mb5wu3", pathname:"/a/123" }}/>
複製代碼複製代碼

六、Redirect

Redirect重定向很簡單,咱們直接看代碼便可

// 基本的重定向
<Redirect to="/somewhere/else" />
// 對象形式
<Redirect
  to={{
    pathname: "/login",
    search: "?utm=your+face",
    state: { referrer: currentLocation }
  }}
/>
// 採用push生成新的記錄
<Redirect push to="/somewhere/else" />
// 配合Switch組件使用,form表示重定向以前的路徑,若是匹配則重定向,不匹配則不重定向
<Switch>
  <Redirect from='/old-path' to='/new-path'/>
  <Route path='/new-path' component={Place}/>
</Switch>
複製代碼複製代碼

七、Switch

路由切換,只會匹配第一個路由,能夠想象成tab欄

Switch內部只能包含Route、Redirect、Router

<Switch>
  <Route exact path="/" component={Home}/>
  <Route path="/about" component={About}/>
  <Route path="/:user" component={User}/>
  <Route component={NoMatch}/>
</Switch>
複製代碼複製代碼

八、withRouter

當一個非路由組件也想訪問到當前路由的match,location,history對象,那麼withRouter將是一個很是好的選擇,能夠理解爲將一個組件包裹成路由組件

import { withRouter } from 'react-router-dom'
const MyComponent = (props) => {
    const { match, location, history } = this.props
     return (
        <div>{props.location.pathname}</div>
    )
}
const FirstTest = withRouter(MyComponent);
複製代碼複製代碼

九、history對象

用過vue的都知道,vue-router有組件形式的導航,也有編程式導航,那react-router怎麼使用api來控制前進後退和刷新呢?這就須要咱們來講明下history對象的做用了

其實在每一個路由組件中咱們可使用this.props.history獲取到history對象,也可使用withRouter包裹組件獲取,

在history中封裝了push,replace,go等方法,具體內容以下

History {
    length: number;
    action: Action;
    location: Location;
    push(path: Path, state?: LocationState): void; // 調用push前進到一個地址,能夠接受一個state對象,就是自定義的路由數據
    push(location: LocationDescriptorObject): void; // 接受一個location的描述對象
    replace(path: Path, state?: LocationState): void; // 用頁面替換當前的路徑,不可再goBack
    replace(location: LocationDescriptorObject): void; // 同上
    go(n: number): void; // 往前走多少也頁面
    goBack(): void; // 返回一個頁面
    goForward(): void; // 前進一個頁面
    block(prompt?: boolean | string | TransitionPromptHook): UnregisterCallback;
    listen(listener: LocationListener): UnregisterCallback;
    createHref(location: LocationDescriptorObject): Href;
}
複製代碼複製代碼

這樣咱們想使用api來操做前進後退就能夠調用history中的方法啦

其次也可經過暗轉history庫來實現,具體案例以下

import { BrowserRouter } from 'react-router-dom';
const history = require('history').createBrowserHistory();
/**
 * forceRefresh: bool
 * 做用:當瀏覽器不支持 HTML5 的 history API 時強制刷新頁面。
 */
const supportsHistory = 'pushState' in window.history;
 <BrowserRouter
        history={history}
        basename="/"
        forceRefresh={!supportsHistory}
      >
        {/* 路由入口 */}
       ......
</BrowserRouter>複製代碼

十、路由鑑權

4、性能篇

寫了一段時間的react以後,漸漸的喜歡上了使用react來寫應用。

咱們知道,Facebook在推出react時打出的旗號之一就是高性能。

今天咱們還一塊兒來聊一聊react的性能優化,思考還能經過哪些手段來提高React的性能,使咱們的react更快,性能更好。

一、react組件的性能優化(渲染角度優化)

一、react性能查看工具

再講性能優化以前,咱們須要先來了解一下如何查看react加載組件時所耗費的時間的工具,在react 16版本以前咱們可使用React Perf來查看。

react16版本以前,咱們可使用react-addons-perf工具來查看,而在最新的16版本,咱們只須要在url後加上?react_pref。

首先來了解一下react-addons-perf

react-addons-perf這是 React 官方推出的一個性能工具包,能夠打印出組件渲染的時間、次數、浪費時間等。

簡單說幾個api,具體用法可參考官網

  • Perf.start() 開始記錄
  • Perf.stop() 結束記錄
  • Perf.printInclusive() 查看全部設計到的組件render
  • Perf.printWasted() 查看不須要的浪費組件render

你們能夠在chorme中先安裝React Perf擴展,而後在入口文件或者reduxstore.js中加入相應的代碼便可:

image.png

再來了解一下,在最新的React16版本中,在url後加上?react_pref,就能夠在chrome瀏覽器的performance,咱們能夠查看User Timeing來查看組件的加載時間。點擊record開始記錄,注意記錄時長不要超過20s,不然可能致使chrome掛起。

image.png

使用此工具的具體操做你們能夠看下圖:

image.png

二、單個react組件性能優化

一、render裏面儘可能減小新建變量和bind函數,傳遞參數是儘可能減小傳遞參數的數量。

首先咱們先思考一個問題,好比我要實現一個點擊按鈕使相應的num增長1,咱們有哪一些方法。

你們應該都能想到,無非就是三種,以下圖:

image.png


第一種是在構造函數中綁定this,第二種是在render()函數裏面綁定this,第三種就是使用箭頭函數,都能實現上述方法;

可是哪種方法的性能最好,是咱們要考慮的問題。應該你們都知道答案:第一種的性能最好

由於第一種,構造函數每一次渲染的時候只會執行一遍;

而第二種方法,在每次render()的時候都會從新執行一遍函數;

第三種方法的話,每一次render()的時候,都會生成一個新的箭頭函數,即便兩個箭頭函數的內容是同樣的。

第三種方法咱們能夠舉一個例子,由於react判斷是否須要進行render淺層比較,簡單來講就是經過===來判斷的,若是state或者prop的類型是字符串或者數字,只要值相同,那麼淺層比較就會認爲其相同;

可是若是前者的類型是複雜的對象的時候,咱們知道對象是引用類型,淺層比較只會認爲這兩個prop是否是同一個引用,若是不是,哪怕這兩個對象中的內容徹底同樣,也會被認爲是兩個不一樣的prop

舉個例子:

當咱們給組件Foo給名爲styleprop賦值;

<Foo style={{ color:"red" }}複製代碼

使用這種方法,每一次渲染都會被認爲是一個style這個prop發生了變化,由於每一次都會產生一個對象給style

那麼咱們應該如何改進,若是想要讓react渲染的時候認爲先後對象類型prop相同,則必需要保證prop指向同一個javascript對象,以下:

const fooStyle = { color: "red" }; //取保這個初始化只執行一次,不要放在render中,能夠放在構造函數中

<Foo style={fooStyle} />複製代碼

二、定製shouldComponentUpdate函數

shouldComponentUpdate是決定react組件何時可以不從新渲染的函數,可是這個函數默認的實現方式就是簡單的返回一個true。也就是說,默認每次更新的時候都要調用所用的生命週期函數,包括render函數,從新渲染。

咱們來看一下下面的一個例子

image.png

咱們寫兩個組件,AppDemo組件,並寫兩個方法,一個改變App中的num的值,一個是改變title,咱們在Demo的render中打印render函數。咱們能夠看到如下的效果:

image.png

咱們能夠清晰的看到雖然demo組件裏的title值沒有改變,可是仍是render了。

爲了解決這個問題,咱們能夠對demo組件進行以下的修改:

image.png

只有當demo的title值發生改變的時候,咱們纔去render,咱們能夠看一下效果:

image.png

以上只是一個特別簡單的一個對於shouldComponentUpdate的定製。

在最新的react中,react給咱們提供了React.PureComponent,官方也在早期提供了名爲react-addons-pure-render-mixin插件來從新實現shouldComponentUpdate生命週期方法。

image.png

經過上述的方法的效果也是和咱們定製shouldComponentUpdate的效果是一致的。

可是咱們要注意的是,這裏的PureRender是淺比較的,由於深比較的場景是至關昂貴的。因此咱們要注意咱們在1.1中說到的一些注意點:不要直接爲props設置對象或者數組不要將方法直接綁定在元素上,由於其實函數也是對象

三、多個react組件性能優化,key的優化

react組件在裝載過程當中,react經過在render方法在內存中產生一個樹形結構,樹上的節點表明一個react組件或者原生的Dom元素,這個樹形結構就是咱們所謂的Vitural Dom,react根據這個來渲染產生瀏覽器的Dom樹。

react在更新階段對比原有的Vitural Dom和新生成的Vitural Dom,找出不一樣之處,在根據不一樣來渲染Dom樹。

react爲了追求高性能,採用了時間複雜度爲O(N)來比較兩個屬性結構的區別,由於要確切比較兩個樹形結構,須要經過O(N^3),這會下降性能。

簡單來講,react利用key來識別組件,它是一種身份標識標識,就像咱們的身份證用來辨識一我的同樣。

列表的 key

  • key 不會傳給組件,若是須要使用 key 的值,另外命名爲其餘屬性傳進去
  • 當一個列表順序會重排時,不適合使用數組的索引做爲 key

*注意:另外有個方式:推薦使用shortid生成惟一key的數組,和數據數組一塊兒使用,省去提交數據時再重組數組。

案例:

import React from 'react';
import shortid from 'shortid';

class Demo extends React.Component {
    constructor(props) {
    super(props);
    this.state = {
      data: ['a', 'b', 'c']
    }
    this.dataKeys = this.state.data.map(v => shortid.generate());
  }
  
    deleteOne = index => { // 刪除操做
        const { data } = this.state;
        this.setState({ data: data.filter((v, i) => i !== index) });
        this.dataKyes.splice(index, 1);
    }
    
    render() {
      return (
          <ul>
               {
                   data.map((v, i) => 
                    <li 
                        onClick={i => this.deleteOne(i)}  
                        key={this.dataKeys[i]}
                    >
                        {v}
                    </li>
                    )
               } 
            </ul>
      )
  }
}
// 稍微抽取,能夠封裝一個通用的組件複製代碼

另外須要指明的是:

key不是用來提高react的性能的,不過用好key對性能是有幫組的。

不能使用random來使用key

key相同,若組件屬性有所變化,則react只更新組件對應的屬性;沒有變化則不更新。

key值不一樣,則react先銷燬該組件(有狀態組件的componentWillUnmount會執行),而後從新建立該組件(有狀態組件的constructorcomponentWillUnmount都會執行)

咱們舉幾個狀況,你們就會立刻理解:

  • 節點類型不一樣
// A組件

<div>
  <Todos />
</div>

// B組件
<span>
  <Todos />
</span>複製代碼

咱們想把A組件更新成B組件,react在作比較的時候,發現最外面的根結點不同,直接就廢掉了以前的<div>節點,包括裏面的子節點,這是一個巨大的浪費,可是爲了不O(N^3)的時間複雜度,只能採用這種方式
因此在開發過程當中,咱們應該儘可能避免上面的狀況,不要將包裹節點的類型隨意改變。

  • 兩個節點類型同樣

這裏包括兩種狀況,一種是節點是Dom類型,還有一種react組件。

對於dom類型,咱們舉個例子:

// A組件
<div style={{color: 'red',fontSize:15}} className="welcome">
  Hello World!!!
</div>

// B組件
<div style={{color: 'green',fontSize:15}} className="react">
  Good Bye!!!
</div>複製代碼

上述A和B組件的區別是文字、classNamestyle中的color發生改變,由於Dom元素沒變,React只會修改他變化的部分。

針對react組件類型,渲染無非就是在走一遍組件實例的更新過程,最主要的就是定製shouldComponentUpdate,咱們上面也有講到,就不細講了。

  • 多個子組件狀況

咱們看兩個例子就能明白

例子一:

// A
<ul>
  <TodoItem text="First" complete={false} />
  <TodoItem text="Second" complete={false} />
</ul>

// B
<ul>
  <TodoItem text="First" complete={false} />
  <TodoItem text="Second" complete={false} />
  <TodoItem text="Third" complete={false} />
</ul>複製代碼

從A變到B,若是shouldComponentUpdate處理得當,咱們只須要更新裝載third的那一次就行。

咱們來看看下一個例子:

// A
<ul>
  <TodoItem text="First" complete={false} />
  <TodoItem text="Second" complete={false} />
</ul>

// B
<ul>
  <TodoItem text="Zero" complete={false} />
  <TodoItem text="First" complete={false} />
  <TodoItem text="Second" complete={false} />
</ul>複製代碼

這裏由於react是採用O(n)的時間複雜度,因此會依次將text爲First的改成Zero,text爲Second改成First,在最後再加上一個組件,text爲Second。現存的兩個的text的屬性都被改變了,因此會依次渲染。

若是咱們這裏有1000個實例,那麼就會發生1000次更新。

這裏咱們就要用到Key

簡單來講,其實這一個Key就是react組件的身份證號。

咱們將上一個例子改爲以下,就能夠避免上面的問題了,react就可以知道其實B裏面的第二個和第三個組件其實就是A中的第一個和第二個實例。

// A
<ul>
  <TodoItem key={1} text="First" complete={false} />
  <TodoItem key={2} text="Second" complete={false} />
</ul>

// B
<ul>
  <TodoItem key={0} text="Zero" complete={false} />
  <TodoItem key={1} text="First" complete={false} />
  <TodoItem key={2} text="Second" complete={false} />
</ul>複製代碼

不過如今,react也會提醒咱們不要忘記使用key,若是沒有加,在瀏覽器中會報錯。

image.png關於key的使用咱們要注意的是,這個key值要穩定不變的,就如同身份證號之於咱們是穩定不變的同樣。

一個常見的錯誤就是,拿數組的的下標值去當作key,這個是很危險的,代碼以下,咱們必定要避免。

<ul>
  {
        todos.map((item, index) => {
            <TodoItem
              key={index}
              text={item.text}
              completed={item.completed}
        })
  }
</ul>複製代碼

二、redux性能優化:reselect(數據獲取時優化)

mapStateToProps也被叫作selector,在store發生變化的時候就會被調用,而無論是否是selector關心的數據發生改變它都會被調用,因此若是selector計算量很是大,每次更新都從新計算可能會帶來性能問題。

Reselect能幫你省去這些不必的從新計算。

Reselect 提供 createSelector 函數來建立可記憶的 selector。

createSelector 接收一個 input-selectors 數組和一個轉換函數做爲參數。

若是 state tree 的改變會引發 input-selector 值變化,那麼 selector 會調用轉換函數,傳入 input-selectors 做爲參數,並返回結果。

若是 input-selectors 的值和前一次的同樣,它將會直接返回前一次計算的數據,而不會再調用一次轉換函數。

這樣就能夠避免沒必要要的計算,爲性能帶來提高。

例子:

import {} from 'reselect';
export const getItems = (state) => state.cart.get('items');
export const getItemsWithTotals = createSelector(
[ getItems ],
(item) => {
 return items.map(i =>{
  return i.set('total', i.get('price', 0) * i.get('quantity');
 });
}
)複製代碼

建立一個記憶性的selector.這個意思是getItemWithTotals在第一次函數運行的時候將會進行運算.

若是同一個函數再次被調用,可是輸入值(getItems的值)沒有變化,函數將會返回一個緩存(cached)的計算結果.

若是items被修改了(例如:item添加,數量的變化,任何事情操做了getItems的結果),函數將會再次執行.

在前面的優化過程當中,咱們都是優化渲染來提升性能的,既然reactredux都是經過數據驅動的的方式驅動渲染過程,那麼處理優化渲染過程,獲取數據的過程也是須要考慮的一個優化點。

//下面是redux中簡單的一個篩選功能
const getVisibleTodos = (todos, filter) => {
  switch (filter) {
    case 'SHOW_ALL':
      return todos
    case 'SHOW_COMPLETED':
      return todos.filter(t => t.completed)
    case 'SHOW_ACTIVE':
      return todos.filter(t => !t.completed)
  }
}

const mapStateToProps = (state) => {
  return {
    todos: getVisibleTodos(state.todos, state.visibilityFilter)
  }
}複製代碼

mapStateToProps函數做爲redux store中獲取數據的重要一環,當咱們根據filtertodos來顯示相應的待辦事項的時候,咱們都要遍歷todos字段上的數組。

當數組比較大的時候,則會下降性能。

這個時候,reselect就應運而生了,它的動做原理:只要相關的狀態沒有改變,那麼就直接使用上一次的緩存結果。

具體的用法我就不在這裏過多介紹了,已經有不少的牛人寫了相關的文章,我也不重複寫了,你們若是想深刻了解的話,能夠參考reselect的giuhubRedux的中間件-Reselect

三、分隔代碼

一、動態加載

ES6標準引入了import以方便咱們靜態加載模塊。形式如:

import xxx from xxx.js複製代碼

儘管import對於咱們加載模塊頗有幫助,可是靜態加載模塊的方式必定程度上限制了咱們來實現異步模塊加載。不過,目前動態加載模塊的import()語法已處於提案階段,而且webpack已將他引入並使用。import()提供了基於Promise的API,所以,import()的返回值是一個完成狀態或拒絕狀態的Promise對象。形式如:

import(/* webpackChunkName: 'module'*/ "module")
    .then(() => {
        //todo
    })
    .catch(_ => console.log('It is an error'))複製代碼

webpack在編譯時,識別到動態加載的import語法,則webpack會爲當前動態加載的模塊建立一個單獨的bundle。若是你使用的是官方的Create-react-app腳手架或React的服務端渲染框架Next.js,那麼能夠直接使用動態import語法。若是你的腳手架是你本身配置的webpack,那麼你須要按照官方指南來設置,請移步[1]。

二、動態加載React組件

當前最爲流行的一種方法是使用 React-loadable [2]庫提供的懶加載React組件。它利用import()語法,使用Promise語法加載React組件。同時,React-loadable支持React的服務端渲染。 一般,咱們以以下方式實現組件:

import LazyComponet from 'LazyComponent';
export default function DemoComponent() {
    return (
        <div>
            <p>demo component</p>
            <AComponent />
        </div>
    )
}複製代碼

在上面的例子中,假設 LazyComponetDemoComponent 渲染時咱們並不展現。可是由於咱們使用import語法將 LazyComponet 導入,因此在編譯時會將 LazyComponet 的代碼與 DemoComponent 的代碼打包到同一個bundle裏面。 可是,這並非咱們想要的。因此咱們能夠經過使用 React-loadable 來懶加載 LazyComponet ,同時將 LazyComponet 的代碼單獨打包到一個bundle裏面。咱們能夠看一下官網提供的例子:

import Loadable from 'react-loadable';
import Loading from './my-loading-component';
const LoadableComponent = Loadable({
  loader: () => import('./my-component'),
  loading: Loading,
});
export default class App extends React.Component {
  render() {
    return <LoadableComponent/>;
  }
}複製代碼

從例子中咱們能夠看到,react-loadable使用動態import()方法,並將導入的組件分配給loader屬性。同時,react-loadable提供了一個loading屬性,以設置在加載組件時將展現的組件。

三、lazy和suspense的使用

React.lazy and Suspense is not yet available for server-side rendering. If you want to do code-splitting in a server rendered app, we recommend Loadable Components. It has a nice guide for bundle splitting with server-side rendering.


在使用以前,咱們須要特別注意的一點是,React官方明確支持,React.lazy和Suspense並不支持服務端渲染。所以,使用服務端渲染的同窗,請繞行至 react-loadableloadable-components [3]。

因爲我是對原有項目進行的升級,所以,本文如下內容主要針對於老項目升級React最新版所作的工做。

一、代碼升級React最新版

若是你的代碼是Reactv16,那麼能夠直接升級到最新版本,固然React16.6已經提供了lazy和suspense方法。若是是v16以前,則按照官方操做遷移。

二、肯定原有代碼的懶加載組件

首先,按照需求,將非首屏加載的組件肯定爲懶加載組件,個人項目共肯定五個組件能夠進行懶加載。修改方式很簡單,原有引入組件的方法爲:

import LazyComponent from "../components/LazyComponent ";複製代碼

修改成:

const LazyComponent = React.lazy(() =>
  import(/* webpackChunkName: 'lazyComponent'*/ "../components/LazyComponent")
);複製代碼

如代碼所示:將靜態引用組件的代碼替換爲調用React.lazy(),在lazy()傳入一個匿名函數做爲參數,在函數中動態引入 lazyComponent 組件。這樣在咱們渲染這個組件前,瀏覽器將不會下載 lazyComponent.bundle.js 文件和它的依賴。 其中,import內的webpackChunkName爲咱們定義的bundle文件名。

若是React要渲染組件時,組件依賴的代碼還沒下載好,會出現什麼狀況? <React.Suspense/>的出現幫咱們解決問題。在代碼未下載好前,它將會渲染fallback屬性傳入的值。所以咱們的原有代碼爲:

return (
        <div>
            <MainComponet />
            <LazyComponent />
        </div>
    )複製代碼

修改成:

return (
        <div>
            <MainComponet />
            <React.Suspense fallback={<div>正在加載中</div>}>
                <LazyComponent />
            </React.Suspense>
        </div>
    )複製代碼

fallback中能夠修改成任意的spinner,本次不作過多優化。假如你不使用React.suspense,則React會給出你錯誤提示,所以記得React.lazy和React.Suspense搭配使用。 此時咱們能夠從網絡請求中看到,動態加載的lazyComponet組件被單獨打包到一個bundle裏面,然而,在首屏加載的時候,該bundle已經加載到咱們的頁面中了,這也許並非咱們想要的,咱們想要的是當咱們須要的時候再加載。接下來咱們就控制一下,當咱們須要的時候,再加載該文件。

三、經過變量控制加載

本來我選擇的五個懶加載組件均屬於彈層性質的組件,所以必然會設置一個state來控制該組件的顯示與隱藏,所以咱們將代碼改成:

return (
        <div>
            <MainComponet />
            {this.state.showLazyComponent && (
                <React.Suspense fallback={<div>正在加載中</div>}>
                    <LazyComponent />
                </React.Suspense>
            )}
        </div>
    )複製代碼

由此,在首屏加載時,並未加載咱們的懶加載組件 LazyComponent 所對應的bundle包。等到咱們點擊須要該組件顯示時,頁面纔去加載該js。這便達到了咱們代碼分離並懶加載的目的。那麼咱們這麼操做,到底主bundle包的體積減小了嗎?接下來咱們打包文件看一下。

四、打包文件

優化前打包出來的文件:

image.png

優化後打包出來的文件:

image.png

app.js 文件變小,隨之增長 lazyComponent.js 。當懶加載組件多時,咱們即可必定程度上減小首屏加載文件的大小,提升首屏的渲染速度。本實驗僅僅抽取一個組件做爲示例,若是懶加載的數量較多,足以明顯減少app.js的體積。

總結——>驗證優化的有效性

五、[驗證優化的時效性] 利用Puppeteer和Performance API作對比

爲了驗證前面我所作的優化的有效性,我作了一組對比實驗。實驗內容爲使用puppeteer分別訪問優化前和優化後的頁面1000次,使用Performance API分別統計五項數據在這1000次訪問時的平均值。 實驗結果以下圖所示,其中:

  • A爲request請求平均耗時
  • B爲解析dom樹耗時平均耗時
  • C爲請求完畢至DOM加載平均耗時
  • D爲請求開始到domContentLoadedEvent結束平均耗時
  • E爲請求開始至load平均耗時

image.png


折線圖沒法準確展現數據,所以,附表格數據以下(均爲平均耗時):

類別 優化後 優化前

A(request請求)

7.01 7.04

B(解析dom樹平均)

30.28 32.59

C(請求完畢至DOM加載)

552.86 582.0

D(請求開始到domContentLoadedEvent結束)

569.13 589.0

E(請求開始至load結束)

1055.59 1126.94

從數據中咱們能夠看出,優化先後請求時間並無什麼影響,可是整體load的時間明顯縮短並立刻進入1000ms大關,可見優化後對於首屏的加載速度仍是有明顯提高。

注:因puppeteer運行1000次的過程當中,會出現網絡波動的狀況,致使有些請求的數據偏大,所以平均值並不能徹底體現正常的請求時間。但1000次的平均值足以進行優化先後的請求時間對比。

六、[驗證優化的時效性] 利用Chorme Performance 參數作對比

由於Performance API提供的參數有限,所以我從Chrome瀏覽器的performance summary中拿到了單次頁面請求時的參數。由於是單次數據,所以咱們不進行詳細的對比。在此列出,只爲說明優化先後瀏覽器渲染時間上哪些部分有提高。 優化前: 優化後:

image.png image.png

  • 藍色:加載(Loading)時間下降
  • 黃色:腳本運算(Scripting)時間下降
  • 紫色:渲染(Rendering)時間下降
  • 綠色:繪製(Painting)時間持平
  • 灰色:其餘(Other)時間下降
  • 閒置:瀏覽器空閒時間下降

另外,我從Network中發現,優化後由於頁面解析的相對以前較快,所以主接口的請求時間也相應的提早了一些。

四、總結

從多項數據代表, React.lazyReact.Suspense 的使用必定程度上加快了首屏的渲染速度,使得咱們的頁面加載更快。 另外,當咱們想添加一個新功能而引入一個新依賴時,咱們每每會評估該依賴的大小以及引入該依賴會對原有bundle形成多大影響。假如該功能不多被用到,那麼咱們能夠痛快地使用 React.lazyReact.Suspense 來按需加載該功能,而無需犧牲用戶體驗了。

四、使用React.memo()來優化函數組件的性能

eact16.6加入的另一個專門用來優化 函數組件(Functional Component)性能的方法: React.memo

一、無用的渲染

組件是構成React視圖的一個基本單元。有些組件會有本身本地的狀態(state), 當它們的值因爲用戶的操做而發生改變時,組件就會從新渲染。在一個React應用中,一個組件可能會被頻繁地進行渲染。這些渲染雖然有一小部分是必須的,不過大多數都是無用的,它們的存在會大大下降咱們應用的性能。
看下面這個例子:

import React from 'react';

class TestC extends React.Component {
    constructor(props) {
        super(props);
        this.state = {
            count: 0
        }
    }
    
    componentWillUpdate(nextProps, nextState) {
        console.log('componentWillUpdate')
    }
    
    componentDidUpdate(prevProps, prevState) {
        console.log('componentDidUpdate')
        
    }
    
    render() {
        return (
            <div >
            {this.state.count}
            <button onClick={()=>this.setState({count: 1})}>Click Me</button>
            </div>
        );
    }
}
export default TestC;

複製代碼

TestC組件有一個本地狀態count,它的初始值是0(state = {count: 0})。當咱們點擊Click Me按鈕時,count的值被設置爲1。這時候屏幕的數字將會由0變成1。當咱們再次點擊該按鈕時,count的值仍是1, 這時候TestC組件不該該被從新渲染,但是現實是這樣的嗎?

爲了測試count重複設置相同的值組件會不會被從新渲染, 我爲TestC組件添加了兩個生命週期函數: componentWillUpdate和componentDidUpdate。componentWillUpdate方法在組件將要被從新渲染時被調用,而componentDidUpdate方法會在組件成功重渲染後被調用。

在瀏覽器中運行咱們的代碼,而後屢次點擊Click Me按鈕,你能夠看到如下輸出:


咱們能夠看到'componentWillUpdate'和'componentWillUpdate'在每次咱們點擊完按鈕後,都會在控制檯輸出來。因此即便count被設置相同的值,TestC組件仍是會被從新渲染,這些就是所謂的無用渲染。

二、函數組件

上面咱們探討了如何使用PureComponentshouldComponentUpdate的方法優化類組件的性能。雖然類組件是React應用的主要組成部分,不過函數組件(Functional Component)一樣能夠被做爲React組件使用。

function TestC(props) {
    return (
        <div>
            I am a functional component
        </div>
    )
}
複製代碼

對於函數組件,它們沒有諸如state的東西去保存它們本地的狀態(雖然在React Hooks中函數組件可使用useState去使用狀態), 因此咱們不能像在類組件中使用shouldComponentUpdate等生命函數去控制函數組件的重渲染。固然,咱們也不能使用extends React.PureComponent了,由於它壓根就不是一個類。

要探討解決方案,讓咱們先驗證一下函數組件是否是也有和類組件同樣的無用渲染的問題。

首先咱們先將ES6的TestC類轉換爲一個函數組件:

import React from 'react';

const TestC = (props) => {
    console.log(`Rendering TestC :` props)
    return ( 
        <div>
            {props.count}
        </div>
    )
}
export default TestC;
// App.js
<TestC count={5} />

複製代碼

當上面的代碼初次加載時,控制檯的輸出是:


一樣,咱們能夠打開Chrome的調試工具,點擊React標籤而後選中TestC組件:


咱們能夠看到這個組件的參數值是5,讓咱們將這個值改成45, 這時候瀏覽器輸出


因爲count的值改變了,因此該組件也被從新渲染了,控制檯輸出Object{count: 45},讓咱們重複設置count的值爲45, 而後再看一下控制檯的輸出結果:

由輸出結果能夠看出,即便count的值保持不變,仍是45, 該組件仍是被重渲染了。

既然函數組件也有無用渲染的問題,咱們如何對其進行優化呢?

三、解決方案: 使用React.memo()

React.memo(...)是React v16.6引進來的新屬性。它的做用和React.PureComponent相似,是用來控制函數組件的從新渲染的。React.memo(...) 其實就是函數組件的React.PureComponent

如何使用React.memo(...)?

React.memo使用起來很是簡單,假設你有如下的函數組件:

const Funcomponent = ()=> {
    return (
        <div>
            Hiya!! I am a Funtional component
        </div>
    )
}
複製代碼

咱們只需將上面的Funcomponent做爲參數傳入React.memo中:

const Funcomponent = ()=> {
    return (
        <div>
            Hiya!! I am a Funtional component
        </div>
    )
}
const MemodFuncComponent = React.memo(FunComponent)
複製代碼

React.memo會返回一個純化(purified)的組件MemoFuncComponent,這個組件將會在JSX標記中渲染出來。當組件的參數props和狀態state發生改變時,React將會檢查前一個狀態和參數是否和下一個狀態和參數是否相同,若是相同,組件將不會被渲染,若是不一樣,組件將會被從新渲染。

如今讓咱們在TestC組件上使用React.memo進行優化:

let TestC = (props) => {
    console.log('Rendering TestC :', props)
    return ( 
        <div>
        { props.count }
        </>
    )
}
TestC = React.memo(TestC);
複製代碼

打開瀏覽器從新加載咱們的應用。而後打開Chrome調試工具,點擊React標籤,而後選中<Memo(TestC)>組件。

接着編輯一下props的值,將count改成89,咱們將會看到咱們的應用被從新渲染了:


而後重複設置count的值爲89:


這裏沒有從新渲染!

這就是React.memo(...)這個函數牛X的地方!

在咱們以前那個沒用到React.memo(...)的例子中,count的重複設置會使組件進行從新渲染。但是咱們用了React.memo後,該組件在傳入的值不變的前提下是不會被從新渲染的。

高階組件(HOC)

高階函數,能夠傳入函數做爲參數的函數,如 map,sort,reduce。高階組件包裝了另外一個組件的組件。

  1. 屬性代理 (Props Proxy)
  2. 反向繼承 (Inheritance Inversion)

Immutable.js

由於數據是不可變的,能夠避免引用傳值的問題,但也麻煩

無狀態組件

使用無狀態組件,只從父組件接收 props,能夠提升組件的渲染性能

const HelloWorld = (props) => <div>{props.name}</div>ReactDOM.render(<HelloWorld name="HelloWorld" />,App)複製代碼

componentWillReceiveProps 中取 props 的值

注意應該取 nextProps,而不是 this.props

bind 綁定函數

利用 bind 綁定函數,是默認有 event 這個參數的,只是這個參數在給定參數以後

handleClockClick (id, e) {console.log(id,e)}<button onClick={this.handleClockClick.bind(this, 2)}>Clock</button>複製代碼

ES6 類中,函數 this 不默認指向 對象

獲取元素

  • this.getDomNode 已經在低版本被移除了,如今設置 ref=xxx,而後使用 this.refs.xxx 訪問 DOM 元素
  • ref 能夠賦值兩種類型,一種是字符串,一種是函數, 字符串只能用在類組件,DOM 元素使用函數,純函數組件不能使用 ref。舊版本 DOM 元素雖然可使用 ref,可是 React 已不推薦。

ref="test" // this.refs.test 訪問ref={test => this.test = test} // this.test 訪問複製代碼

組合 VS 繼承

React 推薦使用組合,而不是繼承,組合在 UI 來的更加直觀,代碼看起來也比較容易,更符合咱們的認知,也符合 React component-base 的特性。

當只寫屬性名時,默認值爲 true

<MyComponent isStock/>// isStock 默認爲 true複製代碼

createPortal

  • 將子節點掛載到父組件之外組件的方法,在 render 方法中使用,不能掛載到父組件,由於此時父組件都沒有渲染好,沒法獲取到 DOM
  • 行爲上跟其餘子節點沒有區別,由於 React DOM 樹上依然在這個組件上,包括事件冒泡等東西
import { createPortal } from 'react-dom'createPortal(this.props.children, document.getElementById('portal-root'))複製代碼

錯誤邊界

componentDidCatch 錯誤邊界,爲組件定義一個父組件,父組件捕獲錯誤,並提供回退的 UI

  • 用法
componentDidCatch(error, info) {this.setState({ hasError: true });console.log(error, info)}複製代碼
  • 沒法捕獲的錯誤
    事件處理
    異步代碼 (例如 setTimeout 或 requestAnimationFrame 回調函數)
    服務端渲染
    錯誤邊界自身拋出來的錯誤 (而不是其子組件)

高級組件就是函數

  • 不該該在高階組件修改原組件的屬性
  • 利用函數包裹組件,返回一個新的組件
  • 爲組件切換不一樣的數據源
  1. Showcase 組件,利用 getData (Showcase, data)函數包裹,獲取不一樣數據
  • 不要在 render 中使用高階組件
    由於每一次掛載組件,都會從新獲取一個高階組件的實例
  • hoistNonReactStatic
    將原始組件的靜態方法拷貝到包裹組件中

容器組件

  • 處理數據訂閱和狀態管理
  • 高階組件是參數化的容器組件

rander prop

標題相同,利用高階組件把標題渲染到不一樣的組件

React 中使用 Web component

這個時候有一點要注意的是,對於 Web component 應該使用 class,而不是 className

lable 的 for

for 是 JS 的保留字,因此使用 htmlFor 替代 for

style屬性

瀏覽器後綴除了ms之外,都應該以大寫字母開頭。這就是爲何WebkitTransition有一個大寫字母W。

const divStyle = {WebkitTransition: 'all', // note the capital 'W' heremsTransition: 'all' // 'ms' is the only lowercase vendor prefix};複製代碼

在 IE11 如下使用 React16

React16 依賴集合類型 Map 和 Set,在未提供原生支持的瀏覽器,須要使用一個 polyfill,例如 core-js 和 babel-polyfill

使用 core-js 支持

import 'core-js/es6/map';import 'core-js/es6/set';import React from 'react';import ReactDOM from 'react-dom';ReactDOM.render(<h1>Hello, world!</h1>,document.getElementById('root'));複製代碼

componentDidMount 請求服務器數據

在 componentDidMount 請求服務器數據並利用 setState 時應注意,在組件卸載 componentWillUnmount 應該把去求去掉

使用es6新特性傳遞組件props

const {data, type} = this.state;
// 通常方法
<Demo data={data} type={type}/>
// es6方法
<Demo {...{data, type}}/>複製代碼

三、 利用es6 rest 參數(形式爲...變量名)傳遞可變數量的props

// 定義子組件
const Demo = ({ prop1, prop2, ...restProps }) => (
<div>{ restProps.text}</div>
)
// 父組件使用Demo
<Demo prop1={xxx} prop2={xxx} text={xxx}/>複製代碼

四、setState的其餘用法

// 通常改變state值的一種方式
const { data } = this.state;
this.setState({ data: {...data, key: 1 } });
// 另一種能夠經過callback的方式改變state的值
this.setState(({ data }) => ({ data: {...data, key: 1 } }));
// 還能夠
this.setState((state, props) => {
return { counter: state.counter + props.step };
});複製代碼

五、React 性能優化

// React 性能優化有不少種方式,
// 那常見的一種就是在生命週期函數shouldComponentUpdate裏面判斷
// 某些值或屬性來控制組件是否從新再次渲染。
// 判斷通常的字符串,數字或者基礎的對象,數組都仍是比較好處理
// 那嵌套的對象或者數組就比較麻煩了,對於這種
// 推薦使用lodash(或者其餘的相似庫)的isEqual對嵌套數組或對象進行判斷
shouldComponentUpdate(nextProps, nextState) {
if (_.isEqual(nextState.columns, this.state.columns)) return false;
return true;
}複製代碼

React 進階提升 - 技巧篇(28 個視頻)連接

介紹 React 的一些進階知識點,一些開發上的實踐技巧,一些工具庫等。

視頻更新地址:www.qiuzhi99.com/

React 進階提升 - 技巧篇

react 技巧 #1 如何用 netlify 雲服務部署 react 應用 65「07:14」

react 技巧 #2 把 react 應用部署到 GitHub Pages 18「05:34」

react 技巧 #3 react-router 教程 part 1 51「10:29」

react 技巧 #4 react-router 教程 part 2 11「07:39」

React 進階提升 #5 無狀態組件的最佳寫法 44「Pro」「04:52」

React 進階提升 #6 Fragment 14「Pro」「02:36」

React 進階提升 #7 context(上下文) 9「03:58」

React 進階提升 #8 高階組件 14「Pro」「02:51」

React 進階提升 #9 強大酷炫好玩的 web IDE 工具(鼠標點擊生成代碼,縮減 N 倍開發時間) 12「Pro」「08:20」

React 進階提升 #10 用高階組件來重構代碼 11「05:58」

React 進階提升 #11 我最愛的 React 庫 - 功能強大的可插入組件 (簡化代碼) 1「Pro」「04:30」

React 進階提升 #12 返回多個組件的正確方式 5「Pro」「03:07」

React 進階提升 #13 netlifyctl 一鍵部署前端應用 2「06:49」

React 進階提升 #14 defaultProps 和 類型檢查 PropTypes part 1 4「06:37」

React 進階提升 #15 類型檢查 PropTypes part 2「Pro」「09:57」

React 進階提升 #16 用 Render Props 代替 HOC(高階組件) 5「Pro」「」

React 進階提升 #17 錯誤邊界和生命週期函數 componentDidCatch 9「Pro」「11:45」

React 進階提升 #18 升級到 16.3「02:37」

React 進階提升 #19 探索 bind (this) 的寫法 9「03:50」

React 進階提升 #20 React 16.3 全新的 Context API 1「06:50」

React 進階提升 #21 React 16.3 全新的 Context API - 實踐 3「Pro」「09:19」

React 進階提升 #22 從 Redux 遷移到 React 16.3 的 Context API 之實踐 1「Pro」「11:37」

React 進階提升 #23 對象,數組,可變數據 9「Pro」「06:10」

React 進階提升 #24 React.Children API 和 props.children 講解 4「Pro」「06:06」

React 進階提升 #25 如何使用 styled-components 5「Pro」「04:56」

React 進階提升 #26 如何使用 styled-components(實踐篇)「Pro」「07:29」

React 進階提升 #27 你應該使用 redux-form(介紹) 12「Pro」「06:40」

React 進階提升 #28 你應該使用 redux-form(實踐篇) 7「Pro」「10:34」

學習資料

資料

doc.react-china.org/ 翻譯後的官方文檔,學技術必定要多看幾遍文檔

React小書 強烈推薦,由淺入深,按部就班

reactpatterns.com/ 因爲react自己 API 比較簡單,貼近原生。經過組件變化產生一系列模式

github.com/CompuIves/c… react在線編輯器,方便的分享你的react項目

image.png

devhints.io/react

image.png

js.coach 找js包的網站

image.png

視頻

基礎的免費,高級的收費 egghead.io

其餘資料

React項目國際化(antd)多語言開發

使用 husky、commitlint 和 lint-staged 來構建你的前端工做流(vue、react、dva)

歡迎你們轉發交流分享

微信交流羣

微信號

image.png

微信交流羣image.png

釘釘交流羣

image.png

相關文章
相關標籤/搜索