最新国产好看的视频,伊人天堂AV在线,国产Aaaaaa视频,蜜臀视频在线观看一区,人妻av色图,密臀久久久精品影片,青青视频免费观看毛片,久草在线观看视,国产三级精品色情在线

手把手教您實現(xiàn)react異步加載高階組件

 更新時間:2020年04月07日 08:31:16   作者:吾乃Jiraiya  
這篇文章主要介紹了手把手教您實現(xiàn)react異步加載高階組件,文中通過示例代碼介紹的非常詳細,對大家的學習或者工作具有一定的參考學習價值,需要的朋友們下面隨著小編來一起學習學習吧

本篇文章通過分析react-loadable包的源碼,手把手教你實現(xiàn)一個react的異步加載高階組件

1. 首先我們想象中的react異步加載組件應該如何入?yún)⒁约氨┞赌男〢PI?

// 組件應用
import * as React from 'react';
import ReactDOM from 'react-dom';
import Loadable from '@component/test/Loadable';
import Loading from '@component/test/loading';
const ComponentA = Loadable({
  loader: () => import(
    /* webpackChunkName: 'componentA' */
    '@component/test/componentA.js'),
  loading: Loading, //異步組件未加載之前l(fā)oading組件
  delay: 1000, //異步延遲多久再渲染
  timeout: 1000, //異步組件加載超時
})
ComponentA.preload(); //預加載異步組件的方式

const ComponentB = Loadable({
  loader: () => import(
    /* webpackChunkName: 'componentB' */
    '@component/test/componentB.js'),
  loading: Loading, //異步組件未加載之前l(fā)oading組件
})

Loadable.preloadAll().then(() => {
  //
}).catch(err => {
  //
}); //預加載所有的異步組件

const App = (props) => {
  const [isDisplay, setIsDisplay] = React.useState(false);
  if(isDisplay){
    return <React.Fragment>
      <ComponentA />
      <ComponentB />
    </React.Fragment> 
  }else{
    return <input type='button' value='點我' onClick={()=>{setIsDisplay(true)}}/>
  }
}

ReactDOM.render(<App />, document.getElementById('app'));
// loading組件
import * as React from 'react';

export default (props) => {
  const {error, pastDelay, isLoading, timedOut, retry} = props;
  if (props.error) {
    return <div>Error! <button onClick={ retry }>Retry</button></div>;
   } else if (timedOut) {
    return <div>Taking a long time... <button onClick={ retry }>Retry</button></div>;
   } else if (props.pastDelay) {
    return <div>Loading...</div>;
   } else {
    return null;
   }
}

通過示例可以看到我們需要入?yún)oaded、loading、delay、timeout,同時暴露單個預加載和全部預加載的API,接下來就讓我們試著去一步步實現(xiàn)Loadable高階組件

2.組件實現(xiàn)過程

整個Loaded函數(shù)大體如下

// 收集所有需要異步加載的組件 用于預加載
const ALL_INITIALIZERS = [];

function Loadable(opts){
  return createLoadableComponent(load, opts);
}
// 靜態(tài)方法 預加載所有組件
Loadable.preloadAll = function(){

}

接下來實現(xiàn)createLoadableComponent以及l(fā)oad函數(shù)

// 預加載單個異步組件
function load(loader){
  let promise = loader();
  let state = {
    loading: true,
    loaded: null,
    error: null,
  }
  state.promise = promise.then(loaded => {
    state.loading = false;
    state.loaded = loaded;
    return loaded;
  }).catch(err => {
    state.loading = false;
    state.error = err;
    throw err;
  })
  return state;
}

// 創(chuàng)建異步加載高階組件
function createLoadableComponent(loadFn, options){
  if (!options.loading) {
    throw new Error("react-loadable requires a `loading` component");
  }
  let opts = Object.assign({
    loader: null,
    loading: null,
    delay: 200,
    timeout: null,
  }, options);

  let res = null;

  function init(){
    if(!res){
      res = loadFn(options.loader);
      return res.promise;
    }
  }

  ALL_INITIALIZERS.push(init);

  return class LoadableComponent extends React{}
}

我們可以看到createLoadableComponent主要功能包括合并默認配置,將異步組件推入預加載數(shù)組,并返回LoadableComponent組件;load函數(shù)用于加載單個組件并返回該組件的初始加載狀態(tài)

接著我們實現(xiàn)核心部分LoadableComponent組件

class LoadableComponent extends React.Component{
    constructor(props){
      super(props);
      //組件初始化之前調用init方法下載異步組件
      init(); 
      this.state = {
        error: res.error,
        postDelay: false,
        timedOut: false,
        loading: res.loading,
        loaded: res.loaded
      }
      this._delay = null;
      this._timeout = null;
    }
    componentWillMount(){
      //設置開關保證不多次去重新請求異步組件
      this._mounted = true;
      this._loadModule();
    }

