React 集成
Supports: React v16.7+ • TypeScript 3.7+ • Stencil v2.9.0+
Stencil 可以为你的 web 组件生成 React 组件包装器。这允许你的 Stencil 组件在 React 应用程序中使用。与标准 web 组件相比,使用 Stencil 的组件包装器的好处包括:
- 自定义事件将被正确处理,并正确地在 React 渲染树中传播
- 非字符串或数字的属性将被正确地绑定到组件
设置
项目结构
我们建议在组件库中使用 monorepo 结构和组件包装器。 你的项目工作区应该包含你的 Stencil 组件库和生成的 React 组件包装库。
一个项目设置的示例可能看起来类似于:
top-most-directory/
└── packages/
├── stencil-library/
│ ├── stencil.config.js
│ └── src/components/
└── react-library/
└── src/
├── components/
└── index.ts
本指南在 monorepo 中使用 Lerna,但你也可以使用其他解决方案,如 Nx、TurboRepo 等。
要在本教程中使用 Lerna,请全局安装 Lerna:
npm install --global lerna
yarn global add lerna
pnpm add --global lerna
创建一个 monorepo
提示
如果您已经有一个 monorepo,请跳过本节。
# From your top-most-directory/, initialize a workspace
lerna init
# install dependencies
npm install
# install typescript and node types
npm install typescript @types/node --save-dev
# From your top-most-directory/, initialize a workspace
lerna init
# install dependencies
yarn install
# install typescript and node types
yarn add typescript @types/node --dev
# From your top-most-directory/, initialize a workspace
lerna init
# install dependencies
pnpm install
# install typescript and node types
pnpm add typescript @types/node --save-dev
创建一个 Stencil 组件库
提示
如果您已经有了 Stencil 组件库,请跳过本节。
cd packages/
npm init stencil components stencil-library
cd stencil-library
# Install dependencies
npm install
cd packages/
yarn create stencil components stencil-library
cd stencil-library
# Install dependencies
yarn install
cd packages/
pnpm create stencil components stencil-library
cd stencil-library
# Install dependencies
pnpm install
创建一个 React 组件库
提示
如果您已经有了 React 组件库,请跳过本节。
当你第一次创建组件包装器时,你需要有一个 React 库来写入。
在 monorepo 的根目录下运行以下命令来创建一个 React 组件库:
# Create a project
lerna create react-library # fill out the prompts accordingly
cd packages/react-library
# Install core dependencies
npm install react react-dom typescript @types/react --save-dev
# Create a project
lerna create react-library # fill out the prompts accordingly
cd packages/react-library
# Install core dependencies
yarn add react react-dom typescript @types/react --dev
# Create a project
lerna create react-library # fill out the prompts accordingly
cd packages/react-library
# Install core dependencies
pnpm add react react-dom typescript @types/react --save-dev
Lerna 没有附带 TypeScript 配置。在工作空间的根目录,创建一个tsconfig.json
:
{
"compilerOptions": {
"module": "commonjs",
"declaration": true,
"noImplicitAny": false,
"removeComments": true,
"noLib": false,
"emitDecoratorMetadata": true,
"experimentalDecorators": true,
"target": "es6",
"sourceMap": true,
"lib": ["es6"]
},
"exclude": ["node_modules", "**/*.spec.ts", "**/__tests__/**"]
}
在你的 react-library
项目中,创建一个特定于项目的 tsconfig.json
,它将扩展根配置:
{
"extends": "../../tsconfig.json",
"compilerOptions": {
"outDir": "./dist",
"lib": ["dom", "es2015"],
"module": "es2015",
"moduleResolution": "node",
"target": "es2015",
"skipLibCheck": true,
"jsx": "react",
"allowSyntheticDefaultImports": true,
"declarationDir": "./dist/types"
},
"include": ["lib"],
"exclude": ["node_modules"]
}
更新 react-library
中生成的 package.json
,在现有配置中添加以下选项:
{
"main": "lib/react-library.js",
"main": "dist/index.js",
"module": "dist/index.js",
"types": "dist/types/index.d.ts",
"scripts": {
"test": "node ./__tests__/react-library.test.js"
"test": "node ./__tests__/react-library.test.js",
"build": "npm run tsc",
"tsc": "tsc -p ."
}
},
"files": [
"lib"
"dist"
]
"publishConfig": {
"access": "public"
},
"dependencies": {
"stencil-library": "*"
}
}
提示
Lerna 通过stencil_library
依赖来解决内部 Stencil 库依赖。请参阅 Lerna 的文档 包依赖管理了解更多信息。
添加 React 输出目标
安装 @stencil/react-output-target
依赖到你的 Stencil 组件库。
# Install dependency
npm install @stencil/react-output-target --save-dev
# Install dependency
yarn add @stencil/react-output-target --save-dev
# Install dependency
pnpm add @stencil/react-output-target --save-dev
在项目的 stencil.config.ts
, 添加 reactOutputTarget
配置到 outputTargets
数组中:
import { reactOutputTarget } from "@stencil/react-output-target";
export const config: Config = {
namespace: "stencil-library",
outputTargets: [
// By default, the generated proxy components will
// leverage the output from the `dist` target, so we
// need to explicitly define that output alongside the
// React target
{
type: "dist",
esmLoaderPath: "../loader",
},
reactOutputTarget({
componentCorePackage: "stencil-library",
proxiesFile: "../react-library/lib/components/stencil-generated/index.ts",
}),
],
};
TIP
proxiesFile
是所有 React 组件包装器生成的文件的相对路径。您将替换文件路径以匹配项目的结构和各自的名称。你可以生成任何文件名而不是 components.ts
。
componentCorePackage
应该与你的 Stencil 项目的 package.json
中的 name
字段匹配。
有关每个输出目标选项的详细信息,请参阅下面的 API 部分。
您现在可以构建您的 Stencil 组件库来生成组件包装器。
# Build the library and wrappers
npm run build
# Build the library and wrappers
yarn build
# Build the library and wrappers
pnpm run build
如果构建成功,你将在 React 组件库中的 proxiesFile
参数指定的位置看到新生成的文件。
将这些组件添加到 React 组件库的入口文件中
为了让生成的文件在 React 组件库及其使用者中可用,你需要从入口文件中导出所有内容。首先,将 react-library.js
重命名为 index.ts
。然后,修改内容以匹配以下内容:
export * from "./components/stencil-generated";
注册自定义元素
要为懒加载(hydrated) bundle 注册 web 组件,你需要暴露一个方法,用于注册底层 Stencil 生成的自定义元素,以便 React 代理组件使用。 最简单的方法是修改 React 库的入口文件,以重新导出模板加载器的 defineCustomElements()
方法。 在你的 React 库的入口文件( packages/react-library/lib/index.ts
)中,添加以下内容:
export * from "./components/stencil-generated";
export { defineCustomElements } from "stencil-library/loader";
Link Your Packages (Optional)
提示
如果你已经使用了 monorepo 工具 (Lerna, Nx, etc.), 请跳过本节。
在你成功构建本地版本的 React 组件库之前,你需要将 Stencil 包链接到 React 包。
在你的 Stencil 项目目录中,运行以下命令:
# Link the working directory
npm link
# Link the working directory
yarn link
# Link the working directory
pnpm link
在你的 React 组件库目录下,运行以下命令:
# Link the package name
npm link name-of-your-stencil-package
# Link the package name
yarn link name-of-your-stencil-package
# Link the package name
pnpm link name-of-your-stencil-package
你的 Stencil 包的名称应该与 Stencil 组件库的 package.json
中的 name
属性匹配。
你的组件库现在已经链接在一起了。你可以在 Stencil 组件库中进行更改,然后运行 npm run build
将更改传播到 React 组件库。
提示
作为 npm link
的替代方法,你也可以使用模板组件库的相对路径运行 npm install
。然而,这个策略会修改你的 package_json
,所以确保你不提交这些更改很重要。
Consumer Usage
Creating a Consumer React App
提示
如果你已经有了一个 React 应用,请跳过本节。
在 packages/
目录下,运行以下命令生成一个 React 应用:
# Create the React app
npm create vite@latest my-app -- --template react-ts
# of if using yarn
yarn create vite my-app --template react-ts
cd ./my-app
# install dependencies
npm install
# or if using yarn
yarn install
你还需要链接你的 React 组件库作为依赖。这一步使您的 React 应用程序能够正确解析从 React 组件库中导入的内容。
通过修改你的 React 应用程序的 package.json
来包含以下内容,很容易实现:
"dependencies": {
"react-library": "*"
}
使用 React 包装器组件
本节将介绍使用 React 组件包装器的开发者如何使用你的包和组件包装器。
在使用 React 代理组件之前,你需要构建 React 组件库。在 packages/react-library
中运行:
npm run build
yarn build
pnpm run build
要在 React 应用程序中使用 React 组件库,请在需要使用组件的文件中从 React 组件库中导入组件。
// App.tsx
import "./App.css";
import { MyComponent, defineCustomElements } from "react-library";
defineCustomElements();
function App() {
return (
<div className="App">
<MyComponent first="Your" last="Name" />
</div>
);
}
export default App;
API
componentCorePackage
Optional
Default: The components.d.ts
file in the Stencil project's package.json
types field
Type: string
消费者可以使用的 Stencil 包的名称(即 Stencil 组件库的 package.json
中的 name
属性的值)。 这用于在编译期间为组件编写正确的导入。
运行以下命令生成一个入门 Stencil 项目:
npm init stencil component my-component-lib
yarn create stencil component my-component-lib
pnpm create stencil component my-component-lib
componentCorePackage
将被设置为:
// stencil.config.ts
export const config: Config = {
...,
outputTargets: [
reactOutputTarget({
componentCorePackage: 'my-component-lib',
// ... additional config options
})
]
}
导入路径如下所示:
import { defineCustomElement as defineMyComponent } from "my-component-lib/components/my-component.js";
提示
尽管此字段是可选的,但 强烈 建议始终定义它,以避免在组合其他 API 参数时无法正确生成路径的潜在问题。
customElementsDir
Optional
Default: 'dist/components'
Type: string
如果 includeImportCustomElements 为 true
,此选项可用于指定生成的自定义元素所在的目录。 只有当 dist-custom-elements
输出目标上的 dir
字段被设置为默认目录以外的目录时,才需要设置此值。
excludeComponents
Optional
Default: []
Type: string[]
这可以让你指定不想生成 React 包装组件的组件标签名。如果你需要编写特定于框架的组件版本,这很有用。 例如,在 Ionic 框架中,它用于路由组件(如选项卡),以便 Ionic 框架可以更好地与 React Router 集成。
includeDefineCustomElements
Optional
Default: true
Type: boolean
如果为 true
,所有的 Web 组件将自动注册到自定义元素注册表。这只能在延迟加载 Web 组件时使用,当 includeImportCustomElements
为 true
时将不起作用。
includeImportCustomElements
Optional
Default: undefined
Type: boolean
如果为 true
,当组件在用户的应用程序内部被导入时,输出目标将导入自定义元素实例并将其注册到自定义元素注册表。 这只能用于自定义元素包,而不能用于惰性加载组件。
includePolyfills
Optional
Default: true
Type: boolean
如果为 true
, polyfills 将自动导入,applyPolyfills
函数将在你的代理文件中被调用。 这只能在延迟加载 Web 组件时使用,当启用 includeImportCustomElements
时将不起作用。
loaderDir
Optional
Default: /dist/loader
Type: string
在构建的项目中,defineCustomElements
辅助方法存在的路径。此选项仅在启用 includeDefineCustomElements
时使用。
proxiesFile
Required
Type: string
此参数允许您命名包含在编译过程中生成的所有组件包装器定义的文件。这是您应该在 React 项目中导入的第一个文件。
FAQ's
我必须使用 dist 输出目标吗?
不!默认情况下,输出目标将使用 dist
输出,但也可以使用 dist-custom-elements
的输出。
为此,只需在输出目标配置中设置 includeImportCustomElements
选项,并确保将自定义元素输出目标添加到模板配置的输出目标数组中:
// stencil.config.ts
export const config: Config = {
...,
outputTargets: [
// Needs to be included
{
type: 'dist-custom-elements'
},
reactOutputTarget({
componentCorePackage: 'component-library',
proxiesFile: '{path to your proxy file}',
// This is what tells the target to use the custom elements output
includeImportCustomElements: true
})
]
}
现在,所有生成的导入都将指向自定义元素输出的默认目录。如果你使用 dir
属性为 dist-custom-elements
指定了一个不同的目录,你还需要为 Vue 的输出目标指定该目录。 查看 API 部分以获取更多信息。
此外,当生成的组件模块被引导时,所有的 Web 组件都将被自动定义。
什么是最好的 event 名字的写法?
最初用模板书写的事件名称不应该包含特殊字符。尝试使用驼峰格式的事件名称来实现框架之间的互操作性。