先說結論:能夠經過 React Native
中提供的 __DEV__
變量,在產出 production
環境的代碼包時,過濾掉一些測試的頁面。javascript
環境說明:前端
"react": "16.8.3",
"react-native": "0.59.3",
複製代碼
咱們的RN代碼目錄大概是這樣:java
./
├── common 公共代碼目錄
├── entry
├── index.js
├── page 頁面代碼目錄
複製代碼
在咱們的RN代碼裏,有一些公共組件,好比 Button
LoadingDialog
之類的,放置在 common
目錄下。組件開發完以後,通常會在頁面中,增長一個對應的測試頁面,好比要測試 LoadingDialog
,通常會增長一個 page/test/loadingTest.tsx
文件,在這個頁面裏,能夠測試下組件的各類功能是否OK。react
在 page/index.ts
裏,以前會有工具,自動掃描 page/**/**.tsx
生成全部的頁面信息,固然裏面也會包含不少在開發中用到的測試頁面。android
這個 page/index.ts
的內容以下:git
import accountBindCardBindCard from './account/bindCard/BindCard';
import testMainMain from './test/main/Main';
import testMarqueeMarquee from './test/marquee/Marquee';
import testStyleTestLoading from './test/styleTest/Loading';
import testStyleTestModal from './test/styleTest/Modal';
import testStyleTestTextAnimation from './test/styleTest/TextAnimation';
import testStyleTestTextTest from './test/styleTest/TextTest';
import testStyleTestWithdrawResult from './test/styleTest/WithdrawResult';
import uplanInvestTypeInvestType from './uplan/investType/InvestType';
import uplanJoinJoin from './uplan/join/Join';
export default {
'account/bindCard/BindCard': accountBindCardBindCard,
'Main': testMainMain,
// 下面這些 test 目錄下的頁面,都是測試頁面
'test/marquee/Marquee': testMarqueeMarquee,
'test/styleTest/Loading': testStyleTestLoading,
'test/styleTest/Modal': testStyleTestModal,
'test/styleTest/TextAnimation': testStyleTestTextAnimation,
'test/styleTest/TextTest': testStyleTestTextTest,
'test/styleTest/WithdrawResult': testStyleTestWithdrawResult,
// 目標就是,在打production包的時候,幹掉這些測試頁面,減少包大小
'uplan/investType/InvestType': uplanInvestTypeInvestType,
'uplan/join/Join': uplanJoinJoin,
};
複製代碼
以前的處理方式,是在打包時候,調用一個本身封裝的打包JS腳本,這個腳本會先掃描 page
目錄,若是當前是 production
模式,即 --dev false
,那麼在生成上面的 page/index.ts
文件內容時,會過濾掉 page/test
目錄下的頁面。github
咱們準備在下一次RN升級的時候,幹掉本身開發的JS腳本,改回到直接調用RN官方的打包腳本。shell
所以,須要有一種方式,可以在 development
和 production
環境下,容許咱們有選擇地加載某些頁面。react-native
__DEV__
很容易想到,RN官方就提供了一個全局變量,__DEV__
來代表當前是否production模式。工具
想法很簡單,不能直接在 page/index.ts
裏 import
測試頁面了,須要根據 __DEV__
來決定是否去import。可是在實際測試過程當中,仍是發現了一些問題,最後總結下來,我大概測試瞭如下幾種狀況:
export default {
get 'account/bindCard/BindCard'() { return require('./account/bindCard/BindCard').default; },
// 下面是幾種寫法
get 'test/story/StoryDisplay'() {
if (__DEV__) {
return require('./test/story/StoryDisplay').default;
}
return null;
},
get 'test/styleTest/Loading'() {
if (__DEV__ !== true) {
return null;
}
return require('./test/styleTest/Loading').default;
},
get 'test/styleTest/Modal'() {
if (!__DEV__) {
return null;
}
return require('./test/styleTest/Modal').default;
},
get 'test/styleTest/TextAnimation'() {
if (__DEV__) {
return require('./test/styleTest/TextAnimation').default;
} else {
return null;
}
},
get 'test/styleTest/TextTest'() {
if (!__DEV__) {
return null;
} else {
return require('./test/styleTest/TextTest').default;
}
},
// 上面是測試幾種寫法的實際效果
get 'test/styleTest/WithdrawResult'() { if (__DEV__) { return require('./test/styleTest/WithdrawResult').default; } return null; },
get 'uplan/investType/InvestType'() { return require('./uplan/investType/InvestType').default; },
get 'uplan/join/Join'() { return require('./uplan/join/Join').default; },
};
複製代碼
其實仔細看下來,就是兩種寫法,require
是寫在 if
內部仍是外面。
先來看下 development
模式下的bundle代碼,訪問本地URL http://localhost:8081/index.bundle?platform=android&dev=true&minify=false
:
get 'test/story/StoryDisplay'() {
if (__DEV__) {
return _$$_REQUIRE(_dependencyMap[58], "./test/story/StoryDisplay").default;
}
return null;
},
get 'test/styleTest/Loading'() {
if (__DEV__ !== true) {
return null;
}
return _$$_REQUIRE(_dependencyMap[59], "./test/styleTest/Loading").default;
},
get 'test/styleTest/Modal'() {
if (!__DEV__) {
return null;
}
return _$$_REQUIRE(_dependencyMap[60], "./test/styleTest/Modal").default;
},
get 'test/styleTest/TextAnimation'() {
if (__DEV__) {
return _$$_REQUIRE(_dependencyMap[61], "./test/styleTest/TextAnimation").default;
} else {
return null;
}
},
get 'test/styleTest/TextTest'() {
if (!__DEV__) {
return null;
} else {
return _$$_REQUIRE(_dependencyMap[62], "./test/styleTest/TextTest").default;
}
},
複製代碼
能夠看出,在 dev=true
模式下,全部的測試頁面都被RN打包了,上面幾種源碼的寫法,產出沒什麼差異。
下面再看看 production
模式的產出bundle,訪問URL http://localhost:8081/index.bundle?platform=android&dev=false&minify=false
:
get 'test/story/StoryDisplay'() {
return null;
},
get 'test/styleTest/Loading'() {
{
return null;
}
return _$$_REQUIRE(_dependencyMap[57]).default;
},
get 'test/styleTest/Modal'() {
{
return null;
}
return _$$_REQUIRE(_dependencyMap[58]).default;
},
get 'test/styleTest/TextAnimation'() {
{
return null;
}
},
get 'test/styleTest/TextTest'() {
{
return null;
}
},
複製代碼
Hmm, interesting🤔,能夠明顯看出,有的寫法最終打包的結果,並非咱們意料之中的。
顯然,上面的 test/styleTest/Loading
和 test/styleTest/Modal
是 有問題 的。這時候在bundle裏搜索這兩個頁面的代碼,能夠發現這兩個頁面代碼已經被打包到了bundle裏(廢話,均可以看到 _$$_REQUIRE(_dependencyMap[58]).default
這樣的字眼了…… )
仔細對比這兩種有問題的代碼,和其餘幾種寫法,很容易發現,有問題的代碼, require
都 沒有 放到 if/else
的分支裏。
在 React Native 中,能夠經過 __DEV__
在不一樣的環境(development
仍是 production
)來有選擇地加載一些JS文件。可是 require
的分支,必須 放在對應的 if(__DEV__){}else{}
裏,這樣才能被RN打包的時候過濾掉。猜想RN打包時,是會刪除 if/else
裏的死碼;可是在 if/else
以外的 require
,即便永遠不會被執行到,也會被 靜態分析 出來,從而將對應代碼打到最終的bundle文件裏。
關於 React (Native) 的死碼移出,感興趣的能夠看這篇譯文:github.com/sophister/2…
最後慣例,歡迎你們star咱們的人人貸大前端團隊博客,全部的文章還會同步更新到知乎專欄 和 掘金帳號,咱們每週都會分享幾篇高質量的大前端技術文章。若是你喜歡這篇文章,但願能動動小手給個贊。