前军教程网

中小站长与DIV+CSS网页布局开发技术人员的首选CSS学习平台

Remix vs Next.Js 你应该使用哪一个?

每日分享最新,最流行的软件开发知识与最新行业趋势,希望大家能够一键三连,多多支持,跪求关注,点赞,留言。

如今,当我们想要基于 React 创建一个新的 Web 项目时,我们有许多不同的框架选择。

如今,当我们想要基于 React 创建一个新的 Web 项目时,我们有许多不同的框架选择。作为一名开发人员,您会发现自己很难知道应该选择哪一种,或者哪一种最适合您的需求。

您可能知道最常用的框架之一是Next.js,Netflix、Twitch 或 Uber 等公司通常使用它。它被认为是增长最快的 React 框架之一。

其他人很难与 Next.js 竞争,因为它涵盖了三种不同的页面渲染策略,但自 2021 年 11 月以来,我们似乎有了一个新的、新鲜的、强大的框架,称为Remix。

对不起盖茨比,我没有忘记你,我喜欢你在生成静态站点时的工作方式。

为什么需要 Next.js 或 Remix 而不是普通的 React
也许你没有;这取决于。重点是了解 React 应用程序如何工作,您可能会发现哪些问题以及 Next.js 或 Remix 等框架如何解决这些问题。

关于 React 的要点是您可以制作单页应用程序 (SPA),其中仅使用一个 HTML 文件来呈现所有网页,并且路由保留在客户端。但这到底是什么意思呢?

当发出初始请求时,浏览器会立即收到一个包含所有应用程序的 HTML 框页面。
没有预渲染的内容。
当用户导航触发时,JavaScript 仅替换那些与请求的路由相关的组件和内容,但 HTML 文件保持不变。
简而言之,浏览器负责管理应加载哪个 JavaScript 文件以呈现当前页面。换句话说,客户端呈现 (CSR)。

CSR 可以非常花哨并且有助于创建不需要关心 SEO、缓存或缓慢的初始渲染的应用程序。

SSlow Initial Render——当用户第一次登陆时,可能需要很长时间才能加载第一个内容页面。这意味着让他们得到一个空白页面,直到 JavaScript 加载并呈现所有内容。
SEO——因为内容只有一个 HTML 页面,所以很难让搜索引擎和社交媒体机器人对内容进行索引。
缓存问题——HTML 结构无法缓存,因为它在第一次渲染时不可用。
在这一点上,您可能认为 SPA 是魔鬼,但想想这么多公司使用的所有内部应用程序或他们销售的应用程序产品。

当您想利用 SPA 的工作方式而又不失去我之前提到的三点时,Next.js或Remix就会出现。优点是网站可以在发送到客户端之前在服务器端呈现。

SSR vs SSG vs ISR
我已经解释了 CSR 的含义和工作原理,现在轮到我来谈谈其他花哨的页面呈现策略。

Next.js是一个强大的选项,因为它提供了三个开箱即用的不同选项,而Remix完全依赖于 SSR(目前)。但这些策略究竟是如何运作的呢?

服务器端渲染 (SSR)
当一个页面被请求时,它在发送到客户端之前在服务器上完全呈现。对于那些拥有大量动态数据和内容的网站来说,这是一个很好的选择。

静态站点生成 (SSG)
该策略在构建期间按路由生成所有页面。出现提示时,只会将请求的页面发送给客户端。也就是说,这构建了一个典型的静态网站。

对于那些几乎没有或没有动态数据且页面不多的静态网站来说,它可能是一个合适的选择。数据中的每个更改都会触发所有页面的重新生成。

增量静态再生 (ISR)
Next.js 提供了这个概念,是 SSR 和 SSG 的混合体。

它类似于 SSG,但您可以设置一个间隔时间,让您知道何时应该重新生成您的页面。适用于那些具有动态内容的静态站点。

Next.js 的强项在于它允许您在每个页面上的这三个选项之间切换,因此您可以让每个选项遵循不同的策略。

所以也许你会问自己,“如果 Next.js 只适用于 SSR,我为什么要用 Remix 替换它”。答案很简单,如今,应用程序和网站往往具有动态数据,并且很少有适合 SSG 或 ISR 场景的示例。

