给 React Native 库添加 Example

本文翻译自 Adding an example app to your React Native library,也夹杂了一些私货,如有帮助,请不吝点赞。

你为 React Native 制作了一个闪亮的新库,现在该向全世界展示它。但是您需要添加一个示例应用程序,以便人们可以在安装之前进行尝试。有一个示例程序也能让你在开发时测试。这看起来很简单,真的是吗?

不幸的是,它不是那么简单,并且可能会非常具有挑战性。我将描述我使用的过程,也许会对您有所帮助。请注意,我将仅介绍 JS 库。如果你的仓库有原生代码,这篇文章仍旧非常有用,但是你需要额外的步骤——将原生代码桥接到 app。

为什么很难?

在探讨解决方案之前,让我们讨论一下为什么如此困难。 React Native 使用了一个名为 Metro Bundler 的自定义打包器来捆绑您的 JavaScript 代码和一个名为 haste 的模块解析机制。

Metro 速度非常快,非常适合大型代码库。但是现在它有一些让写一个示例 app 有些困难的限制:

  • 我们不能使用 symlinks,因为 Metro Bundler 使用 watchman不支持 symlinks。
  • 我们不能导入当前目录外的模块。
  • 当遇到两个具有相同名称的模块时,Example 就无法运行。这很容易遇到,因为我们将为开发和示例安装相同的模块。

私货:因为 react native 不支持 symlinks,所以 npm link 也是不可用的。在 使用 wml 进行 npm 模块调试真香 中可以对此问题有更深入了解。

但是你知道吗?实际上,可以配置打包程序,以便我们可以解决示例应用程序的限制。然后配置属性并没有任何文档,而且 Metro Bundler 现在并不是个成熟的工具,除非你足够了解她,否则配置起来十分困难。

私货:Metro Bundler 先前负责 tree-shaking 的老哥 @rafeca@mjesun 从 Facebook 离职了,tree-shaking 就成了至今未支持的特性,见 Tree shaking while bundling,可以想见 Facebook 也缺人,如果哪位大佬可以搞定这些问题,可以试试入职 Facebook。

当下,我们能做什么?

这里,我们假设你使用 Expo 作为 example。为什么是 Expo?因为这对你来说非常容易开发,而且它的导出示例的方式简单到令人难以置信。但是,我还将为那些不想/不能使用 Expo 的人提供说明(以防你的代码库包含原生代码)。

下面是我的项目的目录结构,也是社区常见的:

1. 2├── example 3├── package.json 4└── src 5 └── index.js

我们的库放在 src 文件夹下,example 应用(一个 Expo 应用或 React Native app)放在 example/ 文件夹下。

接下来,我们可以使用 exp init example 或者 react-native init example 来初始化项目。(国内的 Expo 服务巨慢,科学上网也慢,不建议使用)。

配置 Babel

我们需要将我们的库添加为 example 的依赖项。我非常幼稚地添加库作为 file: 依赖,这是有效的。但是当你修改一些东西,你要每次都记得重新安装,这很蠢不是吗?

此外,npm@5file: 依赖项作为符号链接安装,前面我们提到,React Native Packager 并不支持符号链接。

如果我们可以像对 Webpack 那样对库起别名该怎么办?答案是 babel-plugin-module-resolver

首先安装插件:

1yarn add -D babel-plugin-module-resolver

然后配置 babel.config.js

1module.exports = { 2 ... 3 plugins: [ 4 [ 5 "module-resolver", { 6 "alias": { 7 "name-of-my-library": ".." 8 } 9 } 10 ] 11 ] 12}

这很简单,但是难点是我们还需要接着配置 Metro Bundler

配置 Metro Bundler

Metro Packager 使用特定的配置文件 metro.config.js(老版是 rn-cli.config.js)。让我们创建这个文件并复制下面的内容:

1const path = require('path'); 2const root = path.resolve(__dirname, '..'); 3 4module.exports = { 5 projectRoot: __dirname, 6 watchFolders: [root], 7};

这告诉打包程序将项目根目录设置为当前目录(example 文件夹)并且监听父目录。这让我们可以从父目录引用模块。

不幸的是,我们无法引入父目录中的 node_modules 模块,因此我们不得不在 example 中重复安装模块。

假设我们有 lodashprop-types 模块作为库的依赖项,我们现在需要将它们安装在 example 文件夹中。

1yarn add lodash prop-types

我们需要配置 extraNodeModules resolver 来识别这些库:

1const path = require('path'); 2const root = path.resolve(__dirname, '..'); 3 4module.exports = { 5 projectRoot: __dirname, 6 watchFolders: [root], 7 resolver: { 8 extraNodeModules: modules.reduce((acc, name) => { 9 acc[name] = path.join(__dirname, 'node_modules', name); 10 return acc; 11 }, {}), 12 }, 13};

