React Native --踩坑记 之 创建指定 React Native版本的项目

前 言

最近一段时间一直在写 RN 的项目,期间遇到了挺多的坑,然后想着记录一下填坑的过程(想看答案的小伙伴可以忽略我的心厉路程,直接跳到结尾总结处)。

step1. 我竟然偷偷的给自己挖了个坑?

于是乎,第一步,赶紧新建一个demo,飞快地在 terminal 中输入 react native init yx_rnDemo ,漫长的等待后,项目成功建立。 然后用 IDE 打开 demo ,执行react-native run-android 命令,结果半路夭折,没跑起来。仔细一看错误日志,发现 android 各种依赖都下载失败。然后看了下 package.json 中 react-native 的版本,发现引用的是最新版本,然后点击查看 android 文件夹,发现引用的 gradle 版本是 3.1.4 ,然鹅我用的还是 2.3.3 的版本。。

因为比较懒(这句话在我的博客中出现的次数不低,懒是万恶之源,罪过罪过~~),不想升级,再配置一系列东西,所以按照 中文网 给出的创建指定版本的方法:

提示:你可以使用--version参数(注意是两个杠)创建指定版本的项目。例如react-native init MyApp --version 0.44.3。注意版本号必须精确到两个小数点

删掉 demo ,重新输入 react native init yx_rnDemo --version0.47.2 ,结果最后发现其实创建的还是最新版。。(内心 OS,what??其实细心的朋友估计已经发现问题了,哈哈,嘘~~)

然后开始各种面向搜索引擎,发现大家都是这样创建的啊,并且 RN 官网上给出的命令也是这样的,为什么别人没有问题,到我这就有问题了呢。

然后换了一个命令执行: react-native init yx_rnDemo --verbose --version 0.47.2 想来看一下创建项目的详细信息,结果最后显示创建的竟然是对的!! 就是 0.47.2 。

难道说加了一个 --verbose 条件就能创建成功了?不对啊,我看了下说 --verbose 条件只是会输出详细信息的啊,照理说不应该对结果产生什么影响的。然后不信邪的我把 --verbose 命令去掉,又执行了react-native init yx_rnDemo --version 0.47.2,过了一会发现,竟然也是对的!!

吓得我赶紧去翻我第一次写的命令,一对比,发现我第一个命令--verison 后没有换行~~

第一次的:react native init yx_rnDemo --version0.47.2 第二次的:react-native init yx_rnDemo --version 0.47.2

本来到这就可以结束的,然而作为一个想有灵魂的程序猿,还是很想弄清楚为什么不加空格就会创建最新版本,而不是提示语法错误的原因。

step2. 一步步分析坑是如何产生的

这里首先介绍一下, react-native 源码中 react-native-cli 文件夹下的 index.js 这个文件很重要,它唯一的工作是初始化存储库,然后将所有命令转发到本地的 react-native 版本。所以我们初始化项目时做的操作可以在这个文件中找到。

打开这个 js 文件,然后开始一探究竟吧。 1.

'use strict';

var fs = require('fs');
var path = require('path');
var exec = require('child_process').exec;
var execSync = require('child_process').execSync;
var chalk = require('chalk');
var prompt = require('prompt');
var semver = require('semver');
/**
 * Used arguments:
 *   -v --version - to print current version of react-native-cli and react-native dependency
 *   if you are in a RN app folder
 * init - to create a new project and npm install it
 *   --verbose - to print logs while init
 *   --template - name of the template to use, e.g. --template navigation
 *   --version <alternative react-native package> - override default (https://registry.npmjs.org/react-native@latest),
 *      package to install, examples:
 *     - "0.22.0-rc1" - A new app will be created using a specific version of React Native from npm repo
 *     - "https://registry.npmjs.org/react-native/-/react-native-0.20.0.tgz" - a .tgz archive from any npm repo
 *     - "/Users/home/react-native/react-native-0.22.0.tgz" - for package prepared with `npm pack`, useful for e2e tests
 */

var options = require('minimist')(process.argv.slice(2));
复制代码

文件的前 62 行都是在声明变量及引用,其中有几个变量这里我们需要知道它们是的作用:

变量名含义
fsNode.js 中 文件系统操作的模块
pathNode.js 中用于处理文件路径的小工具的模块
execNode.js 中子进程模块, 衍生一个 shell 并在 shell 上运行命令
execSyncexec 的同步函数,会阻塞 Node.js 事件循环
chalk定制控制台日志的输入样式的一个插件
promptnode 命令行输入控件
semversemver 语义化版本号
options轻量级的命令行参数解析工具

其中一个很关键的变量 options ,也就是引用的require('minimist')(process.argv.slice(2)) ,是一个命令行参数解析工具,具体的介绍可以参考这里,它是以键值对进行解析的。比如我们输入的命令行是:react-native init yx_rnDemo --version 0.47.2 ,其中 --version 0.47.2 就是一个可解析的键值对,key 为 version , value 为 0.47.2 。

