惯性聚合 高效追踪和阅读你感兴趣的博客、新闻、科技资讯
阅读原文 在惯性聚合中打开

推荐订阅源

WordPress大学
WordPress大学
J
Java Code Geeks
Martin Fowler
Martin Fowler
Microsoft Azure Blog
Microsoft Azure Blog
月光博客
月光博客
freeCodeCamp Programming Tutorials: Python, JavaScript, Git & More
人人都是产品经理
人人都是产品经理
有赞技术团队
有赞技术团队
爱范儿
爱范儿
Engineering at Meta
Engineering at Meta
GbyAI
GbyAI
博客园 - 【当耐特】
Y
Y Combinator Blog
Last Week in AI
Last Week in AI
MongoDB | Blog
MongoDB | Blog
G
Google Developers Blog
博客园 - 三生石上(FineUI控件)
Cyber Security Advisories - MS-ISAC
Cyber Security Advisories - MS-ISAC
大猫的无限游戏
大猫的无限游戏
罗磊的独立博客
The Cloudflare Blog
钛媒体:引领未来商业与生活新知
钛媒体:引领未来商业与生活新知
V
V2EX
博客园 - 司徒正美

Max Böck

Faster Horses A year in review: 2024 Going Buildless Live CMS Previews with Sanity and Eleventy Upgrading to Eleventy v3 Old Dogs, new CSS Tricks A year in review: 2023 7 Reasons why I don't write The IndieWeb for Everyone Make Free Stuff Media Queries in Times of @container Container Queries in Web Components Asset Pipelines in Eleventy Medium-Style Share Highlights in Eleventy Space Jam Webmention Analytics Making Persistent Build Folders in Netlify A year in review: 2020 Whimsical Website Club The Return of the 90s Web Human Code Color Theme Switcher Eleventy Résumé Builder The Emergency Website Kit Making a Gigposter A year in review: 2019 Webclerks Conference Roads IndieWeb Link Sharing Good Enough
Connection-Aware Components
Max Böck · 2018-07-02 · via Max Böck

Over the last decade, we have learned to embrace the uncertainty of developing for the web.

We don’t design sites for specific screen dimensions anymore, we make them responsive. We don’t assume ideal browsers and devices, we use progressive enhancement. When it comes to connectivity though, we still treat that as a binary choice: you’re either on- or offline.

Real connections are not that simple. Depending on your location, network condition or data plan, speeds can range from painfully slow to blazingly fast. The concept of “online” can be a drastically different experience for different users, especially on mobile.

What if there was a way to adapt websites based on our users connections, just like we do for varying display widths and browser capabilities? The Network Information API might enable us to do so.

The Network Information API

Permalink to “The Network Information API”

This API is an editor’s draft by the WICG and currently available in Chrome. It can be accessed through the read-only property navigator.connection (MDN), which exposes several properties that provide information about a user’s current connection:

  • connection.type:

Returns the physical network type of the user agent as strings like “cellular”, “ethernet” or “wifi”.

  • connection.downlink:

An effective bandwidth estimate (in Mb/s), based on recently observed active connections.

  • connection.rtt:

An estimate of the average round-trip time (in milliseconds), based on recently observed active connections.

  • connection.saveData:

Returns true if the user has requested “reduced data mode” in their browser settings.

  • connection.effectiveType:

This is a combined estimation of the network quality, based on the round-trip time and downlink properties. It returns a string that describes the connection as either: slow-2g, 2g, 3g or 4g. Here’s how these categories are determined:

Responding to Changes

Permalink to “Responding to Changes”

There is also an Event Listener available on the connection property that fires whenever a change in the network quality is detected:

function onConnectionChange() {
    const { rtt, downlink, effectiveType } = navigator.connection
    console.log(`Round Trip Time: ${rtt}ms`)
    console.log(`Downlink Speed: ${downlink}Mb/s`)
    console.log(`Effective Type: ${effectiveType}`)
}
navigator.connection.addEventListener('change', onConnectionChange)

Support

Permalink to “Support”

can I use support table for the network information API

