ReactJs/NextJs-有条件地呈现互斥组件,而不影响加载时间

ReactJs/NextJs-有条件地呈现互斥组件,而不影响加载时间,reactjs,Reactjs,我正在努力提高react web应用程序的加载速度 我有两个组件导入-一个用于移动设备,一个用于桌面(糟糕的设计?我想是的): 这很容易开发,因为我不必努力使同一个组件与桌面和移动设备兼容 然后,为了检查屏幕大小并加载适当的组件,我执行以下操作: const largeScreen = useMediaQuery(theme => theme.breakpoints.up('sm')); ... {largeScreen? ( <Posts /> ) : ( <Po

我正在努力提高react web应用程序的加载速度

我有两个组件导入-一个用于移动设备,一个用于桌面(糟糕的设计?我想是的):

这很容易开发,因为我不必努力使同一个组件与桌面和移动设备兼容

然后,为了检查屏幕大小并加载适当的组件,我执行以下操作:

const largeScreen = useMediaQuery(theme => theme.breakpoints.up('sm'));
 ...
 {largeScreen? (
 <Posts />
) :
(
<PostsMobile />
)
}
const largeScreen=useMediaQuery(主题=>theme.breakpoints.up('sm');
...
{大屏幕(
) :
(
)
}
您可以在此处调整浏览器的大小,以查看加载的两个组件:

  • 是否仅在react发现需要时才导入,还是在开始时无论发生什么情况都会自动导入

  • 有没有更好的方法在不影响加载时间的情况下有条件地呈现互斥组件


  • 经典的条件渲染,这是正确的方法,在任何情况下,只有一个组件将添加到DOM中。作为一个补充说明,在移动和桌面视图中使用两个不同的组件不是最好的主意,通常您的html结构应该是相同的,您应该使用CSS进行任何布局更改(根据Google的建议-

    经典条件渲染,这是适当的方法,在任何情况下,只有一个组件将添加到DOM中。作为补充说明,在移动视图和桌面视图中使用两个不同的组件不是最好的主意,通常您的html结构应该是相同的,您应该使用CSS进行任何布局更改(根据Google的建议-

    您可以尝试延迟加载组件

    posts;
    
    componentDidMount() {
       if (largeScreen) {
          import('../components/post/posts').then(({ default: Posts }) => {
                                          // ^^^^ make sure it has a default export
             this.posts = Posts;
             this.forceUpdate();
          });
       } else {
         // here load the other component for lower screens
       }
    }
    
    然后,在内部渲染:

    const Posts = this.posts;
    
    return largeScreen? (
       <Posts />
    ) : (
       <PostsMobile />
    );
    
    const Posts=this.Posts;
    归还宽大的屏幕?(
    ) : (
    );
    
    注意:您还必须添加一个调整大小的侦听器,因此如果屏幕达到一定宽度,另一个组件将加载并渲染


    注2:如果您不关心SSR-您可以尝试
    反应。延迟
    暂停

    您可以尝试延迟加载组件

    posts;
    
    componentDidMount() {
       if (largeScreen) {
          import('../components/post/posts').then(({ default: Posts }) => {
                                          // ^^^^ make sure it has a default export
             this.posts = Posts;
             this.forceUpdate();
          });
       } else {
         // here load the other component for lower screens
       }
    }
    
    然后,在内部渲染:

    const Posts = this.posts;
    
    return largeScreen? (
       <Posts />
    ) : (
       <PostsMobile />
    );
    
    const Posts=this.Posts;
    归还宽大的屏幕?(
    ) : (
    );
    
    注意:您还必须添加一个调整大小的侦听器,因此如果屏幕达到一定宽度,另一个组件将加载并渲染


    注2:如果你不关心SSR-你可以尝试
    反应。lazy
    悬念

    在我看来,桌面组件和移动组件的用法非常有效,例如,尽管CSS版本几乎总是首选,对于不能在手机上工作的拖放组件、只能在手机上使用的汉堡包菜单等,这可能没有意义

    React是一种方式(除非您需要SSR):

    import React,{suspend}来自'React';
    const Posts=React.lazy(()=>import('../components/post/Posts');
    const PostsMobile=React.lazy(()=>import('../components/post/PostsMobile');
    函数main component(){
    返回(
    {宽屏?():}
    );
    }
    

    这将保证组件仅在首次呈现时加载。额外提示:如果您将
    加载
    div更改为an,应用程序的UI可能会更令人愉快。

    在我看来,桌面组件和移动组件的用法非常有效,例如,尽管CSS版本几乎总是首选d、 对于不能在手机上工作的拖放组件、只能在手机上使用的汉堡包菜单等,这可能没有意义

    React是一种方式(除非您需要SSR):

    import React,{suspend}来自'React';
    const Posts=React.lazy(()=>import('../components/post/Posts');
    const PostsMobile=React.lazy(()=>import('../components/post/PostsMobile');
    函数main component(){
    返回(
    {宽屏?():}
    );
    }
    

    这将保证组件仅在首次呈现时加载。额外提示:如果将
    加载
    div更改为an,应用程序的UI可能会更令人愉快。

    没有一个答案与使用Nextjs的SSR兼容,因此我最终使用了动态导入功能。看起来非常强大但简单

    从“下一个/动态”导入动态
    const Posts=dynamic(()=>import('../components/post/Posts'),
    {加载:()=>});
    const PostsMobile=dynamic(()=>import('../components/post/PostsMobile'),
    {加载:()=>});
    
    这节省了我几毫秒的时间


    我不确定是否有更好的选择,所以我希望大家能发表评论。

    没有一个答案与使用Nextjs的SSR兼容,因此我最终使用了动态导入功能。看起来非常强大但简单

    从“下一个/动态”导入动态
    const Posts=dynamic(()=>import('../components/post/Posts'),
    {加载:()=>});
    const PostsMobile=dynamic(()=>import('../components/post/PostsMobile'),
    {加载:()=>});
    
    这节省了我几毫秒的时间

    我不确定是否有更好的选择,所以我希望大家能发表评论。

    请看,他们有一个非常简单的例子,展示了如何在SSR环境中实现屏幕宽度相关的控制

    • 注意:它几乎不会增加开销大小;我目前正在我的产品组合中与tailwindcss一起使用它,它已被证明是一个非常好的工具。易于配置,实现简单。下面是一个根据屏幕大小定制相应样式的函数(xs(移动)、sm、md、大于md(桌面))
    (1) 在组件目录中创建一个窗口宽度文件,以配置
    @artsy/fresnel
    进行全局共享

    • 组件/窗口宽度.jsx
      组件/窗口宽度.tsx
    • import { createMedia } from '@artsy/fresnel'; const PortfolioMedia= createMedia({ breakpoints: { xs: 0, sm: 768, md: 1000, lg: 1200, }, }) // Generate CSS to be injected in the head using a styles tag (pages/_document.jsx or pages/_document.tsx) export const mediaStyles = PortfolioMedia.createMediaStyle(); export const { Media, MediaContextProvider } = PortfolioMedia; // https://github.com/artsy/fresnel/tree/master/examples/nextjs
    // ...
    const PortfolioMedia = createMedia({
        breakpoints: {
            xs: 0,
            sm: 640,
            md: 768,
            lg: 1024,
            xl: 1280
        }
    });
    // ...
    
    // ...
    
    import { MediaContextProvider } from 'components/window-width';
    
    
    interface IndexProps {
        allPosts: Post[];
        allAbout: AboutType[];
        allBlog: BlogType[];
    }
    
    const Index = ({ allPosts, allAbout, allBlog }: IndexProps) => {
        const morePosts = allPosts.slice(0);
        const moreAbout = allAbout.slice(0);
        const moreBlog = allBlog.slice(0);
        return (
            <Fragment>
                <MediaContextProvider>
                    <Lead />
                    <Head>
                        <title>{`${CLIENT_NAME} landing page`}</title>
                    </Head>
                    <div className='max-w-cardGridMobile md:max-w-cardGrid my-portfolioH2F grid mx-auto content-center justify-center items-center text-center'>
                        {morePosts.length > 0 && <Cards posts={morePosts} />}
                    </div>
                    <div className='max-w-full my-portfolioH2F block mx-auto content-center justify-center items-center text-left'>
                        {moreAbout.length > 0 && <AboutCoalesced abouts={allAbout} />}
                    </div>
                    <div className='max-w-full my-portfolioH2F block mx-auto content-center justify-center items-center text-left'>
                        {moreBlog.length > 0 && <BlogCoalesced blogs={allBlog} />}
                    </div>
                    <Footer />
                </MediaContextProvider>
            </Fragment>
        );
    };
    
    export default Index;
    
    // ...
    
    import Document, {
        Html,
        Head,
        Main,
        NextScript,
        DocumentContext
    } from 'next/document';
    import { mediaStyles } from 'components/window-width';
    
    export default class MyDocument extends Document {
        static async getInitialProps(ctx: DocumentContext) {
            const initialProps = await Document.getInitialProps(ctx);
            return { ...initialProps };
        }
        render() {
            return (
                <Html lang='en-US'>
                    <Head>
                        <meta charSet='utf-8' />
                        <link rel='stylesheet' href='https://use.typekit.net/cub6off.css' />
                        <style type='text/css' dangerouslySetInnerHTML={{ __html: mediaStyles }} />
                    </Head>
                    <body className='root'>
                        <script src='./noflash.js' />
                        <Main />
                        <NextScript />
                    </body>
                </Html>
            );
        }
    }
    
    import { ArIcon } from 'components/svg-icons';
    import Link from 'next/link';
    import { Media } from 'components/window-width';
    import { Fragment } from 'react';
    import DarkMode from 'components/lead-dark-mode';
    
    const ArIconConditional = (): JSX.Element => {
        const arIconXs: JSX.Element = (
            <Media at='xs'>
                <Link href='/'>
                    <a
                        className='container block pl-portfolio pt-portfolio justify-between mx-auto w-full min-w-full '
                        id='top'
                        aria-label='top'
                    >
                        <ArIcon width='18vw' height='18vw' />
                    </a>
                </Link>
            </Media>
        );
    
        const arIconSm: JSX.Element = (
            <Media at='sm'>
                <Link href='/'>
                    <a
                        className='container block pl-portfolio pt-portfolio justify-between mx-auto w-full min-w-full '
                        id='top'
                        aria-label='top'
                    >
                        <ArIcon width='15vw' height='15vw' />
                    </a>
                </Link>
            </Media>
        );
    
        const arIconMd: JSX.Element = (
            <Media at='md'>
                <Link href='/'>
                    <a
                        className='container block pl-portfolio pt-portfolio justify-between mx-auto w-full min-w-full '
                        id='top'
                        aria-label='top'
                    >
                        <ArIcon width='12.5vw' height='12.5vw' />
                    </a>
                </Link>
            </Media>
        );
    
        const arIconDesktop: JSX.Element = (
            <Media greaterThan='md'>
                <Link href='/'>
                    <a
                        className='container block pl-portfolio pt-portfolio justify-between mx-auto w-full min-w-full'
                        id='top'
                        aria-label='top'
                    >
                        <ArIcon
                            width='10vw'
                            height='10vw'
                            classNames={[
                                ` antialised w-svgIcon max-w-svgIcon transform transition-all`,
                                '  stroke-current',
                                ` fill-primary`
                            ]}
                        />
                    </a>
                </Link>
            </Media>
        );
    
        const DarkModeToggler = (): JSX.Element => (
            <div className='pt-portfolio text-customTitle transition-all transform -translate-y-mdmxSocial col-span-4 text-right -translate-x-portfolioPadding'>
                <DarkMode />
            </div>
        );
    
        const ArIconsCoalesced = (): JSX.Element => (
            <Fragment>
                <div className='relative block justify-between lg:w-auto lg:static lg:block lg:justify-start transition-all w-full min-w-full col-span-2'>
                    {arIconXs}
                    {arIconSm}
                    {arIconMd}
                    {arIconDesktop}
                </div>
            </Fragment>
        );
        return (
            <Fragment>
                <div className='select-none relative z-1 justify-between pt-portfolioDivider navbar-expand-lg grid grid-cols-6 min-w-full w-full container overflow-y-hidden overflow-x-hidden transform'>
                    <ArIconsCoalesced />
                    <DarkModeToggler />
                </div>
            </Fragment>
        );
    };
    
    export default ArIconConditional;