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