👉 Be aware that all of this is still experimental. Only Chrome and Samsung Internet browsers have currently implemented the API. It’s a very good candidate for progressive enhancement though - and support for other platforms is on the way.

Permalink to “Connection-aware components”

So how could this be used? Knowing about connection quality enables us to custom-fit resources based on network speed and data preferences. This makes it possible to build an interface that dynamically responds to the user’s connection - a “connection-aware” frontend.

By combining the Network Information API with React, we could write a component that renders different elements for different speeds. For example, a <Media /> component in a news article might output:

  • offline: a placeholder with alt text
  • 2g / reduced data mode: a low-resolution image, ~30kb
  • 3g: a high resolution retina image, ~200kb
  • 4g: a HD video ~1.8MB
a media component, showing four different states of an image or video of a chameleon
The different states of our Media component

Here’s a (very simplified) example of how that might work:

class ConnectionAwareMedia extends React.Component (
    constructor(props) {
        super(props)
        this.state = {
            connectionType: undefined
        }
    }

    componentWillMount() {
        // check connection type before first render.
        if (navigator.connection && navigator.connection.effectiveType) {
            const connectionType = navigator.onLine 
                ? navigator.connection.effectiveType
                : 'offline'
            this.setState({
                connectionType
            })
        }
    }

    render() {
        const { connectionType } = this.state
        const { imageSrc, videoSrc, alt } = this.props

        // fallback if network info API is not supported.
        if (!connectionType) {
            return <Image src={imageSrc.hires} alt={alt} />
        }

        // render different subcomponents based on network speed.
        switch(connectionType) {
            case 'offline':
                return <Placeholder caption={alt} />

            case '4g':
                return <Video src={videoSrc} />

            case '3g':
                return <Image src={imageSrc.hires} alt={alt} />

            default:
                return <Image src={imageSrc.lowres} alt={alt} />
        }
    }
)

Using a Higher-Order Component

Permalink to “Using a Higher-Order Component”

The above example makes our component a bit unpredictable - it renders different things, even when given the same props. This makes it harder to test and maintain. To simplify it and enable reuse of our logic, moving the network condition check into a separate higher-order component might be a good idea.

Such a HoC could take in any component we want and make it connection-aware, injecting the effective connection type as a prop.

function withConnectionType(WrappedComponent, respondToChange = false) {
    return class extends React.Component {
        constructor(props) {
            super(props)
            this.state = {
                connectionType: undefined
            }
            // Basic API Support Check.
            this.hasNetworkInfoSupport = Boolean(
                navigator.connection && navigator.connection.effectiveType
            )
            this.setConnectionType = this.setConnectionType.bind(this)
        }

        componentWillMount() {
            // Check before the component first renders.
            this.setConnectionType()
        }

        componentDidMount() {
            // optional: respond to connectivity changes.
            if (respondToChange) {
                navigator.connection.addEventListener(
                    'change', 
                    this.setConnectionType
                )
            }
        }

        componentWillUnmount() {
            if (respondToChange) {
                navigator.connection.removeEventListener(
                    'change', 
                    this.setConnectionType
                )
            }
        }

        getConnectionType() {
            const connection = navigator.connection
            // check if we're offline first...
            if (!navigator.onLine) {
                return 'offline'
            }
            // ...or if reduced data is preferred.
            if (connection.saveData) {
                return 'saveData'
            }
            return connection.effectiveType
        }

        setConnectionType() {
            if (this.hasNetworkInfoSupport) {
                const connectionType = this.getConnectionType()
                this.setState({
                    connectionType
                })
            }
        }

        render() {
            // inject the prop into our component.
            // default to "undefined" if API is not supported.
            return (
                <WrappedComponent
                    connectionType={this.state.connectionType}
                    {...this.props}
                />
            )
        }
    }
}

// Now we can reuse the function to enhance all kinds of components.
const ConnectionAwareMedia = withConnectionType(Media)

👉 This small proof-of concept is also available on CodePen, if you want to play around.

Further Reading

Permalink to “Further Reading”