最新国产好看的视频,伊人天堂AV在线,国产Aaaaaa视频,蜜臀视频在线观看一区,人妻av色图,密臀久久久精品影片,青青视频免费观看毛片,久草在线观看视,国产三级精品色情在线

Remix集成antd和pro-components的過程示例

 更新時間:2023年03月24日 11:18:38   作者:喬治_x  
這篇文章主要為大家介紹了Remix集成antd和pro-components的過程示例詳解,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進(jìn)步,早日升職加薪

正文

讀者如果嘗試過 Remix 那么覺得 Remix 頁面與路由用的真的很舒服(簡單易用結(jié)構(gòu)清晰),但是有一個問題,目前 Remix 項(xiàng)目集成了 antd/pro-components 等 國內(nèi)UI 組件庫好模板示例很少,于是想創(chuàng)建一個集成 Remix 與 antd 生態(tài)的組件庫模板,能更快的體驗(yàn)并創(chuàng)建具有 antd 生態(tài)的 remix 項(xiàng)目。

閱讀本文需要 React/Remix 基礎(chǔ)知識和服務(wù)端渲染的相關(guān)知識

要注意的問題

核心要注意的問題就是:

問題說明
模塊(包)兼容性和 peer 依等問題
ssrRemix 服務(wù)端渲染支持問題

兼容性

兼容性主要體現(xiàn)在 React18 和其他的包的兼容性

  • 使用腳手架創(chuàng)建的項(xiàng)目默認(rèn)使用 React 18,由此帶來兼容性問題?
  • React 18 api 發(fā)生了變化,渲染 api 調(diào)用是否手動修改為 React18 的方式?
  • npm 的 peer 依賴安裝與否?
  • 其他的依賴的兼容 React 18 的問題?

Remix 服務(wù)端渲染的支持情況

我們知道 Remix 其實(shí)基于 esbuild 很多代碼都跑在服務(wù)端,所以服務(wù)端的渲染的注意點(diǎn)是我們要提前知道:

  • antd 支持服務(wù)端渲染
  • pro-components 不支持服務(wù)端渲染,一般用于客戶渲染,因?yàn)橹苯邮褂昧?window/document 等客戶端才有的全局對象
  • remix-utils 工具包支持 <ClientOnly>{() => <>You Content</>}</ClientOnly> 使用組件僅僅在客戶端進(jìn)行渲染。

初始化項(xiàng)目安裝必要的包

pnpm dlx create-umi@latest [your_package_name]
# remix 選擇默認(rèn)的選項(xiàng)即可
pnpm install remix-utils antd @ant-design/pro-components @ant-design/cssinjs @ant-design/icons

使用新特性 v2 版本的文件路由模式

  • remix.config.js
/** @type {import('@remix-run/dev').AppConfig} */
module.exports = {
  future: {
    v2_routeConvention: true,
  },
  ignoredRouteFiles: ["**/.*"],
};

添加 pro-components SettingDrawer 組件上下文

import { createContext } from "react";
const SettingContext = createContext({
  theme: {},
  setTheme: (theme: any) => {}
});
export default SettingContext;

全局配置放在 SettingContext 上下文中,需要修改和使用都基于此上下文。

root 文件修改

// type
import type { MetaFunction } from "@remix-run/node";
// core
import {
  Links,
  LiveReload,
  Meta,
  Outlet,
  Scripts,
  ScrollRestoration,
} from "@remix-run/react";
export const meta: MetaFunction = () => ({
  charset: "utf-8",
  title: "New Remix App",
  viewport: "width=device-width,initial-scale=1",
});
function Document({
  children,
  title = "App title",
}: {
  children: React.ReactNode;
  title?: string;
}) {
  return (
    <html lang="en">
      <head>
        <Meta />
        <title>{title}</title>
        <Links />
        {typeof document === "undefined" ? "__ANTD__" : ""}
      </head>
      <body>
        {children}
        <ScrollRestoration />
        <Scripts />
        <LiveReload />
      </body>
    </html>
  );
}
export default function App() {
  return (
    <Document>
      <Outlet />
    </Document>
  );
}
  • 將 html 單獨(dú)的抽離一個 Document 組件,方便日后修改
  • 在 Document 組建中增加 __ANTD__ 方便后期替換 antd 客戶端內(nèi)容