我们还需要将父目录的 node_modules 列入黑名单,否则打包程序将抱怨重复的模块。让我们安装 escape-string-regexp 软件包,然后更新配置。

1yarn add -D escape-string-regexp
1// metro.config.js 2const path = require('path'); 3const blacklist = require('metro-config/src/defaults/blacklist'); 4const escape = require('escape-string-regexp'); 5const pkg = require('../package.json'); 6 7const root = path.resolve(__dirname, '..'); 8 9const modules = [ 10 // 导致重复包的模块可以加在这里 11 ...Object.keys({ 12 ...pkg.dependencies, 13 ...pkg.peerDependencies, 14 }), 15]; 16 17module.exports = { 18 projectRoot: __dirname, 19 watchFolders: [root], 20 21 resolver: { 22 blacklistRE: blacklist([ 23 new RegExp(`^${escape(path.join(root, 'node_modules'))}\\/.*$`), 24 ]), 25 26 extraNodeModules: modules.reduce((acc, name) => { 27 acc[name] = path.join(__dirname, 'node_modules', name); 28 return acc; 29 }, {}), 30 }, 31};

如果你使用旧版的 Metro(比如 React Native 版本小于 0.55),这个配置文件看起来是这样的:

1const path = require('path'); 2const blacklist = require('metro/src/blacklist'); 3const pkg = require('../package.json'); 4 5const modules = [ 6 // 导致重复包的模块可以加在这里 7 ...Object.keys({ 8 ...pkg.dependencies, 9 ...pkg.peerDependencies, 10 }), 11]; 12 13module.exports = { 14 getProjectRoots() { 15 return [__dirname, path.resolve(__dirname, '..')]; 16 }, 17 getProvidesModuleNodeModules() { 18 return modules; 19 }, 20 getBlacklistRE() { 21 return blacklist([ 22 new RegExp( 23 `^${escape(path.resolve(__dirname, '..', 'node_modules'))}\/.*$`, 24 ), 25 ]); 26 }, 27};

注意,如果你的项目里有其他的 node_modules(比如 docs 中的),你可能也要把它填到黑名单中。

如果你使用了 expo,添加下面的配置到 exp.json

1"packagerOpts": { 2 "config": "./metro.config.js", 3 // projectRoots 需要设置为空字符串来覆盖 Expo 默认的配置 4 "projectRoots": "" 5}

你可以参考 react-native-tab-viewtuya-panel-kit 的 example 写法。另外 react-native-builder-bob 这个脚手架也是基于相同的原理。感兴趣的可以上手把玩把玩。

点赞
收藏

评论区

加载中...

相关推荐

MySQL:[Err] 1292 - Incorrect datetime value: ‘0000-00-00 00:00:00‘ for column ‘CREATE_TIME‘ at row 1

文章目录问题用navicat导入数据时,报错:原因这是因为当前的MySQL不支持datetime为0的情况。解决修改sql\mode:sql\mode:SQLMode定义了MySQL应支持的SQL语法、数据校验等,这样可以更容易地在不同的环境中使用MySQL。全局s

Oracle 分组与拼接字符串同时使用

SELECTT.,ROWNUMIDFROM(SELECTT.EMPLID,T.NAME,T.BU,T.REALDEPART,T.FORMATDATE,SUM(T.S0)S0,MAX(UPDATETIME)CREATETIME,LISTAGG(TOCHAR(

MySQL部分从库上面因为大量的临时表tmp_table造成慢查询

背景描述Time:20190124T00:08:14.70572408:00User@Host:@Id:Schema:sentrymetaLast_errno:0Killed:0Query_time:0.315758Lock_

皕杰报表之UUID

​在我们用皕杰报表工具设计填报报表时,如何在新增行里自动增加id呢?能新增整数排序id吗?目前可以在新增行里自动增加id,但只能用uuid函数增加UUID编码,不能新增整数排序id。uuid函数说明:获取一个UUID,可以在填报表中用来创建数据ID语法:uuid()或uuid(sep)参数说明:sep布尔值,生成的uuid中是否包含分隔符'',缺省为

手写Java HashMap源码

HashMap的使用教程HashMap的使用教程HashMap的使用教程HashMap的使用教程HashMap的使用教程22

2020年前端实用代码段,为你的工作保驾护航

有空的时候,自己总结了几个代码段,在开发中也经常使用,谢谢。1、使用解构获取json数据let jsonData  id: 1,status: "OK",data: 'a', 'b';let  id, status, data: number   jsonData;console.log(id, status, number )