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…

相關文章
相關標籤/搜索