    _loadModule(){
      if(!res.loading) return;
      if(typeof opts.delay === 'number'){
        if(opts.delay === 0){
          this.setState({pastDelay: true});
        }else{
          this._delay = setTimeout(()=>{
            this.setState({pastDelay: true});
          }, opts.delay)
        }
      }

      if(typeof opts.timeout === 'number'){
        this._timeout = setTimeout(()=>{
          this.setState({timedOut: true});
        }, opts.timeout)
      }

      let update = () => {
        if(!this._mounted) return;
        this.setState({
          error: res.error,
          loaded: res.loaded,
          loading: res.loading,
        });
      }
      // 接收異步組件的下載結果并重新setState來render
      res.promise.then(()=>{
        update()
      }).catch(err => {
        update()
      })
    }


    // 重新加載異步組件
    retry(){
      this.setState({
        error: null,
        timedOut: false,
        loading: false,
      });
      res = loadFn(opts.loader);
      this._loadModule();
    }
    // 靜態(tài)方法 單個組件預加載
    static preload(){
      init()
    }


    componentWillUnmount(){
      this._mounted = false;
      clearTimeout(this._delay);
      clearTimeout(this._timeout);
    }

    render(){
      const {loading, error, pastDelay, timedOut, loaded} = this.state;
      if(loading || error){
        //異步組件還未下載完成的時候渲染loading組件
        return React.createElement(opts.loading, {
          isLoading: loading,
          pastDelay: pastDelay,
          timedOut: timedOut,
          error: error,
          retry: this.retry.bind(this),
        })
      }else if(loaded){
        // 為何此處不直接用React.createElement?
        return opts.render(loaded, this.props);
      }else{
        return null;
      }
    }    
  }

可以看到,初始的時候調用init方法啟動異步組件的下載,并在_loadModule方法里面接收異步組件的pending結果,待到異步組件下載完畢,重新setState啟動render

接下來還有個細節(jié),異步組件并沒有直接啟動React.createElement去渲染,而是采用opts.render方法,這是因為webpack打包生成的單獨異步組件chunk暴露的是一個對象,其default才是對應的組件

實現(xiàn)如下

function resolve(obj) {
  return obj && obj.__esModule ? obj.default : obj;
}
 
function render(loaded, props) {
  return React.createElement(resolve(loaded), props);
}

最后實現(xiàn)全部預加載方法

Loadable.preloadAll = function(){
  let promises = [];
  while(initializers.length){
    const init = initializers.pop();
    promises.push(init())
  }
  return Promise.all(promises);
}

整個代碼實現(xiàn)如下

const React = require("react");

// 收集所有需要異步加載的組件
const ALL_INITIALIZERS = [];

// 預加載單個異步組件
function load(loader){
  let promise = loader();
  let state = {
    loading: true,
    loaded: null,
    error: null,
  }
  state.promise = promise.then(loaded => {
    state.loading = false;
    state.loaded = loaded;
    return loaded;
  }).catch(err => {
    state.loading = false;
    state.error = err;
    throw err;
  })
  return state;
}

function resolve(obj) {
  return obj && obj.__esModule ? obj.default : obj;
}
 
function render(loaded, props) {
  return React.createElement(resolve(loaded), props);
}

// 創(chuàng)建異步加載高階組件
function createLoadableComponent(loadFn, options){
  if (!options.loading) {
    throw new Error("react-loadable requires a `loading` component");
  }
  let opts = Object.assign({
    loader: null,
    loading: null,
    delay: 200,
    timeout: null,
    render,
  }, options);

  let res = null;

  function init(){
    if(!res){
      res = loadFn(options.loader);
      return res.promise;
    }
  }

  ALL_INITIALIZERS.push(init);

  class LoadableComponent extends React.Component{
    constructor(props){
      super(props);
      init();
      this.state = {
        error: res.error,
        postDelay: false,
        timedOut: false,
        loading: res.loading,
        loaded: res.loaded
      }
      this._delay = null;
      this._timeout = null;
    }

    

    componentWillMount(){
      this._mounted = true;
      this._loadModule();
    }

    _loadModule(){
      if(!res.loading) return;
      if(typeof opts.delay === 'number'){
        if(opts.delay === 0){
          this.setState({pastDelay: true});
        }else{
          this._delay = setTimeout(()=>{
            this.setState({pastDelay: true});
          }, opts.delay)
        }
      }

      if(typeof opts.timeout === 'number'){
        this._timeout = setTimeout(()=>{
          this.setState({timedOut: true});
        }, opts.timeout)
      }

      let update = () => {
        if(!this._mounted) return;
        this.setState({
          error: res.error,
          loaded: res.loaded,
          loading: res.loading,
        });
      }

      res.promise.then(()=>{
        update()
      }).catch(err => {
        update()
      })
    }


    // 重新加載異步組件
    retry(){
      this.setState({
        error: null,
        timedOut: false,
        loading: false,
      });
      res = loadFn(opts.loader);
      this._loadModule();
    }

    static preload(){
      init()
    }


    componentWillUnmount(){
      this._mounted = false;
      clearTimeout(this._delay);
      clearTimeout(this._timeout);
    }

    render(){
      const {loading, error, pastDelay, timedOut, loaded} = this.state;
      if(loading || error){
        return React.createElement(opts.loading, {
          isLoading: loading,
          pastDelay: pastDelay,
          timedOut: timedOut,
          error: error,
          retry: this.retry.bind(this),
        })
      }else if(loaded){
        return opts.render(loaded, this.props);
      }else{
        return null;
      }
    }

    
  }

  return LoadableComponent;
}

