国产xxxx99真实实拍_久久不雅视频_高清韩国a级特黄毛片_嗯老师别我我受不了了小说

資訊專欄INFORMATION COLUMN

組件復(fù)用那些事兒 - React 實(shí)現(xiàn)按需加載輪子

dackel / 606人閱讀

摘要:同時(shí),懶加載按需加載概念至關(guān)重要。時(shí)至今日,這些實(shí)現(xiàn)懶加載腳本的代碼仍有學(xué)習(xí)意義。代碼實(shí)戰(zhàn)下面讓我們動(dòng)手實(shí)現(xiàn)一個(gè)按需加載輪子。同樣,對(duì)于組件也可以使用無(wú)狀態(tài)組件函數(shù)式組件實(shí)現(xiàn)這樣無(wú)疑更加簡(jiǎn)潔。

組件化在當(dāng)今前端開(kāi)發(fā)領(lǐng)域中是一個(gè)非常重要的概念。著名的前端類庫(kù),比如 React、Vue 等對(duì)此概念都倍加推崇。確實(shí),組件化復(fù)用性(reusability)和模塊性(modularization)的優(yōu)點(diǎn)對(duì)于復(fù)雜場(chǎng)景需求具有先天優(yōu)勢(shì)。組件就如同樂(lè)高積木、建筑石塊一般,一點(diǎn)點(diǎn)拼接構(gòu)成了我們的應(yīng)用。

同時(shí),懶加載(Lazy-loading)/按需加載概念至關(guān)重要。它對(duì)于頁(yè)面性能優(yōu)化,用戶體驗(yàn)提升提供了新思路。在必要情況下,我們請(qǐng)求的資源更少、解析的腳本更少、執(zhí)行的內(nèi)容更少,達(dá)到效果也就越好。

這篇文章將從懶加載時(shí)機(jī)、組件復(fù)用手段、代碼實(shí)例三方面來(lái)分析,happy reading!

按需加載場(chǎng)景設(shè)計(jì)分析

一個(gè)典型的頁(yè)面如下圖:

它包含了以下幾個(gè)區(qū)塊:

一個(gè)頭部 header;

圖片展示區(qū);

地圖展現(xiàn)區(qū);

頁(yè)面 footer。

對(duì)應(yīng)代碼示例:

const Page = () => {
  
};

當(dāng)用戶來(lái)訪時(shí),如果不滾動(dòng)頁(yè)面,只能看見(jiàn)頭部區(qū)域。但在很多場(chǎng)景下,我們都會(huì)加載所有的 JavaScript 腳本、 CSS 資源以及其他資源,進(jìn)而渲染了完整頁(yè)面。這明顯是不必要的,消耗了更多帶寬,延遲了頁(yè)面 load 時(shí)間。為此,前端歷史上做過(guò)很多懶加載探索,很多大公司的開(kāi)源作品應(yīng)勢(shì)而出:比如 Yahoo 的 YUI Loader,F(xiàn)acebook 的 Haste, Bootloader and Primer等。時(shí)至今日,這些實(shí)現(xiàn)懶加載腳本的代碼仍有學(xué)習(xí)意義。這里不再展開(kāi)。

如下圖,在正常邏輯情況下,代碼覆蓋率層面,我們看到 1.1MB/1.5MB (76%) 的代碼并沒(méi)有應(yīng)用到。

另外,并不是所有資源都需要進(jìn)行懶加載,我們?cè)谠O(shè)計(jì)層面上需要考慮以下幾點(diǎn):

不要按需加載首屏內(nèi)容。這很好理解,首屏?xí)r間至關(guān)重要,用戶能夠越早看到越好。那么如何定義首屏內(nèi)容?這需要結(jié)合用戶終端,站點(diǎn)布局來(lái)考慮;

預(yù)先懶加載。我們應(yīng)該避免給用戶呈現(xiàn)空白內(nèi)容,因此預(yù)先懶加載,提前執(zhí)行腳本對(duì)于用戶體驗(yàn)的提升非常明顯。比如下圖,在圖片出現(xiàn)在屏幕 100px 時(shí),提前進(jìn)行圖片請(qǐng)求和渲染;

懶加載對(duì) SEO 的影響。這里面涉及到內(nèi)容較多,需要開(kāi)發(fā)者了解搜索引擎爬蟲(chóng)機(jī)制。以 Googlebot 為例,它支持 IntersectionObserver,但是也僅僅對(duì)視口里內(nèi)容起作用。這里不再詳細(xì)展開(kāi),感興趣的讀者可以通過(guò)測(cè)試頁(yè)面以及測(cè)試頁(yè)面源碼,并結(jié)合 Google 站長(zhǎng)工具:Fetch as Google 進(jìn)行試驗(yàn)。