考虑到这一点,Remix 提供了一个较低级别的 API。例如,它公开了 Request 对象,因此您可以在呈现页面之前轻松修改标头,而在 Next.js 中,您需要中间件来实现它。

路由
这两个框架都使用基于文件的路由系统来呈现页面。

每个页面都保留在不同的文件中。
每个页面都与基于其文件名的路由相关联。
最后,每个页面都会呈现一个 React 组件。
混音中的路由
您将在 中添加每个路由/app/routes,并且在其中创建的每个文件或文件夹都将被视为一个路由。

混音路由 1
此外,您会发现一个文件,/app/root.jsx,其中是“根路由”,是整个应用程序的布局。它包含 <html>、<head> 和 <body> 标签,以及其他与 Remix 相关的内容。

Next.js 中的路由
您将在 中添加每条路线/pages,它的工作方式与 Remix 完全相同。

nextjs 路由 1
在这种情况下,您可能有也可能没有pages/_app.jsand pages/_document.js。其中App用于初始化页面,文档包含标签。

Next.js 默认使用它们,无需额外配置。但是,如果您需要自定义它们,您可以创建这两个文件,以这种方式覆盖默认文件。

索引路线
他们都使用相同的系统来呈现容器路线。

Next.js 示例:

pages/index.js=> /(根页面)
pages/posts/index.js或者pages/posts.js=>/posts
混音示例:

routes/index.js=> /(根页面)
routes/posts/index.js或者routes/posts.js=>/posts
嵌套路由
Remix 建立在 React Router 之上,由同一个团队开发,所以你可以想象在嵌套路由方面谁是赢家。
其背后的想法是创建一个活动路由来挂载包含多个嵌套路由的布局,以便每个路由管理自己的内容。

如果我必须用 React 语言来解释它,我会告诉你想象每个嵌套路由都是一个组件,并且根据 URL 路径,它们可以被挂载和卸载。

为实现这一点,Remix 使用了<Outlet />React-router 组件。它用在父路由上,告诉它们只要被触发就渲染子路由元素。
请记住,所有这些嵌套路由都已预加载到服务器上,因此几乎每个加载状态都可以删除。听起来不像是 SSR 和 SPA 的混合体?

另一方面,Next.js 确实支持嵌套路由,但它们作为单独的页面工作。如果您想实现类似于 Remix 所做的事情,您可能应该自定义您的_app.js.

两者的示例:

routes/posts/news/index.js=>/posts/news
动态路线
它们通过基于动态的 URL 参数呈现不同的内容来工作,但它使用单个路由文件。
在这种情况下,两个框架都支持此功能,但使用不同的命名约定。

Next.js 示例:

pages/posts/[postId].js=>/posts/some-post-slug
pages/products/[category]/[productId].js=>/products/keyboards/some-keyboard-slug或/products/headphones/some-headphone-slug
它有自己的钩子,useRouter将为您提供那些动态参数(postId、类别、productId)的当前值。

混音示例:

/app/routes/posts/$postId.js=>/posts/some-post-slug
/app/routes/products/$category/$productId.js=>/products/keyboards/some-keyboard-slug或/products/headphones/some-headphone-slug
您可以使用useParamReact Router hook 来访问这些动态参数。

需要加载文件的路由怎么办?
想象一下,您需要您的网站包含典型文件robots.txt和sitemap.xml文件。

在 Next.js 和 Remix 中,您都可以将它们添加到 /public 目录。但是 Remix 可以让你通过路由系统实现它,只需创建一个/app/routes/[sitemap.xml].js或/app/routes/[robots.txt].js

数据加载
这两个框架都是服务器端的,因此每个框架都有不同的系统来获取数据和手动调用 API。

对于 Next.js,您可以在两个选项中进行选择,具体取决于您要构建的页面类型。

getStaticProps(SSG,如果设置了重新验证间隔,则为 ISR)——它在构建时获取数据并将其数据作为页面的组件属性提供。
export async function getStaticProps( {params} ) {
const productList = await getProductList()

return {
props: {
productList,
slug: params.slug
}
}
}


