给 React Native 库添加 Example

洛竹 等级 370 0 0

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

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

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

给 React Native 库添加 Example

为什么很难?

在探讨解决方案之前,让我们讨论一下为什么如此困难。 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 的人提供说明(以防你的代码库包含原生代码)。

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

.
├── example
├── package.json
└── src
    └── 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

首先安装插件:

yarn add -D babel-plugin-module-resolver

然后配置 babel.config.js

module.exports = {
  ...
  plugins: [
    [
      "module-resolver", {
        "alias": {
          "name-of-my-library": ".."
        }
      }
    ]
  ]
}

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

配置 Metro Bundler

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

const path = require('path');
const root = path.resolve(__dirname, '..');

module.exports = {
  projectRoot: __dirname,
  watchFolders: [root],
};

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

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

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

yarn add lodash prop-types

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

const path = require('path');
const root = path.resolve(__dirname, '..');

module.exports = {
  projectRoot: __dirname,
  watchFolders: [root],
  resolver: {
    extraNodeModules: modules.reduce((acc, name) => {
      acc[name] = path.join(__dirname, 'node_modules', name);
      return acc;
    }, {}),
  },
};

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

yarn add -D escape-string-regexp
// metro.config.js
const path = require('path');
const blacklist = require('metro-config/src/defaults/blacklist');
const escape = require('escape-string-regexp');
const pkg = require('../package.json');

const root = path.resolve(__dirname, '..');

const modules = [
  // 导致重复包的模块可以加在这里
  ...Object.keys({
    ...pkg.dependencies,
    ...pkg.peerDependencies,
  }),
];

module.exports = {
  projectRoot: __dirname,
  watchFolders: [root],

  resolver: {
    blacklistRE: blacklist([
      new RegExp(`^${escape(path.join(root, 'node_modules'))}\\/.*$`),
    ]),

    extraNodeModules: modules.reduce((acc, name) => {
      acc[name] = path.join(__dirname, 'node_modules', name);
      return acc;
    }, {}),
  },
};

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

const path = require('path');
const blacklist = require('metro/src/blacklist');
const pkg = require('../package.json');

const modules = [
  // 导致重复包的模块可以加在这里
  ...Object.keys({
    ...pkg.dependencies,
    ...pkg.peerDependencies,
  }),
];

module.exports = {
  getProjectRoots() {
    return [__dirname, path.resolve(__dirname, '..')];
  },
  getProvidesModuleNodeModules() {
    return modules;
  },
  getBlacklistRE() {
    return blacklist([
      new RegExp(
        `^${escape(path.resolve(__dirname, '..', 'node_modules'))}\/.*$`,
      ),
    ]);
  },
};

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

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

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

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

给 React Native 库添加 Example

收藏
评论区

相关推荐

为什么 React 源码不用 TypeScript 来写?
周末的,看点轻松的吧,之前看过 React 的源码,比较好奇像 React 这样庞大的工程为什么没有用 TypeScript。 Facebook 工程师 Cat Chen 在知乎上(https://www.zhihu.com/question/378470381/answer/1079675543(https://www.zhihu.com/quest
前端组件/库打包利器rollup使用与配置实战
目前主流的前端框架vue和react都采用rollup来打包,为了探索rollup的奥妙,接下来就让我们一步步来探索,并基于rollup搭建一个库打包脚手架,来发布自己的库和组件。 (https://imghelloworld.osscnbeijing.aliyuncs.com/imgs/16cb1c297071015523fb08d9e0f
Angular vs React 最全面深入对比
Angular vs React 最全面深入对比 Angular vs React 最全面深入对比 本文参考文章:https://www.sitepoint.com/reactvsangul
Create React App
Create React App Create React App Facebook开源了React前端框架(MIT Licence),也同时提供了React脚手架 createreactapp(ht
一起走进React核心团队
当我刚来Facebook的React团队工作时,我不确定接下来的工作会怎么样。表面看,React核心团队似乎很大!但事实证明,像Eli White和Sebastian McKenzie这样的人都在React Native团队。考虑加上那些维护开源库的维护者,比如Chakra UI、Framer Motion,React核心团队人数似乎能填满整个体育场!但事实
Hook 简介 – React
Hook 简介 _Hook_ 是 React 16.8 的新增特性。它可以让你在不编写 class 的情况下使用 state 以及其他的 React 特性。 import React, { useState } from 'react'; function Example() { // 声明一个新的叫做 “count” 的 sta
Taro 入门教程
简介 Taro 是一个遵循 React 语法规范的开放式跨端跨框架解决方案,支持使用 React/Vue/Nerv 等框架来开发微信/京东/百度/
30行代码实现Javascript中的 MVC
从09年左右开始,MVC逐渐在前端领域大放异彩,并终于在刚刚过去的2015年随着React Native的推出而迎来大爆发:AngularJS、EmberJS、Backbone、ReactJS、RiotJS、VueJS…… 一连串的名字走马观花式的出现
使用 State Hook – React
使用 State Hook_Hook_ 是 React 16.8 的新增特性。它可以让你在不编写 class 的情况下使用 state 以及其他的 React 特性。中使用下面的例子介绍了 Hook:import React, { useState } from 'react';function Example() { //
给 React Native 库添加 Example
本文翻译自 ,也夹杂了一些私货,如有帮助,请不吝点赞。你为 React Native 制作了一个闪亮的新库,现在该向全世界展示它。但是您需要添加一个示例应用程序,以便人们可以在安装之前进行尝试。有一个示例程序也能让你在开发时测试。这看起来很简单,真的是吗?不幸的是,它不是那么简单,并且可能会非常具有挑战性。我将描述我使用的过程,也许会对您有所帮助。请注意
React Native 调试最佳实践
本文翻译自 本文首发于 人非圣贤孰能无过。这句话还(更确切地说:尤其是...)适用于软件开发者。这就是为什么在每一个开发者的职业生涯中最重要的是知道如何处理这些失误。作为一个 React Native 开发者,我经常想要写出没有 BUG 的代码,但是当出现问题时,我需要确保我能够追踪并解决问题。在这篇文章中,我想讨论一些日常用于移动应用程序开发的工具
Hybrid APP基础篇-_Native、Hybrid、React Native、Web App方案的分析比较
说明 Native、Hybrid、React、Web App方案的分析比较 前言 参考来源 前人栽树,后台乘凉,本文参考了以下来源 对当前主流hybrid app、web app与native app工具的初步比较与分析
React Router 使用教程
React Router 使用教程作者: 日期: 真正学会 是一个漫长的过程。你会发现,它不是一个库,也不是一个框架,而是一个庞大的体系。想要发挥它的威力,整个技术栈都要配合它改造。你要学习一整套解决方案,从后端到前端,都是全新的做法。举例来说,React 不使用 HTML,而使用 JSX 。它打算抛弃 DOM,要求开发者
面向初学者的 React 路由-React Router的完整指南!
因此,您正在尝试学习React.js。也许您甚至已经在其中构建了几个简单的项目。无论您是新开发人员还是有一定经验的人,都可能会发现自己必须开发具有不同页面和路线的Web应用程序。那就是React Router发挥作用的时候。什么是React Router?React Router提供了一种在React或React Native应用程序中实现路由的简便方法。入
如何在React Native和Expo中掩盖Text和TextInput组件
在本文中,我将向您展示如何在React Native和Expo中使用自定义蒙版,可用于iOS,Android和Web!我们将使用一个名为库,这是一个没有本机代码的完整javascript库,然后您可以在React Native环境的所有CLI中使用。](https://res.cloudinary.com/practicaldev/image/fetch/s