增加客戶端渲染入口文件:entry.client.tsx

客戶端主要配合: @ant-design/cssinjs

// cores
import { startTransition, useState } from "react";
import { hydrateRoot } from "react-dom/client";
import { RemixBrowser } from "@remix-run/react";
// components and others
import { createCache, StyleProvider } from "@ant-design/cssinjs";
import { ConfigProvider } from "antd";
// context
import SettingContext from "./settingContext";
const hydrate = () => {
  startTransition(() => {
    const cache = createCache();
    function MainApp() {
      const [theme, setTheme] = useState({
        colorPrimary: "#00b96b"
      });
      return (
        <SettingContext.Provider value={{ theme, setTheme }}>
          <StyleProvider cache={cache}>
            <ConfigProvider
              theme={{
                token: {
                  colorPrimary: theme.colorPrimary,
                },
              }}
            >
              <RemixBrowser />
            </ConfigProvider>
          </StyleProvider>
        </SettingContext.Provider>
      );
    }
    hydrateRoot(document, <MainApp />);
  });
};
if (typeof requestIdleCallback === "function") {
  requestIdleCallback(hydrate);
} else {
  // Safari doesn't support requestIdleCallback
  // https://caniuse.com/requestidlecallback
  setTimeout(hydrate, 1);
}

定義 theme, setTheme 給 SettingContext 使用控制 antd 配置變化,要說明的點(diǎn) StyleProvider 是用于 antd 服務(wù)端渲染 配置, 而 ConfigProvider 是 antd 主題配置的提供者。

  • 注意:React18 中不能使用 hydrateRoot api 來進(jìn)行水合。

增加服務(wù)端渲染入口文件:entry.server.tsx

與 客戶端一樣需要 @ant-design/cssinjs 來配置 antd 的樣式。

// types
import type { EntryContext } from "@remix-run/node";
// core
import { useState } from "react";
import { RemixServer } from "@remix-run/react";
import { renderToString } from "react-dom/server";
// components
import { ConfigProvider } from "antd";
import { createCache, extractStyle, StyleProvider } from "@ant-design/cssinjs";
// context
import SettingContext from "./settingContext";
export default function handleRequest(
  request: Request,
  responseStatusCode: number,
  responseHeaders: Headers,
  remixContext: EntryContext
) {
  const cache = createCache();
  function MainApp() {
    const [theme, setTheme] = useState({
      colorPrimary: "#00b96b"
    });
    return (
      <SettingContext.Provider value={{ theme, setTheme }}>
        <StyleProvider cache={cache}>
          <ConfigProvider
            theme={{
              token: {
                colorPrimary: theme.colorPrimary,
              },
            }}
          >
            <RemixServer context={remixContext} url={request.url} />
          </ConfigProvider>
        </StyleProvider>
      </SettingContext.Provider>
    );
  }
  let markup = renderToString(<MainApp />);
  const styleText = extractStyle(cache);
  markup = markup.replace("__ANTD__", styleText);
  responseHeaders.set("Content-Type", "text/html");
  return new Response("<!DOCTYPE html>" + markup, {
    status: responseStatusCode,
    headers: responseHeaders,
  });
}

客戶端和服務(wù)端的改造中包含了:

markup = markup.replace("__ANTD__", styleText);
{typeof document === "undefined" ? "__ANTD__" : ""}

__ANTD__ 在服務(wù)端環(huán)境中替換

創(chuàng)建一個布局用于承載 pro-components 組件

  • /routes/_layout.tsx
