我不懂原生,只作 js 部分,有懂這方面的大佬,也能夠補充原生部分,git地址: github.com/Mr-jiangzhi…前端
環境:node
macOS: 10.14.4;react
node(nvm): 10.15.3;android
react-native-cli: 2.0.1;ios
react-native: 0.60.0;git
.
├── .babelrc
├── .buckconfig
├── .editorconfig
├── .eslintrc.js
├── .flowconfig
├── .git
├── .gitattributes
├── .gitignore
├── .patch (存放增量文件)
├── .watchmanconfig
├── README.md
├── __tests__
├── android (原生代碼,原生開發維護)
├── app.json
├── babel.config.js
├── bundles (bundle輸出目錄)
├── business.config.js (打包業務模塊的配置)
├── common.config.js (打包公共基礎模塊的配置)
├── common.js (公共基礎模塊的入口文件,將第三方依賴/shim等文件引入進來)
├── config (配置文件目錄)
├── index.js (開發模式的入口文件)
├── ios (原生代碼,原生開發維護)
├── jsconfig.json
├── package.json
├── scripts (腳本目錄)
├── src (前端頁面,前端開發維護)
├── upload (存放需上傳服務器的文件)
└── yarn.lock
複製代碼
一般,咱們將一個大的 jsbundle 包拆分爲基礎包和業務包:github
基於 react-native bundle
命令拆包(實際上是基於metro) 命令爲咱們提供了 --config
參數,讓咱們能夠本身指定配置文件,這樣咱們就能夠經過兩個配置文件(common 和 business)來分別進行打包了:npm
react-native bundle [其餘配置] --config common
:打公共基礎包react-native bundle [其餘配置] --config business
:打業務包下面咱們來看看 metro 的這個 config 文件:json
module.exports = {
resolver: {
/* resolver options */
},
transformer: {
/* transformer options */
},
serializer: {
/* serializer options */
},
server: {
/* server options */
},
/* general options */
};
複製代碼
對於拆包咱們能用到的就是 serializer
中的 createModuleIdFactory
和 postProcessBundleSourcemap
這兩個方法:react-native
createModuleIdFactory
:
require
statements.postProcessBundleSourcemap
:
config
目錄新建 createModuleIdFactory.js
和 postProcessBundleSourcemap.js
createModuleIdFactory.js
:
/** * 生成模塊Id * 基礎打包配置 */
const path = require('path');
const pathSep = path.posix.sep;
function createModuleIdFactory() {
const projectRootPath = process.cwd(); //獲取命令行執行的目錄
return modulePath => {
// console.log(modulePath)
let moduleName = '';
if (modulePath.indexOf(`node_modules${pathSep}react-native${pathSep}Libraries${pathSep}`) > 0) {
//這裏是去除路徑中的'node_modules/react-native/Libraries/‘以前(包括)的字符串,能夠減小包大小,無關緊要
moduleName = modulePath.substr(modulePath.lastIndexOf(pathSep) + 1);
} else if (modulePath.indexOf(projectRootPath) === 0) {
//這裏是取相對路徑,不這麼弄的話就會打出_user_smallnew_works_....這麼長的路徑,還會把計算機名打進去
moduleName = modulePath.substr(projectRootPath.length + 1);
}
moduleName = moduleName.replace('.js', ''); //js png字符串不必打進去
moduleName = moduleName.replace('.png', '');
let regExp = pathSep === '\\' ? new RegExp('\\\\', 'gm') : new RegExp(pathSep, 'gm');
moduleName = moduleName.replace(regExp, '_'); //把path中的/換成下劃線(適配Windows平臺路徑問題)
// console.log(moduleName);
return moduleName;
};
}
module.exports = createModuleIdFactory;
複製代碼
postProcessBundleSourcemap.js
:
// 業務代碼
const path = require('path');
const pathSep = path.posix.sep;
// 這裏簡單暴力地吧preclude和node_modules目錄下的文件所有過濾掉,只打本身寫的代碼。
// 只有本身寫的纔算是業務代碼
function postProcessModulesFilter(module) {
//返回false則過濾不編譯
if (module.path.indexOf('__prelude__') >= 0) {
return false;
}
if (module.path.indexOf(pathSep + 'node_modules' + pathSep) > 0) {
if (`js${pathSep}script${pathSep}virtual` === module.output[0].type) {
return true;
}
if (
module.path.indexOf(
`${pathSep}node_modules${pathSep}@babel${pathSep}runtime${pathSep}helpers`
) > 0
) {
//添加這個判斷,讓@babel/runtime打進包去
return true;
}
return false;
}
return true;
}
module.exports = postProcessModulesFilter;
複製代碼
common.config.js
和 business.config.js
common.config.js
:
// 基礎打包配置
const createModuleIdFactory = require('./config/createModuleIdFactory');
module.exports = {
transformer: {
getTransformOptions: async () => ({
transform: {
experimentalImportSupport: true,
inlineRequires: false,
},
}),
},
serializer: {
// 全部模塊一經轉換就會被序列化,Serialization會組合這些模塊來生成一個或多個包,包就是將模塊組合成一個JavaScript文件的包。
// 函數傳入的是你要打包的module文件的絕對路徑返回的是這個module的id
// 配置createModuleIdFactory讓其每次打包都module們使用固定的id(路徑相關)
createModuleIdFactory: createModuleIdFactory,
/* serializer options */
},
};
複製代碼
business.config.js
:
// 業務代碼
const createModuleIdFactory = require('./config/createModuleIdFactory');
const postProcessModulesFilter = require('./config/postProcessModulesFilter');
module.exports = {
transformer: {
getTransformOptions: async () => ({
transform: {
experimentalImportSupport: true,
inlineRequires: false,
},
}),
},
serializer: {
// 函數傳入的是你要打包的module文件的絕對路徑返回的是這個module的id
createModuleIdFactory: createModuleIdFactory,
// A filter function to discard specific modules from the output.
// 數傳入的是module信息,返回是boolean值,若是是false就過濾不打包
// 配置processModuleFilter過濾基礎包打出業務包
processModuleFilter: postProcessModulesFilter,
/* serializer options */
},
};
複製代碼
config
目錄新建 index.js
,用於設置打包配置index.js
:
// 基礎打包配置
/** * version - js bundle版本,初始值是1,每次更新請手動加1 * common - 公共基礎包bundle * bundles - 業務模塊基礎包bundle * { "animate": true, // ios平臺會使用, 當從A頁面轉場到B頁面的時候,控制[self.navigationControllersetNavigationBarHidden: animated:];中的animate "statusBgColor": "#408EF5", //導航狀態欄的背景色 "type": "push", // 進入rn的形式(2種, push和present) "source": "Login", // 用於拆包打包時的入口entry_file "moduleName": "platform", // 模塊名稱,和 AppRegistry.registerComponent('platform', () => App);一一對應 "bundleName": "platform.bundle" // 此模塊打包出來的bundle名稱, 生產環境使用 } */
module.exports = {
version: 1,
common: {
moduleName: 'platform',
bundleName: 'platform.bundle',
},
bundles: [
{
animate: false,
statusBgColor: '#408EF5',
type: 'push',
source: 'mine',
moduleName: 'rbd_mine',
bundleName: 'rbd_mine.bundle',
},
{
animate: true,
statusBgColor: '#ffffff',
type: 'push',
source: 'discover',
moduleName: 'rbd_discover',
bundleName: 'rbd_discover.bundle',
},
],
};
複製代碼
node
腳本,進行打包,包含熱更新(文件級別增量升級)注:<>
表示必選,[]
表示可選,-v 默認取 config/index.js 的 version
目前是文件級別增量升級,下一步是內容級別增量升級(diff-match-patch)
package.json
增長
"bin": {
"rn": "./scripts/command.js"
},
複製代碼
在項目目錄執行 npm link
,以後就能夠用 rn
腳手架命令了:
rn pack <platform> <type> [-v version]
: 打包 android/ios 的 version 版本的 common/business 包,並壓縮至 upload 目錄rn patch <platform> [-v version]
: 打包 android/ios 的 version 版本的增量包,並壓縮至 upload 目錄rn hash <target> [-v version]
: target 能夠是文件路徑(此時無需-v 參數)、ios、android,獲取文件指紋(md5)腳本內容有點多,請直接看代碼吧: command
爲了保證 jsbundle 版本和原生 native 版本保持同步,須要前端和原生人員共同維護一個配置文件,例如放到 firebase:
附:repo
===🧐🧐 文中不足,歡迎指正 🤪🤪===