function Loadable(opts){
  return createLoadableComponent(load, opts);
}

function flushInitializers(initializers){
  
  
}
Loadable.preloadAll = function(){
  let promises = [];
  while(initializers.length){
    const init = initializers.pop();
    promises.push(init())
  }
  return Promise.all(promises);
}

export default Loadable;

到此這篇關于手把手教您實現(xiàn)react異步加載高階組件的文章就介紹到這了,更多相關react異步加載高階組件內容請搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關文章希望大家以后多多支持腳本之家!

相關文章

  • React Native項目框架搭建的一些心得體會

    React Native項目框架搭建的一些心得體會

    React Native使你能夠在Javascript和React的基礎上獲得完全一致的開發(fā)體驗,構建世界一流的原生APP。接下來通過本文給大家分享React Native項目框架搭建的一些心得體會,感興趣的朋友跟隨小編一起看看吧
    2021-05-05
  • React?+?Typescript領域初學者的常見問題和技巧(最新)

    React?+?Typescript領域初學者的常見問題和技巧(最新)

    這篇文章主要介紹了React?+?Typescript領域初學者的常見問題和技巧,本文給大家介紹的非常詳細,對大家的學習或工作具有一定的參考借鑒價值,需要的朋友可以參考下
    2022-06-06
  • react-native 封裝選擇彈出框示例(試用ios&android)

    react-native 封裝選擇彈出框示例(試用ios&android)

    本篇文章主要介紹了react-native 封裝選擇彈出框示例(試用ios&android),具有一定的參考價值,有興趣的可以了解一下
    2017-07-07
  • React實現(xiàn)類似淘寶tab居中切換效果的示例代碼

    React實現(xiàn)類似淘寶tab居中切換效果的示例代碼

    這篇文章主要介紹了React實現(xiàn)類似淘寶tab居中切換效果,本文通過實例代碼給大家介紹的非常詳細,對大家的學習或工作具有一定的參考借鑒價值,需要的朋友可以參考下
    2020-06-06
  • 詳解React Hooks是如何工作的

    詳解React Hooks是如何工作的

    React Hooks是在React 16.8版本新增的特性,在我看了React官網(wǎng)和一些博客對React Hook的講解后還是覺得沒有get到本質。本篇博客通過手動實現(xiàn)useState()來了解Hook的原理和本質。閱讀此篇博客的前提是你要知道一些 React Hooks的基本用法和使用規(guī)則,不然會看得云里霧里。
    2021-05-05
  • 實例講解React 組件生命周期

    實例講解React 組件生命周期

    這篇文章主要介紹了React 組件生命周期的相關資料,文中示例代碼非常詳細,幫助大家更好的理解和學習,感興趣的朋友可以了解下
    2020-07-07
  • 詳解React中傳入組件的props改變時更新組件的幾種實現(xiàn)方法

    詳解React中傳入組件的props改變時更新組件的幾種實現(xiàn)方法

    這篇文章主要介紹了詳解React中傳入組件的props改變時更新組件的幾種實現(xiàn)方法,小編覺得挺不錯的,現(xiàn)在分享給大家,也給大家做個參考。一起跟隨小編過來看看吧
    2018-09-09
  • 使用React+ts實現(xiàn)無縫滾動的走馬燈詳細過程

    使用React+ts實現(xiàn)無縫滾動的走馬燈詳細過程

    這篇文章主要給大家介紹了關于使用React+ts實現(xiàn)無縫滾動的走馬燈詳細過程,文中給出了詳細的代碼示例以及圖文教程,對大家的學習或者工作具有一定的參考學習價值,需要的朋友可以參考下
    2023-08-08
  • 編寫簡潔React組件的小技巧

    編寫簡潔React組件的小技巧

    這篇文章主要介紹了編寫簡潔React組件的小技巧,幫助大家更好的理解和學習使用React,感興趣的朋友可以了解下
    2021-04-04
  • React創(chuàng)建組件的三種方式及其區(qū)別

    React創(chuàng)建組件的三種方式及其區(qū)別

    本文主要介紹了React創(chuàng)建組件的三種方式及其區(qū)別,具有一定的參考價值,下面跟著小編一起來看下吧
    2017-01-01

最新評論

天台县| 岐山县| 曲沃县| 庆元县| 米泉市| 星子县| 平昌县| 六枝特区| 弋阳县| 罗定市| 黑水县| 雅江县| 鸡东县| 邵阳市| 寻乌县| 丰镇市| 通化县| 福安市| 同德县| 阿勒泰市| 台中县| 和林格尔县| 涡阳县| 蚌埠市| 施秉县| 镇原县| 巧家县| 苍南县| 钟祥市| 大方县| 宣汉县| 金寨县| 玉溪市| 武川县| 沙田区| 天长市| 定结县| 苗栗县| 清流县| 朝阳县| 临漳县|