// core
import { useContext } from "react";
import { Outlet } from "@remix-run/react";
// components
import { ClientOnly } from "remix-utils";
import { ProConfigProvider, SettingDrawer } from "@ant-design/pro-components";
// context
import SettingContext from "~/settingContext";
export default function Layout() {
  const value = useContext(SettingContext);
  return (
    <ClientOnly fallback={<div>Loading...</div>}>
      {() => (
        <ProConfigProvider>
          <Outlet />
          <SettingDrawer
            getContainer={() => document.body}
            enableDarkTheme
            onSettingChange={(settings: any) => {
              value?.setTheme(settings);
            }}
            settings={{ ...value.theme }}
            themeOnly
          />
        </ProConfigProvider>
      )}
    </ClientOnly>
  );
}

注意:布局組件中使用有以下幾個點(diǎn)需要注意:

  • useContext 獲取當(dāng)前的上下文
  • ClientOnly 組件用于僅僅在客戶端渲染 Remix 組件
  • ProConfigProvider 組件為 SettingDrawer/Outlet 組件提供上下文
  • SettingDrawer 給使用當(dāng)前布局 _layout 的組件提供顏色等配置

使用 antd 創(chuàng)建一個簡單的基于 _layout._index.tsx 頁面

// core
import { json } from "@remix-run/node";
import { useFetcher } from "@remix-run/react";
// components
import { Button, Form, Input, Select } from "antd";
export async function action() {
  return json({
    title: 1,
  });
}
const { Option } = Select;
const layout = {
  labelCol: { span: 8 },
  wrapperCol: { span: 16 },
};
const tailLayout = {
  wrapperCol: { offset: 8, span: 16 },
};
export default function Index() {
  const fetcher = useFetcher();
  const [form] = Form.useForm();
  const onGenderChange = (value: string) => {
    switch (value) {
      case "male":
        form.setFieldsValue({ note: "Hi, man!" });
        break;
      case "female":
        form.setFieldsValue({ note: "Hi, lady!" });
        break;
      case "other":
        form.setFieldsValue({ note: "Hi there!" });
        break;
      default:
    }
  };
  const onFinish = (value: any) => {
    const formData = new FormData();
    formData.append("username", value.username);
    formData.append("password", value.password);
    fetcher.submit(formData, { method: "post" });
  };
  const onReset = () => {
    form.resetFields();
  };
  const onFill = () => {
    form.setFieldsValue({ note: "Hello world!", gender: "male" });
  };
  return (
    <div>
      <Form
        {...layout}
        form={form}
        name="control-hooks"
        onFinish={onFinish}
        style={{ maxWidth: 600 }}
      >
        <Form.Item name="note" label="Note" rules={[{ required: true }]}>
          <Input />
        </Form.Item>
        <Form.Item name="gender" label="Gender" rules={[{ required: true }]}>
          <Select
            placeholder="Select a option and change input text above"
            onChange={onGenderChange}
            allowClear
          >
            <Option value="male">male</Option>
            <Option value="female">female</Option>
            <Option value="other">other</Option>
          </Select>
        </Form.Item>
        <Form.Item
          noStyle
          shouldUpdate={(prevValues, currentValues) =>
            prevValues.gender !== currentValues.gender
          }
        >
          {({ getFieldValue }) =>
            getFieldValue("gender") === "other" ? (
              <Form.Item
                name="customizeGender"
                label="Customize Gender"
                rules={[{ required: true }]}
              >
                <Input />
              </Form.Item>
            ) : null
          }
        </Form.Item>
        <Form.Item {...tailLayout}>
          <Button type="primary" htmlType="submit">
            Submit
          </Button>
          <Button htmlType="button" onClick={onReset}>
            Reset
          </Button>
          <Button type="link" htmlType="button" onClick={onFill}>
            Fill form
          </Button>
        </Form.Item>
      </Form>
    </div>
  );
}

_layout._index.tsx 表示使用:_layout 布局的 / 頁面路由。

使用 pro-component 創(chuàng)建一個簡單的基于 _layout._procomponents.tsx 頁面

