Contents

Remix 快速入门

Remix 快速入门

Remix 是一个比较新的全栈 Web 框架, 它基于 React 技术栈, 类似 Next.js. 本文仅介绍 Remix 的基本使用与一些开发过程可能遭遇的一些场景进行展开, 将同 Remix 文档的快速开始一样意简言骇.

创建项目

Remix 至少需要 nodejs 版本为 ^14.17.0>=16.0.0.

初始化一个新的 Remix 项目可以使用 npxyarn create:

npx:

npx create-remix --template remix-run/indie-stack your-project-name

yarn:

yarn create remix --template remix-run/indie-stack your-project-name

运行后会提示选择 Typescript 或 Javascript:

TypeScript or JavaScript? (Use arrow keys)

建议使用 Typescript 保证代码健壮性.

继续将会提示是否 install, 完成安装后让我们启动开发服务器:

yarn dev

路由

相关文档

Remix 的路由与 Next.js 类似, 也是通过约定自动生成. 当你在 app/routes/ 文件夹下创建 tsx/jsx 文件时, 它会自动生成对应的路由.

例如 app\routes\help\index.tsx 对应路由: website.com/help, 而动态路由 app\routes\help\$id.tsx 对应路由: website.com/help/${queryId}. app/root.tsx 为根节点.

Remix 还支持嵌套路由, 在父级页面使用 <Outlet> 组件渲染.

举个例子父组件还是用 app\routes\help\index.tsx, 内部结构为:

app\routes\help\index.tsx

export default function Help() {
  return (
    <PageWrapper>
      <Header />
      <Main>
        <Outlet />
      </Main>
    </PageWrapper>
  );
}

子组件即可继承外部样式与布局直接渲染数据:

app\routes\help$id.tsx

export default function HelpItem() {
  const { id } = useParams();
  const { data } = useLoaderData<{ data: Data }>();

  if (!data.length) return <Empty type='error' title='暂无数据!' />;

  return data.map((item, i) => <Item {...item} />);
}

配置链接与 meta 信息

在开发过程中我们可能会使用外部的链接或全局 CSS 样式, remix 通过导出默认方法 links 创建链接:

// 链接集合
export const links: LinksFunction = () => [
  { rel: 'stylesheet', href: GlobalStyle },
  { rel: 'icon', href: '/help/favicon.ico' },
];

SSR 最初就是为了解决 SEO 而诞生的, 所以 meta 信息的配置必不可少. 同上通过导出默认方法 meta 创建 meta 信息配置:

// 常见的元信息集合
export const meta: MetaFunction = () => ({
  charset: 'utf-8',
  title: 'AntPro',
  keywords: 'AntPro',
});

CSS-in-JS libraries 配置

建议使用 @emotion/styled, 开箱即用无需配置且没有水合警告.

使用 CSS-in-JS 库, 例如 Styled Components 需要"双重渲染", 以便在服务器渲染期间从组件树中提取样式, 并且我们需要在 app/root.tsx 根组件中放置一个占位符来控制样式插入:

app/root.tsx

export default function App() {
  return (
    <html lang='en'>
      <head>
        <Meta />
        <Links />
        {typeof document === 'undefined' ? '__STYLES__' : null}
      </head>
      <body>
        <Outlet />
        <ScrollRestoration />
        <Scripts />
        <LiveReload />
      </body>
    </html>
  );
}

然后在控制 HTTP 响应的文件 app/entry.server.tsx 渲染样式, 例如:

import type { EntryContext } from '@remix-run/node';
import { RemixServer } from '@remix-run/react';
import { renderToString } from 'react-dom/server';
import { ServerStyleSheet } from 'styled-components';

export default function handleRequest(
  request: Request,
  responseStatusCode: number,
  responseHeaders: Headers,
  remixContext: EntryContext
) {
  const sheet = new ServerStyleSheet();
  // 渲染html为string
  let markup = renderToString(sheet.collectStyles(<RemixServer context={remixContext} url={request.url} />));

  const styles = sheet.getStyleTags();
  // 替换占位符
  markup = markup.replace('__STYLES__', styles);
  responseHeaders.set('Content-Type', 'text/html');

  return new Response('<!DOCTYPE html>' + markup, {
    status: responseStatusCode,
    headers: responseHeaders,
  });
}

使用 Styled Components 时可能会遇到水合警告, 这个不会影响页面的样式展示.

数据获取

remix 基于 Web Fetch API 建立, 通过在 server 端获取数据来完成 browser 的快速渲染.

在 server 端获取数据后通过导出 loader 加载器将数据推送至页面, 再通过 useLoaderData 获取, 例如刚刚的 help\$id.tsx 组件, 它就通过 useLoaderData 数据来获取数据 data:

app\routes\help$id.tsx

// 服务端获取数据后将数据推送至页面
export const loader: LoaderFunction = async ({ params }) => {
  if (!params.id) return [];
  try {
    return getHelpItem(params.id);
  } catch (error) {
    return [];
  }
};

export default function HelpItem() {
  const { id } = useParams();
  // 获取数据
  const { data } = useLoaderData<{ data: Data }>();

  if (!data.length) return <Empty type='error' title='暂无数据!' />;

  return data.map((item, i) => <Item {...item} />);
}

相关链接

Remix 官方文档 React