getServerSideProps (SSR) — 它在运行时在服务器端获取数据,并将返回的数据作为页面的组件道具提供。
export async function getStaticProps( {params} ) {
const productList = await getProductList()

return {
props: {
productList,
slug: params.slug
}
}
}


/**
* Here you would have your own getStaticProps/getServerSideProps functions
* depending on what system you choose.
**/

const PageName = (props)=> {
const { productList } = props
// Here you could render the requested data
return (<div>
<pre> {JSON.stringify(productList, null, 4)} </pre>
</div>)
}


Remix 使用不同的方式加载数据;

它为您提供了一个loader在每个路由文件上使用的函数,该文件将在运行时在服务器端运行。
在页面组件本身上,您将能够使用useLoaderData挂钩来收集这些数据。
/**
* Here you would have your own getStaticProps/getServerSideProps functions
* depending on what system you choose.
**/
export const async loader = ( {params} ) => {
const productList = await getProductList()
return {
productList,
slug: params.slug
}
};

export default function PageName() {
const { productList } = useLoaderData();

// Here you could render the requested data
return (<div>
<pre> {JSON.stringify(productList, null, 4)} </pre>
</div>)
}


数据突变
Next.js 没有内置的方式来执行突变,你必须手动进行。
另一方面,Remix 创建了一个表单组件,并像使用浏览器的本机 HTML 表单元素一样使用它。如果您不输入任何操作 URL,它将对表单使用相同的路由。

如果方法是GET,loader将触发导出函数,如果方法是POST,action将触发定义在组件中的导出函数。

此外,您可以使用提供的 useTransition 挂钩根据请求状态管理应用程序的行为,而无需像往常一样手动管理它。

export const loader = async ({ params }) => {
// Each time this page receive a GET Request, this loader will be executed
const userData = await getSomeUserData(params.slug)

return {
userData // it contains the User Name
}
}

export const action = async ({ request }) => {
// Each time this page receive a POST Request, this loader will be executed
const form = await request.formData()
const content = form.get('content')

return redirect('/')
}

export default function App() {
const { name } = useLoaderData();

return (
<div>
<div>{`Hello there ${name}`}</div>

<Form method="POST">
<label htmlFor="content">
Content: <textarea name="content" />
</label>

<input type="submit" value="Add New" />
</Form>
</div>
)
}


造型
Next.js 带有开箱即用的内置 CSS 支持,因此您可以直接将其导入到 /pages/_app.js 所需的任何 style.css 文件中。

您还可以添加其他 CSS 框架,使用一些配置或插件很容易实现。

linksRemix 更喜欢通过提供组件和导出功能来专注于对 Web 标准更加友好的解决方案。

import styles from "./styles/app.css";

export function links() {
return [
{ rel: "stylesheet", href: styles },
{ rel: "stylesheet", href: 'https://.....' },
];
}

export default function App() {
return (
<html lang="en">
<head>
<Links />
</head>
<body>
<Outlet />
</body>
</html>
);
}


您可能认为仅仅加载一个样式表有点过分了,但是如果我告诉您可以在每个页面上单独使用它,让您仅在需要时加载特定样式/字体怎么办?

另外,在这里你可以使用其他的 CSS 框架,只需很少的配置,比如 Tailwindcss。

图像优化
Next.js 在这里获胜,因为它有自己的内置组件next/image,可以实现图像优化、延迟加载、大小控制和集成加载器。不幸的是,目前 Remix 没有任何图像优化支持。

搜索引擎优化
在本文的开头,我告诉过您这些框架背后的要点之一是解决 SPA 的 SEO 问题。

它们都有自己的内置机制来动态管理每个页面上应该使用哪些元数据,例如关键字、标题、描述等。

next/head在 Next.js 中,您可以通过将其导入页面/路由来使用其内置组件。

import Head from 'next/head'

export default function PostPage() {
return (
<div>
<Head>
<title>Post Page Title</title>
<meta name="description" content="Post Page Description" />
</Head>
<p>
All the metadata inside Head component will be injected into the
documents head
</p>
</div>
)
}


