用于 Storybook 的 React Native Web 插件
此插件配置 @storybook/react
以使用 React Native for Web (RNW) 显示 React Native (RN) 项目
请参阅 FAQ 了解常见问题。
你可以在这篇博客文章中阅读有关此软件包的更多信息。
要贡献,请参阅此处的贡献指南
这里是你可以如何将其与 storybook/react-native 一起使用的截图,该图片取自以下入门代码
入门
假设你有一个现有的 RN 项目,从项目根目录运行以下命令
npx sb init --type react
yarn add react-dom react-native-web babel-plugin-react-native-web @storybook/addon-react-native-web @react-native/babel-preset --dev
然后编辑你的 .storybook/main.js
文件
module.exports = {
addons: [/*existing addons,*/ '@storybook/addon-react-native-web'],
};
从这里,你应该能够根据 Storybook for React 的说明编写包含你的 RN 组件的故事。
常见问题
请参阅FAQ了解“loader not found”等常见问题。
配置选项
大多数软件包应该无需额外更改即可工作,但在某些情况下需要额外的步骤。一个常见的例子是“reanimated”,它需要一些 babel 配置和额外的转译。
选项 | 类型 | 描述 |
---|---|---|
modulesToTranspile | Array<string> |
需要转译的 node_modules |
modulesToAlias | {[key: string]: string} |
需要别名化的 node_modules |
babelPlugins | Array<string | [string, Record<string, string>]> |
你想应用的 Babel 插件 |
projectRoot | string |
项目根目录的路径,如果在单体仓库中,你可能需要设置此项。 |
babelPresets | Array<string | [string, Record<string, string>]> |
你想应用的 Babel 预设 |
babelPresetReactOptions | Record<string, any> |
传递给 @babel/preset-react 的选项 |
babelPresetReactNativeOptions | Record<string, any> |
传递给 @react-native/babel-preset 的选项 |
未转译的 React Native 库
许多 react-native 软件包未经转译就发布了,这不适用于 web 平台。如果你在添加软件包后收到诸如“proper loader not found”的错误,请尝试将其添加到此插件的 modulesToTranspile
选项中。
你可以这样做
module.exports = {
addons: [
/*existing addons,*/
{
name: '@storybook/addon-react-native-web',
options: {
modulesToTranspile: ['react-native-package-name'],
},
},
],
};
别名化 React Native Web 库
一些 react-native 软件包推荐使用模块别名来导入和使用现有软件包的 web 变体。如果你需要为 webpack 的 config.resolve.alias
添加额外的键值对,请使用此插件的 modulesToAlias
选项。你无需将 react-native-web 添加到此列表,因为它默认已包含在内。
你可以这样做
module.exports = {
addons: [
/*existing addons,*/
{
name: '@storybook/addon-react-native-web',
options: {
modulesToAlias: {
'react-native-package-name': 'react-native-web-package-name',
},
},
},
],
};
将 react-native-package-name
替换为真实软件包的名称。
添加 Babel 插件
在 react native 生态系统中,为某些软件包提供 babel 插件是很常见的,你可以将这些插件列表传递给此插件。
module.exports = {
addons: [
/*existing addons,*/
{
name: '@storybook/addon-react-native-web',
options: {
babelPlugins: ['babel-plugin-name'],
},
},
],
};
配置常用库
许多库无需额外配置即可工作,这里是一些软件包所需配置的示例。
注意:由于需要字体,react-native-vector-icons 需要一些额外的步骤,未来将有一个包含该配置的插件。
module.exports = {
addons: [
/*existing addons,*/
{
name: '@storybook/addon-react-native-web',
options: {
modulesToTranspile: ['react-native-reanimated'],
babelPlugins: [
'@babel/plugin-proposal-export-namespace-from',
'react-native-reanimated/plugin',
],
},
},
],
};
module.exports = {
addons: [
/*existing addons,*/
{
name: '@storybook/addon-react-native-web',
options: {
modulesToTranspile: ['react-native-vector-icons'],
},
},
],
};
module.exports = {
addons: [
/*existing addons,*/
{
name: '@storybook/addon-react-native-web',
options: {
modulesToTranspile: ['react-native-vector-icons'],
},
},
],
};
提示:使用 Storybook 的 Web 版本可以接收可能不会从移动模拟器中冒出的编译和运行时错误。
// .ondevice/main.ts
// AND/OR .storybook/main.ts
module.exports = {
addons: [
/*existing addons,*/
{
name: '@storybook/addon-react-native-web',
options: {
modulesToTranspile: [
'react-native-reanimated',
'nativewind',
'react-native-css-interop',
],
babelPresetReactOptions: { jsxImportSource: 'nativewind' },
// If you have a bable.config.js file: this can also be placed there, and removed from here
babelPresets: ['nativewind/babel'],
babelPlugins: [
// If you have a bable.config.js file: this can also be placed there, and removed from here
'react-native-reanimated/plugin',
// If you have a bable.config.js file: this can also be placed there, and removed from here
[
'@babel/plugin-transform-react-jsx',
{
runtime: 'automatic',
importSource: 'nativewind',
},
],
],
},
},
],
};
// .ondevice/preview.tsx
// add the following
import '../styles/global.css'
// metro.config.js
const path = require("path");
const { getDefaultConfig } = require("expo/metro-config");
const { withNativeWind } = require("nativewind/metro");
const withStorybook = require("@storybook/react-native/metro/withStorybook");
const defaultConfig = getDefaultConfig(__dirname);
// 👇 important: nativeWindConfig is defined and passed to withStorybook!
// replace this: module.exports = withNativeWind(config, { input: "./global.css" });
// with the below (and update your input):
const nativeWindConfig = withNativeWind(defaultConfig, { input: "./styles/global.css" });
module.exports = withStorybook(nativeWindConfig, {
// this line helps you switch between app and storybook using variable in package.json script,
// and changes to your app's main entry point.
enabled: process.env.EXPO_PUBLIC_STORYBOOK_ENABLED === "true",
configPath: path.resolve(__dirname, "./.ondevice"),
onDisabledRemoveStorybook: true,
});
// babel.config.js
module.exports = function (api) {
api.cache(true);
return {
presets: [
["babel-preset-expo", { jsxImportSource: "nativewind" }],
"nativewind/babel",
],
plugins: [
["babel-plugin-react-docgen-typescript", { exclude: "node_modules" }],
'react-native-reanimated/plugin',
[
'@babel/plugin-transform-react-jsx',
{
runtime: 'automatic',
importSource: 'nativewind',
},
],
],
};
};
添加对静态资源和 SVGs 的支持
安装 @svgr/webpack
和 url-loader
module.exports = {
/*existing config*/
// to provide a public export for assets
staticDirs: ['<path_to_assets>'],
webpackFinal: async (config) => {
const fileLoaderRule = config.module.rules.find(
(rule) => rule.test && rule.test.test('.svg'),
);
if (fileLoaderRule) {
fileLoaderRule.exclude = /\.svg$/;
}
config.module.rules.push({
test: /\.svg$/,
use: ['@svgr/webpack', 'url-loader'],
});
return config;
},
};
Webpack 5 的 Node Polyfills
安装 node-polyfill-webpack-plugin
const NodePolyfillPlugin = require('node-polyfill-webpack-plugin');
module.exports = {
/*existing config*/
core: {
builder: 'webpack5',
},
webpackFinal: async (config) => {
config.plugins.push(new NodePolyfillPlugin());
return config;
},
};
已知限制
- 不支持 react-native-web 的库将无法工作
- 组件将在 Web 上显示,因此可能与移动设备上的组件不同,因为可能会使用这些组件的 DOM 版本(例如
<div>
和<span>
)- 当使用 View/Text 等基础组件或其他跨平台组件时,差异应该很小。