微信公众号搜"智元新知"关注
微信扫一扫可直接关注哦!

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

如何解决ReactJs / NextJs-有条件地渲染互斥组件,而不会影响加载时间

我正在尝试提高React Web应用程序的加载速度。

我有两个组件导入-一个用于移动设备,一个用于台式机(不良设计?我想是这样):

import Posts from '../components/post/posts';
import PostsMobile from '../components/post/postsMobile';

这很容易开发,因为我不必努力使同一组件与台式机和移动设备兼容。

然后检查屏幕尺寸并加载适当的组件,我这样做:

const largeScreen = useMediaQuery(theme => theme.breakpoints.up('sm'));
 ...
 {largeScreen? (
 <Posts />
) :
(
<PostsMobile />
)
}

您可以在此处调整浏览器的大小以查看两个组件的负载Link to home page showing the two components

  1. <PostsMobile />仅在react看到其需要时才导入,还是无论什么时候都自动开始导入?

  2. 是否有更好的方法来有条件地呈现互斥组件而又不影响加载时间?

解决方法

经典的条件渲染,这是执行此操作的适当方法,在任何情况下,都只会将一个组件添加到DOM。附带说明一下,为移动视图和桌面视图使用两个不同的组件并不是最好的主意,通常您的html结构应该相同,并且您应该使用CSS进行布局更改(根据Google的建议-https://web.dev/responsive-web-design-basics/

,

查看@artsy/fresnel,他们有一个非常简单的示例,展示了如何configure Next.js and Gatsby.js在SSR环境中实现依赖屏幕宽度的控制

  • 注意:它几乎不会增加开销大小;目前,我在我的产品组合中将它与tailwindcss结合使用,并且事实证明它是一个了不起的工具。易于配置,实现起来很简单。这是example of conditionally rendering the same svg icon four times的屏幕尺寸函数,用于相应地自定义样式(xs(移动设备),sm,md,大于md(桌面))

(1)在您的组件目录中创建一个窗口宽度的文件,以配置@artsy/fresnel进行全局共享

  • components/window-width.jsxcomponents/window-width.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
    }
});
// ...

(2)用pages/index.jsx组件包裹pages/index.tsxMediaContextProvider

// ...

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;

// ...

(3)最后,将生成的mediaStyles CSS注入到Next的style中的text/css类型的Head标记中

  • pages/_document.jsxpages/_document.tsx
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>
        );
    }
}

(4)利润;配置完成

(5)奖金示例-有条件地将ArIcon渲染为设备大小的函数

  • components/lead-arIcon.tsx
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;

Alt Text

,

您可以尝试延迟加载组件。

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 />
);

注意:您还必须添加一个调整大小的侦听器,因此,如果屏幕达到特定宽度,则会加载另一个组件并进行渲染。

注意2 :如果您不关心SSR,则可以尝试将React.lazySuspense一起使用:https://en.reactjs.org/docs/code-splitting.html#reactlazy

,

我认为台式机和移动设备的用法非常有效,例如,尽管CSS版本几乎总是首选,但对于在手机,移动设备上无法使用的拖放组件,这可能就没有意义了仅汉堡菜单等。

反应的lazy loading是必经之路(除非您需要SSR):

import React,{ Suspense } from 'react';

const Posts = React.lazy(() => import('../components/post/posts');
const PostsMobile = React.lazy(() => import('../components/post/postsMobile');

function MainComponent() {
  return (
    <div>
      <Suspense fallback={<div>Loading...</div>}>
        { largeScreen? ( <Posts />) : <PostsMobile /> }
      </Suspense>
    </div>
  );
}

这将确保仅在首次渲染组件时才加载该组件。温馨提示:如果将loading div更改为animated loading placeholder,则应用程序的UI可能会更令人愉悦。

,

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

https://nextjs.org/docs/advanced-features/dynamic-import

import dynamic from 'next/dynamic'

const Posts = dynamic(() => import('../components/post/posts'),{ loading: () => <LinearProgress /> });

const PostsMobile = dynamic(() => import('../components/post/postsMobile'),{ loading: () => <LinearProgress /> });

这为我节省了几毫秒

enter image description here

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

版权声明:本文内容由互联网用户自发贡献,该文观点与技术仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请发送邮件至 dio@foxmail.com 举报,一经查实,本站将立刻删除。