另一方面,Remix 为我们提供了一个meta导出函数,可以接收页面的请求数据以及 URL 参数。

export const meta = ({ data, params }) => {
charset: "utf-8",
viewport: "width=device-width,initial-scale=1",
title: `Post | ${data.title}`,
description: data.description,
};

export default function PostPage() {
return (
<div>
<p>
All the metadata returned on meta function will be injected into the
documents head
</p>
</div>
)
}


错误处理
Next.js 使您可以在捕获某些错误时定义自定义页面,例如 400 或 500 错误。但是除了路由或状态错误之外的任何其他错误都会导致页面中断。

Remix 让您尽情发挥想象力,这样您就可以覆盖和自定义您想要的每个错误。他们添加了错误边界,这是一种处理错误的独特概念——在 React v16 中引入。

您可以通过在您的甚至每个页面上使用CatchBoundary, 和ErrorBoundary导出的函数轻松地覆盖这些错误。root.tsx让我们添加一些视觉示例。

// Here you will catch any controlled error and will tell the framework what information should be shown
const CatchBoundary = () => {
let caught = useCatch();

switch (caught.status) {
case 401:
// Document is a custom React Component which contains <html>, <head>,<body>, etc
return (
<Document title={`${caught.status} ${caught.statusText}`}>
<h1>
{caught.status} {caught.statusText}
</h1>
</Document>
);

case 404:
return <PageNotFoundComponent />; // Yes, you can also use a React Component
default:
throw new Error(
`Unexpected caught response with status: ${caught.status}`
);
}
};

// Here you will have a more generic advise for when an uncontrolled error has been triggered
const ErrorBoundary = ( { error } ) => {
return (
<Document title="Uh-oh!">
<h1>App Error</h1>
<pre>{error.message}</pre>
<p>
Replace this UI with what you want users to see when your app throws
uncaught errors.
</p>
</Document>
);
};

export const loader: LoaderFunction = async ({ params }) => {
const post = await getPost(params.slug);

if (!post) {
throw new Response("Not Found", {
status: 404,
});
}

return json({ post });
};

export default function PostPage() {
const { post } = useLoaderData();
return (
<div>
<pre>
{JSON.stringify(post, null, 4)}
</pre>
</div>
)
}


部署
Next.js 很容易部署在任何支持 Node.js 的服务器上。它还集成了无服务器部署到 Vercel 的功能,Netlify 有自己的适配器,因此您可以轻松部署应用程序。

Remix 基于 Web Fetch API 而不是 Node.js 构建,因此它可以在 Vercel、Netlify、Architect(Node.js 服务器)以及 Cloudflare 等非 Node.js 环境中运行。

它还会在您开始项目时为您提供开箱即用的适配器,因此如果您知道要将项目部署到哪里,就可以直奔主题。

Remix 最重要的事情之一是它不再使用 Webpack。相反,它使用 Esbuild,它在捆绑和部署方面速度极快。

其他提及
每当您想选择 Next.js 或 Remix 时,还有其他要点需要考虑,但我想收集那些在我看来作为开发人员在开发项目时具有更大影响的因素。
但如果您想了解更多,这里有一些我在本文中没有谈到的事情的回顾。

实时重新加载。Next.js 使用热模块重新加载 (HMR) 并默认启用,而在 Remix 上,您必须在 root.tsx 上添加一个组件以强制您的应用程序在代码更改时重新加载。
Remix 支持 Cookie、会话和身份验证,而 Next.js 不支持——您必须使用外部库。
Next.js 内置了对字体和脚本优化的支持,而 Remix 则没有。
两者都让您开箱即用地使用 Typescript。
Remix 无需执行 JavaScript 即可完成其大部分功能,而 Next.js 则不会。
Remix 可以在嵌套路由中包含独立路由
两者都可以使用 Tailwindcss 快速工作,并有自己的指南来实现它。
Next.js 内置了对国际化路由的支持。
Next.js 对 AMP 具有开箱即用的支持,而 Remix 则没有。

发表评论:

控制面板
您好,欢迎到访网站!
  查看权限
网站分类
最新留言