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

前 言

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

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 的版本。。node

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

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

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

而后开始各类面向搜索引擎,发现你们都是这样建立的啊,而且 RN 官网上给出的命令也是这样的,为何别人没有问题,到我这就有问题了呢。github

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

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

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

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

原本到这就能够结束的,然而做为一个想有灵魂的程序猿,仍是很想弄清楚为何不加空格就会建立最新版本,而不是提示语法错误的缘由。

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 行都是在声明变量及引用,其中有几个变量这里咱们须要知道它们是的做用:

变量名 含义
fs Node.js 中 文件系统操做的模块
path Node.js 中用于处理文件路径的小工具的模块
exec Node.js 中子进程模块, 衍生一个 shell 并在 shell 上运行命令
execSync exec 的同步函数,会阻塞 Node.js 事件循环
chalk 定制控制台日志的输入样式的一个插件
prompt node 命令行输入控件
semver semver 语义化版本号
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…

相关文章
相关标签/搜索