React 組件復(fù)用技術(shù)

提到組件復(fù)用,大多開(kāi)發(fā)者應(yīng)該對(duì)高階組件并不陌生。這類組件接受其他組件,進(jìn)行功能增強(qiáng),并最終返回一個(gè)組件進(jìn)行消費(fèi)。React-redux 的 connect 即是一個(gè) currying 化的典型應(yīng)用,代碼示例:

const MyComponent = props => (
  
{props.id} - {props.name}
); // ... const ConnectedComponent = connect(mapStateToProps, mapDispatchToProps)( MyComponent );

同樣,F(xiàn)unction as Child Component 或者稱為 Render Callback 技術(shù)也較為常用。很多 React 類庫(kù)比如 react-media 和 unstated 都有廣泛使用。以 react-media 為例:

const MyComponent = () => (
  
    {matches =>
      matches ? (
        

The document is less than 600px wide.

) : (

The document is at least 600px wide.

) }
);

Media 組件將會(huì)調(diào)用其 children 進(jìn)行渲染,核心邏輯為:

class Media extends React.Component {
    ...
    render() {
        React.Children.only(children)
    }
}

這樣,子組件并不需要感知 media query 邏輯,進(jìn)而完成復(fù)用。

除此之外,還有很多組件復(fù)用技巧,比如 render props 等,這里不再一一分析。感興趣的讀者可以在我的新書中找到相關(guān)內(nèi)容。

代碼實(shí)戰(zhàn)

下面讓我們動(dòng)手實(shí)現(xiàn)一個(gè)按需加載輪子。首先需要設(shè)計(jì)一個(gè) Observer 組件,這個(gè)組件將會(huì)去檢測(cè)目標(biāo)區(qū)塊是否在視口之中可見(jiàn)。為了簡(jiǎn)化不必要的邏輯,我們使用 Intersection Observer API,這個(gè)方法異步觀察目標(biāo)元素的可視狀態(tài)。其兼容性可以參考這里。

class Observer extends Component {
  constructor() {
    super();
    this.state = { isVisible: false };
    this.io = null;
    this.container = null;
  }
  componentDidMount() {
    this.io = new IntersectionObserver([entry] => {
      this.setState({ isVisible: entry.isIntersecting });
    }, {});
    this.io.observe(this.container);
  }
  componentWillUnmount() {
    if (this.io) {
      this.io.disconnect();
    }
  }
  render() {
    return (
      // 這里也可以使用 findDOMNode 實(shí)現(xiàn),但是不建議
      
{ this.container = div; }} > {Array.isArray(this.props.children) ? this.props.children.map(child => child(this.state.isVisible)) : this.props.children(this.state.isVisible)}
); } }

如上,該組件具有 isVisible 狀態(tài),表示目標(biāo)元素是否可見(jiàn)。this.io 表示當(dāng)前 IntersectionObserver 實(shí)例;this.container 表示當(dāng)前觀察元素,它通過(guò) ref 來(lái)完成目標(biāo)元素的獲取。

componentDidMount 方法中,我們進(jìn)行 this.setState.isVisible 狀態(tài)的切換;在 componentWillUnmount 方法中,進(jìn)行垃圾回收。

很明顯,這種復(fù)用方式為前文提到的 Function as Child Component。

注意,對(duì)于上述基本實(shí)現(xiàn),我們完全可以進(jìn)行自定義的個(gè)性化設(shè)置。IntersectionObserver 支持 margins 或者 thresholds 的選項(xiàng)。我們可以在 constructor 里實(shí)現(xiàn)配置項(xiàng)目初始化,在 componentWillReceiveProps 生命周期函數(shù)中進(jìn)行更新。

這樣一來(lái),針對(duì)前文頁(yè)面內(nèi)容,我們可以進(jìn)行 Gallery 組件和 Map 組件懶加載處理:

const Page = () => {
    
{isVisible => } {isVisible => }
}

我們將 isVisible 狀態(tài)進(jìn)行傳遞。相應(yīng)消費(fèi)組件可以根據(jù) isVisible 進(jìn)行選擇性渲染。具體實(shí)現(xiàn):

