For AI agents: the complete documentation index is available at /zh/llms.txt, the full documentation bundle is available at /zh/llms-full.txt, and this page is available as Markdown at /zh/guide/solution/reactlynx.md.
close
  • 简体中文
  • ReactLynx

    在本文档中,你将学习如何使用 Rslib 构建用于 Lynx 应用的 ReactLynx 组件库,你可在 示例 中查看 ReactLynx 相关演示项目。

    创建 ReactLynx 项目

    你可以使用 @lynx-js/create-lynx 创建基于 Rslib 的 ReactLynx 组件库:

    npm
    yarn
    pnpm
    bun
    deno
    npm create @lynx-js/lynx@latest

    然后,当提示 "Select build tool" 时选择 Rslib,再选择 TypeScript 或 JavaScript。你也可以直接指定 Rslib 模板:

    npm
    yarn
    pnpm
    bun
    deno
    npm create @lynx-js/lynx@latest my-lib -- --template rslib-react-ts

    在现有 Rslib 项目中使用

    开发 ReactLynx 组件,需要在 rslib.config.ts 中设置 target"web"。这一点至关重要,因为 Rslib 默认将 target 设置为 "node",这与 Rsbuild 的 target 默认值不同。

    此外,ReactLynx 组件库通常需要在产物中保留 JSX 语法,交给应用侧的 ReactLynx 编译器根据目标环境和构建配置进行处理。你可以注册 Rsbuild React 插件,通过 swcReactOptionsruntime 设置为 'preserve',并将 bundle 设置为 false,启用 bundleless 构建。同时,将 output.filename 中的 js 设置为 '[name].jsx',输出 .jsx 后缀的文件。

    例如,在 rslib.config.ts 中配置:

    rslib.config.ts
    import { 
    function defineConfig<const Config extends RslibConfig, const Definition extends Config | ((env: ConfigParams) => Config) | ((env: ConfigParams) => Promise<Config>)>(config: Definition & (Definition extends (...args: never[]) => infer CallbackResult ? [Awaited<CallbackResult>] extends [RslibConfig] ? unknown : never : RslibConfig & Record<Exclude<keyof Definition, keyof RslibConfig>, never>)): Definition extends (...args: never[]) => infer CallbackResult ? [CallbackResult] extends [RslibConfig] ? RslibConfigSyncFn : RslibConfigAsyncFn : RslibConfig (+4 overloads)

    This function helps you to autocomplete configuration types. It accepts a Rslib config object, or a function that returns a config.

    defineConfig
    } from '@rslib/core';
    import {
    const pluginReact: (options?: PluginReactOptions) => RsbuildPlugin
    pluginReact
    } from '@rsbuild/plugin-react';
    export default
    defineConfig<RslibConfig, {
        readonly bundle: false;
        readonly output: {
            readonly target: "web";
            readonly filename: {
                readonly js: "[name].jsx";
            };
        };
        readonly plugins: [RsbuildPlugin];
    }>(config: {
        readonly bundle: false;
        readonly output: {
            readonly target: "web";
            readonly filename: {
                readonly js: "[name].jsx";
            };
        };
        readonly plugins: [RsbuildPlugin];
    } & RslibConfig & Record<never, never>): RslibConfig (+4 overloads)

    This function helps you to autocomplete configuration types. It accepts a Rslib config object, or a function that returns a config.

    defineConfig
    ({
    bundle: false

    Whether to bundle the library.

    @defaultValuetrue@seehttps://rslib.rs/config/lib/bundle
    bundle
    : false,
    output: {
        readonly target: "web";
        readonly filename: {
            readonly js: "[name].jsx";
        };
    } & RslibOutputConfig

    Options for build outputs.

    @inheritdoc
    output
    : {
    target: "web"

    Setting the build target for Rsbuild.

    @override@default'node'
    target
    : 'web',
    filename: {
        readonly js: "[name].jsx";
    } & FilenameConfig

    Sets the filename of output files.

    filename
    : {
    js: "[name].jsx" | ("[name].jsx" & ((pathData: PathData, assetInfo?: AssetInfo) => string))

    The name of the JavaScript files.

    @default

    - dev: '[name].js'

    • prod: '[name].[contenthash:10].js'
    js
    : '[name].jsx',
    }, },
    plugins: [RsbuildPlugin] & RsbuildPlugins

    Configure Rsbuild plugins.

    plugins
    : [
    function pluginReact(options?: PluginReactOptions): RsbuildPlugin
    pluginReact
    ({
    swcReactOptions?: ReactConfig | undefined

    Configure the behavior of SWC to transform React code, the same as SWC's jsc.transform.react.

    swcReactOptions
    : {
    ReactConfig.runtime?: "automatic" | "classic" | "preserve" | undefined

    Decides which runtime to use when transforming JSX.

    • "automatic" - Automatically imports the functions that JSX transpiles to. This is the modern approach introduced in React 17+ that eliminates the need to manually import React in every file that uses JSX.
    • "classic" - Uses the traditional JSX transform that relies on React.createElement calls. Requires React to be in scope, which was the standard behavior before React 17.
    • "preserve" - Leaves JSX syntax unchanged without transforming it.
    @default"classic"
    runtime
    : 'preserve',
    }, }), ], });

    TypeScript

    对于使用 TypeScript 的 ReactLynx 项目,在 tsconfig.json 中设置 "jsx": "preserve""jsxImportSource": "@lynx-js/react",并在 types 中添加 @lynx-js/types

    tsconfig.json
    {
      "compilerOptions": {
        "jsx": "preserve",
        "jsxImportSource": "@lynx-js/react",
        "types": ["@lynx-js/types", "@rslib/core/types"]
      }
    }

    rslib.config.ts 中设置 dtstrue,可以生成组件库的类型声明。

    输出产物

    package.json 中配置 .jsx 入口和类型声明入口,并将 ReactLynx 及其类型依赖声明为 peer 依赖:

    package.json
    {
      "name": "reactlynx-scroll-list",
      "type": "module",
      "exports": {
        ".": {
          "types": "./dist/index.d.ts",
          "default": "./dist/index.jsx"
        }
      },
      "types": "./dist/index.d.ts",
      "files": ["dist"],
      "peerDependencies": {
        "@lynx-js/react": ">=0.100.0",
        "@lynx-js/types": ">=4",
        "@types/react": ">=19"
      }
    }

    测试组件

    你可以使用 Rstest 测试 ReactLynx 组件。首先,安装测试所需的依赖:

    npm
    yarn
    pnpm
    bun
    deno
    npm add @rstest/core @rstest/adapter-rslib @lynx-js/react-rsbuild-plugin @testing-library/dom @testing-library/jest-dom happy-dom -D

    通过 @rstest/adapter-rslibwithRslibConfig 函数复用 Rslib 配置,详见 使用 Rstest

    同时,使用 @lynx-js/react 提供的 withDefaultConfig 函数加载 ReactLynx 测试预设,并注册 @lynx-js/react-rsbuild-pluginpluginReactLynx 插件来编译 JSX:

    rstest.config.ts
    import { pluginReactLynx } from '@lynx-js/react-rsbuild-plugin'; 
    import { withDefaultConfig } from '@lynx-js/react/testing-library/rstest-config'; 
    import { withRslibConfig } from '@rstest/adapter-rslib';
    import { defineConfig } from '@rstest/core';
    
    export default defineConfig({
      extends: [withDefaultConfig(), withRslibConfig()],
      plugins: [pluginReactLynx()],
    });

    配置完成后,可以从 @lynx-js/react/testing-library 导入 renderscreenfireEvent 等 API,测试组件的渲染与交互。

    具体用法参考 ReactLynx 测试指南,你可在 示例 中查看完整的组件测试项目。

    使用组件库

    在应用中使用

    在 Lynx 应用中,你可以通过包导入 ReactLynx 组件库,也可以将其作为 External Bundle 加载。

    通过包导入

    通过上述配置构建并发布的组件库会保留 JSX,在 Lynx 应用中安装后,可以直接导入使用,由应用侧的 ReactLynx 编译器处理其中的 JSX。例如,使用组件库导出的 ScrollList

    src/App.tsx
    import { ScrollList } from 'reactlynx-scroll-list';
    
    export function App() {
      return <ScrollList />;
    }

    加载 External bundle

    Lynx 应用也可以在运行时按需加载 External Bundle,其中的 JSX 已在 bundle 构建阶段完成编译。创建组件库时,你可以选择可选工具 External Bundle,也可以在初始化命令中通过 --tools external-bundle 启用:

    npm
    yarn
    pnpm
    bun
    deno
    npm create @lynx-js/lynx@latest my-lib -- --template rslib-react-ts --tools external-bundle

    生成的项目包含 rslib.external-bundle.config.*build:external-bundle 脚本。运行以下命令,可以将组件库编译为 dist-external-bundle/<id>.lynx.bundle

    npm
    yarn
    pnpm
    bun
    deno
    npm run build:external-bundle

    加载方式和详细配置请参考 Lynx External Bundle 文档

    在组件库中使用

    你可以在组件库中使用已有的公共组件,构建时保留 JSX,供应用侧统一编译。推荐将公共组件所在的包作为依赖分发;如果需要将依赖代码随包发布,产物可直接使用时可以复制,需要编译代码、样式或调整内部导入时则重新构建。

    类型声明

    通过构建或复制将依赖代码随包发布时,若 .d.ts 文件仍引用该依赖的类型,使用方仍需安装该依赖。可以通过 dts.bundle.bundledPackages 将引用到的类型声明一并打包,例如设置为 ['reactlynx-scroll-list']

    作为依赖分发(推荐)

    将公共组件所在的库声明在 dependenciespeerDependencies 中。Rslib 默认会将这类依赖标记为 external,保留包名导入。

    使用方安装组件库时,包管理器会按声明的依赖关系安装或复用相关包,应用构建时再通过包名导入加载这些组件库,并统一编译其中的 JSX。

    重新构建依赖产物

    需要编译依赖的代码、样式或调整内部导入时,可以使用 Rslib 分别构建当前库和依赖,并通过 output.externals 改写导入路径。

    以一个包含滚动列表和其他组件的组件库为例,滚动列表基于 reactlynx-scroll-list 进行调整,其他组件由你编写。配置分为三个构建项,通过 id 区分:

    • components:以 bundleless 方式构建你编写的其他组件,处理需要保留 JSX 的组件代码。
    • bundled-components:使用 bundle 构建滚动列表入口及其引用的本地 TS/JS 模块,可包含导出调整或其他 TS/JS 逻辑,并通过 output.externals 引用依赖产物。
    • vendor:重新构建 reactlynx-scroll-list 的产物,保留 JSX,并输出到 dist/vendor/reactlynx-scroll-list

    示例仅在 src/scroll-list/index.ts 中导入 reactlynx-scroll-list,其他组件通过本地路径引用该入口:

    src/scroll-list/index.ts
    export { ScrollList } from 'reactlynx-scroll-list';

    outBase 和入口需按依赖的实际产物调整,包含需要处理的代码、样式和静态资源:

    rslib.config.ts
    import { dirname } from 'node:path';
    import { fileURLToPath } from 'node:url';
    import { pluginReact } from '@rsbuild/plugin-react';
    import { defineConfig } from '@rslib/core';
    
    const scrollListDir = dirname(
      fileURLToPath(import.meta.resolve('reactlynx-scroll-list')),
    );
    const reactPlugin = pluginReact({
      swcReactOptions: {
        runtime: 'preserve',
      },
    });
    
    export default defineConfig({
      lib: [
        {
          id: 'components',
          bundle: false,
          dts: true,
          source: {
            entry: {
              index: [
                './src/**/*',
                '!./src/scroll-list/**',
              ],
            },
          },
          plugins: [reactPlugin],
        },
        {
          id: 'bundled-components',
          source: {
            entry: {
              'scroll-list/index': './src/scroll-list/index.ts',
            },
          },
          output: {
            externals: {
              'reactlynx-scroll-list': '../vendor/reactlynx-scroll-list/index.jsx',
            },
          },
        },
        {
          id: 'vendor',
          bundle: false,
          outBase: scrollListDir,
          source: {
            entry: {
              index: `${scrollListDir}/**/*.{js,jsx,css,svg}`,
            },
          },
          output: {
            distPath: './dist/vendor/reactlynx-scroll-list',
          },
          plugins: [reactPlugin],
        },
      ],
      output: {
        target: 'web',
        filename: {
          js: '[name].jsx',
        },
      },
    });

    通过顶层 output.filename 将各构建项的代码产物统一输出为 .jsx 文件,dist/scroll-list/index.jsx 引用 dist/vendor/reactlynx-scroll-list/index.jsx,发布时将整个 dist 目录包含在包中。

    如果 reactlynx-scroll-list 还引用了其他需要随包发布的组件库,需要为这些包添加构建,并在 vendor 构建的 output.externals 中将对应包名改写为它们的产物路径。

    直接复制依赖产物

    如果依赖产物可以直接使用,且无需修改内部导入,可以通过 output.copy 复制完整产物。

    以下配置以 bundleless 方式构建组件库源码并保留 JSX,同时复制依赖产物。示例同样仅在 src/scroll-list/index.ts 中导入该依赖,output.externals 中的路径相对于生成的 dist/scroll-list/index.jsx

    rslib.config.ts
    import { dirname } from 'node:path';
    import { fileURLToPath } from 'node:url';
    import { pluginReact } from '@rsbuild/plugin-react';
    import { defineConfig } from '@rslib/core';
    
    const scrollListDir = dirname(
      fileURLToPath(import.meta.resolve('reactlynx-scroll-list')),
    );
    
    export default defineConfig({
      bundle: false,
      dts: true,
      output: {
        target: 'web',
        filename: {
          js: '[name].jsx',
        },
        externals: {
          'reactlynx-scroll-list': '../vendor/reactlynx-scroll-list/index.jsx',
        },
        copy: [
          {
            from: scrollListDir,
            to: 'vendor/reactlynx-scroll-list',
          },
        ],
      },
      plugins: [
        pluginReact({
          swcReactOptions: {
            runtime: 'preserve',
          },
        }),
      ],
    });