这个文件中,我们有用到的键值对的值在截图的注释中可以看到:

  • -v : 打印当前 react-native-cli 的版本和 react native 的依赖关系
  • init : 创建一个新工程并且执行 npm install
  • --verbose: init 时添加的参数,打印init时的参数
  • --template:用到的模板的名称
  • --version : 会覆盖默认(最新版本)安装的 react-native 的版本。 也就是如果要创建指定版本的,需要加上这个参数

OK, 各个变量的含义我们都弄清楚了,下面让我们继续探究~~

2.

 switch (commands[0]) {
    case 'init':
      if (!commands[1]) {
        console.error('Usage: react-native init <ProjectName> [--verbose]');
        process.exit(1);
      } else {
        init(commands[1], options);
      }
      break;
    default:
     //...代码省略
      break;
  }
}
复制代码

在这之前 116 行 定义了 commands 这个变量,取值的结果是解析的参数,应该是 _ [ 'init ', 'yx_rnDEmo'],所以会走switch 的第一个选项,去执行 init(commands[1], options) 方法,参数为 'yx_rnDemo’ 和 options 变量。

3.

/**
 * @param name Project name, e.g. 'AwesomeApp'.
 * @param options.verbose If true, will run 'npm install' in verbose mode (for debugging).
 * @param options.version Version of React Native to install, e.g. '0.38.0'.
 * @param options.npm If true, always use the npm command line client,
 *                       don't use yarn even if available.
 */
function init(name, options) {
  validateProjectName(name);

  if (fs.existsSync(name)) {
    createAfterConfirmation(name, options);
  } else {
    createProject(name, options);
  }
}
复制代码

很简单,先去判断我们起的工程名称是否符合命名规范,并且判断是否存在。所以下面直接看 createProject(name, options) 方法

4.

function createProject(name, options) {
   //....代码省略
  run(root, projectName, options);
}
复制代码

这个方法里主要是去进行创建工程文件夹和 package.json 文件的操作,然后后续行动在run(root, projectName, options) 函数中

5.

function run(root, projectName, options) {
  var rnPackage = options.version; // e.g. '0.38' or '/path/to/archive.tgz'

  console.log('Installing ' + getInstallPackage(rnPackage) + '...');
   //...代码省略
  installCommand =  'npm install --save --save-exact ' + getInstallPackage(rnPackage);
  if (options.verbose) {
      installCommand += ' --verbose';
   }
   //...代码省略
   try {
    execSync(installCommand, {stdio: 'inherit'});
    } catch (err) {
      //... 代码省略
  }
  cli.init(root, projectName);
}
复制代码

其中这个 rnPackage 就是解析的 version 参数 ,所以,对于我的第一次使用的命令:react-native init yx_rnDemo --version0.47.2 来说,解析工具并没有找到 key 为 version 的参数,所以第一次命令的 rnPackage的值应该是空的,输入正确后就是 0.47.2 了。 然后看 installCommand 这个变量,就是最终执行的命令。其中一个参数是需要到getInstallPackage(rnPackage)去确定一下是什么。

function getInstallPackage(rnPackage) {
  var packageToInstall = 'react-native';
  var isValidSemver = semver.valid(rnPackage);
  if (isValidSemver) {
    packageToInstall += '@' + isValidSemver;
  } else if (rnPackage) {
    // for tar.gz or alternative paths
    packageToInstall = rnPackage;
  }
  return packageToInstall;
}
复制代码

OMG! 看到上面的代码 激不激动,终于真相大白了!! 按照我的第一次错误的写法,这个 rnPackage 是空,然后

 var isValidSemver = semver.valid(rnPackage);
复制代码

这一行代码的含义是进行一个版本语义化规范的检查,就是你创建的版本号必须符合 semver语义化规范,也就是 x.y.z 的格式,比如 0.47.2 ,然而我现在传的空,肯定是不符合规范的,果断返回 false ,所以该方法会返回 "react-native", 默认会安装最新版。 而我后来正确的写法,是符合规范的,最终该方法会返回 "react-native@0.47.2" ! 然后就会下载指定的版本了。

最后我们这边可以验证下,输出的 log 参数是不是我们在代码中看到的。

上图:

果然如此~~

然后终于理解了,react-native 中文网 中提示如果创建指定版本,版本号必须满足两位小数点 这句话是为什么了。

step3. 总结 & 填坑

所以说了那么多,如果想在 init 时候指定版本号,非常简单,,就是官网指出的:

react-native init MyApp --version 0.44.3

但必须注意检查两点(估计也就我这么粗心的人会犯吧):

1.--version 一定要加空格,千万不要写成 --version0.44.3

2.版本号一定要两位小数点,必须符合 semver语义化规范

参考文章

github.com/facebook/re… nodejs.cn/api/child_p… www.runoob.com/nodejs/node… nodejs.cn/api/fs.html… www.jianshu.com/p/231b931ab…

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值