class Map extends Component {
  constructor() {
    super();
    this.state = { initialized: false };
    this.map = null;
  }
initializeMap() {
    this.setState({ initialized: true });
    // 加載第三方 Google map
    loadScript("https://maps.google.com/maps/api/js?key=", () => {
      const latlng = new google.maps.LatLng(38.34, -0.48);
      const myOptions = { zoom: 15, center: latlng };
      const map = new google.maps.Map(this.map, myOptions);
    });
  }
componentDidMount() {
    if (this.props.isVisible) {
      this.initializeMap();
    }
  }
componentWillReceiveProps(nextProps) {
    if (!this.state.initialized && nextProps.isVisible) {
      this.initializeMap();
    }
  }
render() {
    return (
      
{ this.map = div; }} /> ); } }

只有當(dāng) Map 組件對(duì)應(yīng)的 container 出現(xiàn)在視口時(shí),我們?cè)偃ミM(jìn)行第三方資源的加載。

同樣,對(duì)于 Gallery 組件:

class Gallery extends Component {
  constructor() {
    super();
    this.state = { hasBeenVisible: false };
  }
  componentDidMount() {
    if (this.props.isVisible) {
      this.setState({ hasBeenVisible: true });
    }
  }
  componentWillReceiveProps(nextProps) {
    if (!this.state.hasBeenVisible && nextProps.isVisible) {
      this.setState({ hasBeenVisible: true });
    }
  }
  render() {
    return (
      

Some pictures

Picture 1 {this.state.hasBeenVisible ? ( ) : (
)} Picture 2 {this.state.hasBeenVisible ? ( ) : (
)}
); } }

也可以使用無(wú)狀態(tài)組件/函數(shù)式組件實(shí)現(xiàn):

const Gallery = ({ isVisible }) => (
  

Some pictures

Picture 1 {isVisible ? ( ) : (
)} Picture 2 {isVisible ? ( ) : (
)}
);

這樣無(wú)疑更加簡(jiǎn)潔。但是當(dāng)元素移出視口時(shí),相應(yīng)圖片不會(huì)再繼續(xù)展現(xiàn),而是復(fù)現(xiàn)了 placeholder。

如果我們需要懶加載的內(nèi)容只在頁(yè)面生命周期中記錄一次,可以設(shè)置 hasBeenVisible 參數(shù):

const Page = () => {
  ...
  
    {(isVisible, hasBeenVisible) =>
       // Gallery can be now stateless
    }
  
  ...
}

或者直接實(shí)現(xiàn) ObserverOnce 組件:

class ObserverOnce extends Component {
  constructor() {
    super();
    this.state = { hasBeenVisible: false };
    this.io = null;
    this.container = null;
  }
  componentDidMount() {
    this.io = new IntersectionObserver(entries => {
      entries.forEach(entry => {
        if (entry.isIntersecting) {
          this.setState({ hasBeenVisible: true });
          this.io.disconnect();
        }
      });
    }, {});
    this.io.observe(this.container);
  }
  componentWillUnmount() {
    if (this.io) {
      this.io.disconnect();
    }
  }
  render() {
    return (
      
{ this.container = div; }} > {Array.isArray(this.props.children) ? this.props.children.map(child => child(this.state.hasBeenVisible)) : this.props.children(this.state.hasBeenVisible)}
); } }
更多場(chǎng)景

上面我們使用了 Observer 組件去加載資源。包括了 Google Map 第三方內(nèi)容和圖片。我們同樣可以完成“當(dāng)組件出現(xiàn)在視口時(shí),才展現(xiàn)元素動(dòng)畫”的需求。

仿照 React Alicante 網(wǎng)站,我們實(shí)現(xiàn)了類似的按需執(zhí)行動(dòng)畫需求。具體可見(jiàn) codepen 地址。

IntersectionObserver polyfilling

前面提到了 IntersectionObserver API 的兼容性,這自然就繞不開(kāi) polyfill 話題。

一種處理兼容性的選項(xiàng)是“漸進(jìn)增強(qiáng)”(progressive enhancement),即只有在支持的場(chǎng)景下實(shí)現(xiàn)按需加載,否則永遠(yuǎn)設(shè)置 isVisible 狀態(tài)為 true:

class Observer extends Component {
  constructor() {
    super();
    this.state = { isVisible: !(window.IntersectionObserver) };
    this.io = null;
    this.container = null;
  }
  componentDidMount() {
    if (window.IntersectionObserver) {
      this.io = new IntersectionObserver(entries => {
        ...
      }
    }
  }
}

這樣顯然不能實(shí)現(xiàn)按需的目的,我更加推薦 w3c 的 IntersectionObserver polyfill:

class Observer extends Component {
  ...
  componentDidMount() {
    (window.IntersectionObserver
      ? Promise.resolve()
      : import("intersection-observer")
    ).then(() => {
      this.io = new window.IntersectionObserver(entries => {
        entries.forEach(entry => {
          this.setState({ isVisible: entry.isIntersecting });
        });
      }, {});
      this.io.observe(this.container);
    });
  }
  ...
}

當(dāng)瀏覽器不支持 IntersectionObserver 時(shí),我們動(dòng)態(tài) import 進(jìn)來(lái) polyfill,這就需要支持 dynamic import,此為另外話題,這里不再展開(kāi)。

最后試驗(yàn)一下,在不支持的 Safari 瀏覽器下,我們看到 Network 時(shí)間線如下:

總結(jié)

這篇文章介紹涉及到組件復(fù)用、按需加載(懶加載)實(shí)現(xiàn)內(nèi)容。更多相關(guān)知識(shí),可以關(guān)注作者新書。
同時(shí)這篇文章截取于 José M. Pérez 的 Improve the Performance of your Site with Lazy-Loading and Code-Splitting,部分內(nèi)容有所改動(dòng)。

廣告時(shí)間:
如果你對(duì)前端發(fā)展,尤其對(duì) React 技術(shù)棧感興趣:我的新書中,也許有你想看到的內(nèi)容。關(guān)注作者 Lucas HC,新書出版將會(huì)有送書活動(dòng)。

Happy Coding!

PS: 作者?Github倉(cāng)庫(kù)?和?知乎問(wèn)答鏈接?歡迎各種形式交流。

文章版權(quán)歸作者所有,未經(jīng)允許請(qǐng)勿轉(zhuǎn)載,若此文章存在違規(guī)行為,您可以聯(lián)系管理員刪除。

轉(zhuǎn)載請(qǐng)注明本文地址:http://specialneedsforspecialkids.com/yun/113077.html

相關(guān)文章

  • 組件復(fù)用那些事兒 - React 實(shí)現(xiàn)按需加載輪子

    摘要:同時(shí),懶加載按需加載概念至關(guān)重要。時(shí)至今日,這些實(shí)現(xiàn)懶加載腳本的代碼仍有學(xué)習(xí)意義。代碼實(shí)戰(zhàn)下面讓我們動(dòng)手實(shí)現(xiàn)一個(gè)按需加載輪子。同樣,對(duì)于組件也可以使用無(wú)狀態(tài)組件函數(shù)式組件實(shí)現(xiàn)這樣無(wú)疑更加簡(jiǎn)潔。 組件化在當(dāng)今前端開(kāi)發(fā)領(lǐng)域中是一個(gè)非常重要的概念。著名的前端類庫(kù),比如 React、Vue 等對(duì)此概念都倍加推崇。確實(shí),組件化復(fù)用性(reusability)和模塊性(modularization...

    lidashuang 評(píng)論0 收藏0
  • 組件復(fù)用那些事兒 - React 實(shí)現(xiàn)按需加載輪子

    摘要:同時(shí),懶加載按需加載概念至關(guān)重要。時(shí)至今日,這些實(shí)現(xiàn)懶加載腳本的代碼仍有學(xué)習(xí)意義。代碼實(shí)戰(zhàn)下面讓我們動(dòng)手實(shí)現(xiàn)一個(gè)按需加載輪子。同樣,對(duì)于組件也可以使用無(wú)狀態(tài)組件函數(shù)式組件實(shí)現(xiàn)這樣無(wú)疑更加簡(jiǎn)潔。 組件化在當(dāng)今前端開(kāi)發(fā)領(lǐng)域中是一個(gè)非常重要的概念。著名的前端類庫(kù),比如 React、Vue 等對(duì)此概念都倍加推崇。確實(shí),組件化復(fù)用性(reusability)和模塊性(modularization...

    K_B_Z 評(píng)論0 收藏0
  • React 設(shè)計(jì)模式和場(chǎng)景分析

    摘要:這一周連續(xù)發(fā)表了兩篇關(guān)于的文章組件復(fù)用那些事兒實(shí)現(xiàn)按需加載輪子應(yīng)用設(shè)計(jì)之道化妙用其中涉及到組件復(fù)用輪子設(shè)計(jì)相關(guān)話題,并配合相關(guān)場(chǎng)景實(shí)例進(jìn)行了分析。 showImg(https://segmentfault.com/img/remote/1460000014482098); 這一周連續(xù)發(fā)表了兩篇關(guān)于 React 的文章: 組件復(fù)用那些事兒 - React 實(shí)現(xiàn)按需加載輪子 React ...

    avwu 評(píng)論0 收藏0

發(fā)表評(píng)論

0條評(píng)論

最新活動(dòng)
閱讀需要支付1元查看
<