前 言
最近一段时间一直在写 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 行都是在声明变量及引用,其中有几个变量这里我们需要知道它们是的作用:
变量名 | 含义 |
---|---|
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…