React 源码(三)使用本地依赖库

Posted

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了React 源码(三)使用本地依赖库相关的知识,希望对你有一定的参考价值。

参考技术A 在 React 应用中依赖基本上是通过 yarn 或者 npm 进行安装的,但是在看源码的过程中,有的时候想要去调试,或者说打印一些数据,如果可以在本地的 React 应用里面依赖本地的 React 仓库,那么就可以进行上述的操作了。

在 React 官方文档中的开发流程 里面介绍了如何使用本地依赖库。

在启动本地 React 项目的时候出现了以下报错

在将 react-jsx-dev-runtime.development.js 文件复制到 build/node_modules/react/cjs 目录下即可。

在react-native 项目中使用yarn link

最近把项目中沉淀出来的一些工具函数整理出来编写成一个代码仓库,准备先在本地把每个函数都调试一遍之后再发布,需要使用yarn link 或者npm link 在测试的项目中link 指定的本地依赖库,在这过程中遇到一些问题

问题

在React Native 项目下无法使用yarn link 或者npm link 来link 本地的依赖,原因是RN 使用的打包工具Metro 不支持symlinks

逛了一番issues 后,找到了一个可行的解决方案

解决方案

使用第三方库metro-symlinked-deps

这个库可以用来自定义metro 的打包配置,使用方法可以参考metro-symlinked-deps 的文档

这里贴一下metro-symlinked-deps 给出的例子

// metro.config.js

const {
    applyConfigForLinkedDependencies,
} = require(\'@carimus/metro-symlinked-deps\');

module.exports = applyConfigForLinkedDependencies(
    {
        transformer: {
            getTransformOptions: async () => ({
                transform: {
                    experimentalImportSupport: false,
                    inlineRequires: false,
                },
            }),
        },
    },
    {
        projectRoot: __dirname,
        blacklistLinkedModules: [\'react-native\'],
    },
);

但是在使用这个库的时候也遇到了一些问题

另一个问题

一开始使用这个库并没有解决yarn link 无效的问题。确定自己配置没错之后,就开始寻找问题。因为看到有人说这个库有用,有人说没用。就开始看这个库的源码,看看它到底干了啥

于是,发现这个库会拿到项目下node_modules 里面使用symlink 的依赖的真实地址。

那么按理来说我用yarn link 是符合他的这个逻辑的, 并且我使用 ls -l 命令查看我link 的本地依赖也是正确返回了软链的地址。于是带着疑问我查看了这个库用来收集link 的依赖的方法,最终发现这个库使用了get-dev-paths 来检查link 的依赖

/**
 * Resolve all detected linked directories to unique absolute paths without a trailing slash.
 *
 * @param {string} projectRoot
 */
function resolveDevPaths(projectRoot) {
    return Array.from(
        new Set(
            getDevPaths(projectRoot)
                .map((linkPath) => {
                    return `${fs.realpathSync(linkPath)}`.replace(/\\/+$/, \'\');
                })
                .filter((absLinkPath) => !!absLinkPath),
        ),
    );
}

所以目光来到了get-dev-paths

首先看看它是如何返回link 的依赖的

addPath = function(dep) {
  var target;
  if (!fs.lstatSync(dep).isSymbolicLink()) {
    return;
  }
  try {
    target = realpath.sync(dep);
  } catch (error) {
    err = error;
    return typeof opts.onError === "function" ? opts.onError(err) : void 0;
  }
  // Skip target paths with "/node_modules/" in them.
  if (nodeModulesRE.test(target)) {
    return typeof opts.onError === "function" ? opts.onError(new Error(`Symlink leads to nothing: \'${dep}\'`)) : void 0;
  }
  if (opts.preserveLinks) {
    paths.push(dep);
  }
  if (!visited.has(target)) {
    visited.add(target);
    if (!opts.preserveLinks) {
      paths.push(target);
    }
    queue.push(dep);
  }
};

addPath 方法通过判断一个文件路径是否是软链,并文件的真实路径添加最终的输出结果中

那么addPath 在哪调用的呢,在这个库的源码里发现下面这段代码

return fs.readdirSync(depsDir).forEach(function(name) {
        var scope;
        if (name[0] === \'.\') { // Skip hidden directories.
          return;
        }
        if (name[0] === \'@\') {
          scope = name;
          fs.readdirSync(path.join(depsDir, name)).forEach(function(name) {
            if (deps[name = scope + \'/\' + name]) {
              return addPath(path.join(depsDir, name));
            }
          });
        } else if (deps[name]) {
          addPath(path.join(depsDir, name));
        }
      });

上面代码中deps[name]deps是获取了项目目录下的package.json中的dependencies 里的依赖,并且与fs.readdirSync方法获取的文件名来比对,如果文件名出现在了dependencies 就执行addPath方法来获取真实路径并添加到最终结果中。

于是我发现,我使用yarn link 的依赖的名字并没有出现在我的package.json中的dependencies 里,于是我在package.json 中的dependencies 里添加了本地依赖的名字

"dependencies": {
    "myPackageName" : "*"
},

再次尝试react-native start ,终于编译成功。

One more thing

到这里就可以开始测试自己的本地依赖了,但是还有一个小小问题,可能会报错。本地依赖的依赖找不到了。这时候需要改一下配置

// metro.config.js

const {
    applyConfigForLinkedDependencies,
} = require(\'@carimus/metro-symlinked-deps\');

module.exports = applyConfigForLinkedDependencies(
    {
        transformer: {
            getTransformOptions: async () => ({
                transform: {
                    experimentalImportSupport: false,
                    inlineRequires: false,
                },
            }),
        },
    },
    {
        projectRoot: __dirname,
        blacklistLinkedModules: [\'react-native\'],
        // 新增这个
          resolveNodeModulesAtRoot: true 
    },
);

终于可以在React Native 项目中调试本地依赖了~

最后

以上是我寻找在React Native 项目中使用yarn link的解决办法的全过程。

参考

  • metro-symlinked-deps
  • get-dev-paths
本文作者: Roy Luo

本文链接: 在react-native 项目中使用yarn link

以上是关于React 源码(三)使用本地依赖库的主要内容,如果未能解决你的问题,请参考以下文章

React.js + Summernote,如何导入 JavaScript 依赖库

本地 Maven 依赖库

如何将依赖于 Styled Components 的 React 组件库提供给另一个也具有 Styled Comopnents 依赖项的库?

Maven 3 不会从本地存储库更新快照依赖项

手动检索 Ivy 依赖项作为 sbt 中的本地非托管库

linux下JUCE源码编译依赖库