// core
import { json } from "@remix-run/node";
import { useFetcher } from "@remix-run/react";
// components
import { Button, Form, Space } from "antd";
import {
  ProForm,
  ProFormDependency,
  ProFormSelect,
  ProFormText,
} from "@ant-design/pro-components";
export async function action() {
  return json({
    title: 1,
  });
}
const layout = {
  labelCol: { span: 8 },
  wrapperCol: { span: 16 },
};
const tailLayout = {
  wrapperCol: { offset: 8, span: 16 },
};
export default function Index() {
  const fetcher = useFetcher();
  const [form] = Form.useForm();
  const onGenderChange = (value: string) => {
    switch (value) {
      case "male":
        form.setFieldsValue({ note: "Hi, man!" });
        break;
      case "female":
        form.setFieldsValue({ note: "Hi, lady!" });
        break;
      case "other":
        form.setFieldsValue({ note: "Hi there!" });
        break;
      default:
    }
  };
  const onFinish = (value: any) => {
    const formData = new FormData();
    formData.append("username", value.username);
    formData.append("password", value.password);
    fetcher.submit(formData, { method: "post" });
  };
  const onReset = () => {
    form.resetFields();
  };
  const onFill = () => {
    form.setFieldsValue({ note: "Hello world!", gender: "male" });
  };
  return (
    <div>
      <Form
        {...layout}
        form={form}
        name="control-hooks"
        onFinish={onFinish}
        style={{ maxWidth: 600 }}
      >
        <ProFormText name="note" label="Note" rules={[{ required: true }]} />
        <ProFormSelect
          name="gender"
          label="Gender"
          rules={[{ required: true }]}
          fieldProps={{
            onChange: onGenderChange
          }}
          options={[
            {
              label: "male",
              value: "male",
            },
            {
              label: "female",
              value: "female",
            },
            {
              label: "other",
              value: "other",
            },
          ]}
        />
        <ProFormDependency name={["gender"]}>
          {({ gender }) => {
            return gender === "other" ? (
              <ProFormText
                noStyle
                name="customizeGender"
                label="Customize Gender"
                rules={[{ required: true }]}
              />
            ) : null;
          }}
        </ProFormDependency>
        <ProForm.Item {...tailLayout}>
          <Space>
            <Button type="primary" htmlType="submit">
              Submit
            </Button>
            <Button htmlType="button" onClick={onReset}>
              Reset
            </Button>
            <Button type="link" htmlType="button" onClick={onFill}>
              Fill form
            </Button>
          </Space>
        </ProForm.Item>
      </Form>
    </div>
  );
}

/procomponents 頁面基本是 / 頁面使用 pro-components 的改造版本。需要我們注意的是 表單聯(lián)動 使用用方式不一樣。

  • 項(xiàng)目地址

到目前為止基于 antd 的項(xiàng)目 remix 已經(jīng)探究出一部分,對應(yīng) remix antd 感興趣可訪問 create-remix-antd-pro-app 該項(xiàng)目托管在 Github。

小結(jié)

到這里就在 Remix 中就集成了 antd/pro-components 組件庫就基本結(jié)束了

  • 核心還是使用 ClientOnly 在客戶端渲染組件。
  • 提供了 SettingDrawer 更換當(dāng)前主題的功能。
  • 核心難點(diǎn): 庫之間的兼容性問題的解決方案或者替代方案

以上就是Remix集成antd和pro-components的過程示例的詳細(xì)內(nèi)容,更多關(guān)于Remix集成antd pro-components的資料請關(guān)注腳本之家其它相關(guān)文章!

相關(guān)文章

  • React中使用axios發(fā)送請求的幾種常用方法

    React中使用axios發(fā)送請求的幾種常用方法

    本文主要介紹了React中使用axios發(fā)送請求的幾種常用方法,主要介紹了get和post請求,具有一定的參考價值,感興趣的可以了解一下
    2021-08-08
  • 詳解三種方式在React中解決綁定this的作用域問題并傳參

    詳解三種方式在React中解決綁定this的作用域問題并傳參

    這篇文章主要介紹了詳解三種方式在React中解決綁定this的作用域問題并傳參,文中通過示例代碼介紹的非常詳細(xì),對大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價值,需要的朋友們下面隨著小編來一起學(xué)習(xí)學(xué)習(xí)吧
    2020-08-08
  • 基于React路由跳轉(zhuǎn)的幾種方式

    基于React路由跳轉(zhuǎn)的幾種方式

    這篇文章主要介紹了React路由跳轉(zhuǎn)的幾種方式,具有很好的參考價值,希望對大家有所幫助。如有錯誤或未考慮完全的地方,望不吝賜教
    2022-07-07
  • React?Flux與Redux設(shè)計及使用原理

    React?Flux與Redux設(shè)計及使用原理

    這篇文章主要介紹了React?Flux與Redux設(shè)計及使用,Redux最主要是用作應(yīng)用狀態(tài)的管理。簡言之,Redux用一個單獨(dú)的常量狀態(tài)樹(state對象)保存這一整個應(yīng)用的狀態(tài),這個對象不能直接被改變
    2023-03-03
  • react顯示文件上傳進(jìn)度的示例

    react顯示文件上傳進(jìn)度的示例

    這篇文章主要介紹了react顯示文件上傳進(jìn)度的示例,幫助大家更好的理解和學(xué)習(xí)使用react,感興趣的朋友可以了解下
    2021-04-04
  • React Native Popup實(shí)現(xiàn)示例

    React Native Popup實(shí)現(xiàn)示例

    本文主要介紹了React Native Popup實(shí)現(xiàn)示例,文中通過示例代碼介紹的非常詳細(xì),對大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價值,需要的朋友們下面隨著小編來一起學(xué)習(xí)學(xué)習(xí)吧
    2022-05-05
  • React-router4路由監(jiān)聽的實(shí)現(xiàn)

    React-router4路由監(jiān)聽的實(shí)現(xiàn)

    這篇文章主要介紹了React-router4路由監(jiān)聽的實(shí)現(xiàn),小編覺得挺不錯的,現(xiàn)在分享給大家,也給大家做個參考。一起跟隨小編過來看看吧
    2018-08-08
  • React之防止按鈕多次點(diǎn)擊事件?重復(fù)提交

    React之防止按鈕多次點(diǎn)擊事件?重復(fù)提交

    這篇文章主要介紹了React之防止按鈕多次點(diǎn)擊事件?重復(fù)提交問題,具有很好的參考價值,希望對大家有所幫助,如有錯誤或未考慮完全的地方,望不吝賜教
    2023-10-10
  • react中使用ant組件庫的modal彈窗報錯問題及解決

    react中使用ant組件庫的modal彈窗報錯問題及解決

    這篇文章主要介紹了react中使用ant組件庫的modal彈窗報錯問題及解決方案,具有很好的參考價值,希望對大家有所幫助。如有錯誤或未考慮完全的地方,望不吝賜教
    2023-03-03
  • 一文掌握React?組件樹遍歷技巧

    一文掌握React?組件樹遍歷技巧

    這篇文章主要為大家介紹了React?組件樹遍歷技巧的掌握,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進(jìn)步,早日升職加薪
    2023-04-04

最新評論

大同县| 湄潭县| 随州市| 海南省| 广平县| 女性| 南康市| 赫章县| 华蓥市| 密山市| 克拉玛依市| 明光市| 南通市| 青州市| 安丘市| 宝兴县| 罗甸县| 南康市| 周口市| 西城区| 华容县| 襄垣县| 博湖县| 长葛市| 黑河市| 文安县| 乌兰察布市| 定州市| 石棉县| 阳西县| 略阳县| 洱源县| 扎赉特旗| 武平县| 专栏| 绥阳县| 庆云县| 连云港市| 八宿县| 林甸县| 左云县|