npm package discovery and stats viewer.

Discover Tips

  • General search

    [free text search, go nuts!]

  • Package details

    pkg:[package-name]

  • User packages

    @[username]

Sponsor

Optimize Toolset

I’ve always been into building performant and accessible sites, but lately I’ve been taking it extremely seriously. So much so that I’ve been building a tool to help me optimize and monitor the sites that I build to make sure that I’m making an attempt to offer the best experience to those who visit them. If you’re into performant, accessible and SEO friendly sites, you might like it too! You can check it out at Optimize Toolset.

About

Hi, 👋, I’m Ryan Hefner  and I built this site for me, and you! The goal of this site was to provide an easy way for me to check the stats on my npm packages, both for prioritizing issues and updates, and to give me a little kick in the pants to keep up on stuff.

As I was building it, I realized that I was actually using the tool to build the tool, and figured I might as well put this out there and hopefully others will find it to be a fast and useful way to search and browse npm packages as I have.

If you’re interested in other things I’m working on, follow me on Twitter or check out the open source projects I’ve been publishing on GitHub.

I am also working on a Twitter bot for this site to tweet the most popular, newest, random packages from npm. Please follow that account now and it will start sending out packages soon–ish.

Open Software & Tools

This site wouldn’t be possible without the immense generosity and tireless efforts from the people who make contributions to the world and share their work via open source initiatives. Thank you 🙏

© 2024 – Pkg Stats / Ryan Hefner

fego-rn-update

v0.0.16

Published

fego-rn官方热更新方案

Downloads

3

Readme

fego-rn-update · npm version PRs Welcome

  • fego-rn 官方热更新方案

项目介绍

  • 基于React Native(0.47~0.58)的热更新库
  • 提供android和ios两端的支持
  • 支持全量、增量更新,配置简单,部署方便,一键打包
  • 支持字体文件更新
  • 针对apk版本进行更新,支持非强制更新模式
  • 支持设置增量包最大生成数量,限制包生成个数

支持平台

  • android

  • ios

API文档

iOS

Android

设计原则

  • ios和android两端使用同一套脚本
  • 打全量包和增量包只需要执行一个脚本即可

目录结构

  • 整体目录结构
.
├── FegoRnUpdate.podspec      # ios pod库的描述文件
├── android                   # android原生源码
├── demo                      # demo示例
│   ├── App.js                # js主代码
│   ├── android               # android工程
│   ├── increment             # 增量包、全量包存储路径
│   ├── index.js              # js入口文件
│   ├── ios                   # ios工程
│   ├── pkg.sh                # 主打包脚本文件
│   ├── pkgCmd                # 辅助脚本文件夹
│   └── resource              # 存放字体文件
├── docs                      # api文档
├── increment                 # 包生成目录
├── index.js                  # js源码
├── ios                       # ios源码
├── package.json              # 项目描述文件
├── pkg.sh                    # 打包文件
└── pkgCmd                    # 辅助脚本文件夹
  • 打包脚本目录
.
├── pkg.sh                    # 整体打包文件,包含全量打包和增量打包,主执行脚本文件
└── pkgCmd                    # 辅助脚本文件夹
    ├── allPkg.sh             # 全量包打包脚本
    ├── config.js             # 配置文件,主要配置生成包的存储位置
    ├── incre                 # bundle和assets增量生成脚本
    │   ├── assets.js         # 资源assets增量生成脚本
    │   └── jsbundle.js       # bundle增量生成脚本
    ├── incregen.js           # 增量包生成脚本
    ├── md5.js                # 生成全量更新config
    ├── third                 # 依赖的第三方脚本
    │   ├── diff_match_patch_uncompressed.js      # 文件差异生成脚本
    │   └── file_list.js      # 列出目录下所有的文件
    └── Utils                 # 公共方法

安装

$ npm install fego-rn-update --save

手动安装

Android

  1. 把下面几行添加到 android/setting.gradle
include ':fego-rn-update'

project(':fego-rn-update').projectDir = new File(rootProject.projectDir, '../node_modules/fego-rn-update/android')
  1. android/build.gradle中更新build工具版本为2.2+
buildscript {
    ...
    dependencies {
        classpath 'com.android.tools.build:gradle:2.2.3'
    }
}
  1. android/gradle/wrapper/gradle-wrapper.properties中更新gradle版本为2.14.1以上
...
distributionUrl=https\://services.gradle.org/distributions/gradle-3.3-all.zip
  1. android/app/build.gradle添加依赖
dependencies {
    compile project(':fego-rn-update')
    // 需要添加以下依赖项
    compile "com.squareup.retrofit2:retrofit:2.1.0"
    compile "com.squareup.retrofit2:converter-gson:2.0.0"
}
  1. AndroidManifest.xml中添加依赖
<uses-permission android:name="android.permission.INTERNET" />
  1. 初始化ReactManager
ReactManager.getInstance().init(getApplication(), "index", "index.jsbundle", "https://raw.githubusercontent.com/fegos/fego-rn-update/master/demo/increment/android/");
  1. 生成ReactRootView时,需要使用ReactManager中生成的RnInstanceManager
// 业务名
if (mReactRootView == null) {
    mReactRootView = new ReactRootView(this);
    if (mReactInstanceManager == null) {
        if (ReactManager.getInstance().getRnInstanceManager() == null) {
            List<ReactPackage> reactPackages = new ArrayList<>();
            // 添加额外的package
            reactPackages.add(new HotUpdatePackage());
            ReactManager.getInstance().loadBundle(reactPackages, BuildConfig.DEBUG);
        }
		mReactInstanceManager = ReactManager.getInstance().getRnInstanceManager();
	  }
    mReactRootView = ReactManager.getInstance().getReactViewByModuleName("hotUpdate", this, null);
    setContentView(mReactRootView);
}

8、调用热更新代码(也可js端调用)

SuccessListener sucListener;// 一般是activity实现了该接口,可以为null,为null时表示不交给用户处理,而是内部默认解压加载
FailListener failListener;	// 一般是activity实现了该接口,可以为null
ReactManager.getInstance().loadBundleBehind(sucListener, failListenr);

9、处理结果通知

  • 默认全部更新,不需做任何其他处理

  • 可以分别实现SuccessListener、FailListener,来处理成功和失败的情况

@Override
public void onSuccess() {
    questionUpdateReactSource();// 可以弹窗提示
}

protected void questionUpdateReactSource() {
    //此处标记已经下载了新的rn资源包,提示用户是否进行更新
    AlertDialog dialog = new AlertDialog.Builder(this)
            .setTitle("温馨提示")
            .setMessage("有新的资源包可以更新,是否立即更新?")
            .setNegativeButton("取消", new DialogInterface.OnClickListener() {
                @Override
                public void onClick(DialogInterface dialog, int which) {
                    dialog.cancel();
                }
            })
            .setPositiveButton("确定", new DialogInterface.OnClickListener() {
                @Override
                public void onClick(DialogInterface dialog, int which) {
                    ReactManager.getInstance().unzipBundle();
                    ReactManager.getInstance().doReloadBundle();
                    // 下次启动应用时更新
                    // ReactManager.getInstance().unzipBundle();
                }
            })
            .create();
    dialog.show();
}

@Override
public void onFail(ReactManager.NPReactManagerTask task) {
    if (task == ReactManager.NPReactManagerTask.GetConfigFail) {
        // 获取config失败
    } else if (task == ReactManager.NPReactManagerTask.GetSourceFail) {
        // 获取zip包失败
    } else if (task == ReactManager.NPReactManagerTask.Md5VerifyFail) {
        // md5验证失败
    }
}

注意

如果实现了SuccessListener,则不会解压新包,也不会自动加载最新bundle,所有成功后的操作需要自行实现,可以调用下面的方法进行重新加载

// 仅解压包,不执行下面的操作时下次启动自动更新
ReactManager.getInstance().unzipBundle();
// 加载新bundle
ReactManager.getInstance().doReloadBundle();

IOS

  1. pod库引入热更新库,Podfile中添加:
pod 'FegoRnUpdate'
  1. 工程目录下执行pod命令:
pod update

3.调用热更新代码

NIPRnManager *manager = [NIPRnManager sharedManager];
manager.delegate = self;
manager.bundleUrl = @"https://raw.githubusercontent.com/fegos/fego-rn-update/master/demo/increment/ios/increment";
manager.noHotUpdate = NO;
manager.noJsServer = YES;
[manager requestRCTAssetsBehind];

4.处理代理结果

-(void)successHandlerWithFilePath:(NSString *)filePath{
    NSLog(@"NIPHotReloadSuccess");
    [[NIPRnManager sharedManager] unzipBundle:filePath];
    [self loadRnController];
}
-(void)failedHandlerWithStatus:(HotReloadStatus)status{
    switch (status) {
    case NIPReadConfigFailed:
    {
    NSLog(@"NIPReadConfigFailed");
    }
    break;
    case NIPDownloadBundleFailed:
    {
    NSLog(@"NIPDownloadBundleFailed");
    }
    break;
    case NIPMD5CheckFailed:
    {
    NSLog(@"NIPMD5CheckFailed");
    }
    break;
    default:
    break;
    }

}
  1. AppDelegate中添加热更新代码的例子
#import "NIPRnManager.h"

- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions
{
    self.window = [[UIWindow alloc] initWithFrame:[[UIScreen mainScreen] bounds]];
    [self loadRnController];
    [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(loadRnController) name:@"RNHotReloadRequestSuccess" object:nil];

  	return YES;
}

- (void)loadRnController {
    /**
    @param bundleUrl 服务器存放bundle的地址
    @param noHotUpdate 用来标记只使用工程自带的rn包,不支持热更新 default:NO
    @param noJsServer 不通过本地启动的server来获取bundle,直接使用离线包 default:NO
    @param moduleName 默认main bundle的指定模块
    */
    NIPRnController *controller = [[NIPRnManager managerWithBundleUrl:@"bundle下载路径" noHotUpdate:NO noJsServer:YES] loadControllerWithModel:@"moduleName"];

    #pragma clang diagnostic push
    #pragma clang diagnostic ignored "-Wincompatible-pointer-types"
    self.window.rootViewController = controller;
    #pragma clang diagnostic pop
}

使用

  1. node_modules/fego-rn-update/pkdCmd文件夹pkg.sh文件拷贝到与node_modules同级目录

  2. 在想要生成包的地方创建包存储目录

存储目录可以参考node_modules/fego-rn-update/下的increment文件夹(其内部是android和ios目录均是自动生成的,只提供生成包最外层目录即可)

.
├── React-Native 热更新目录
├── android                 # 存放android生成的包
│   	├── all             # 存放全量包
│     │   ├── README.md   
│     │   └── temp        # 该目录为自动生成,存放解压后的包,该目录可添加到.gitignore文件中
│    	├── config          # 最终的config,该文件会自动生成,默认为增量
│     └── increment       # 存放增量包
│         └── README.md
└── ios                     # 存放ios生成的包
      ├── all             # 存放全量包
      │   └── README.md
      │   └── temp        # 该目录为自动生成,存放解压后的包,该目录可添加到.gitignore文件中
      ├── config          # 最终的config,该文件会自动生成,默认为增量
      └── increment       # 存放增量包
          └── README.md   
  1. 修改配置文件config.js中的pathapkVerbundleNamemaxGenNum(config.js文件位于pkgCmd/下)

    path:生成包存储路径 apkVer:apk版本号 bundleName:打包bundle文件名 maxGenNum:增量包生成最大数量

// 写个用户名跟路径对应的字典,这个方便一个工程多个人维护使用,支持mac
let map = {
	/**
	 * 注意:
	 * 1、username为电脑用户名;
	 * 2、path为包存储位置,末尾需要加“/”,否则会报路径错误
	 */
	username1: 'path1',
	username2: 'path2'
}
// 获取系统信息
let os = require('os');
// 获取本机当前用户名
let username = os.userInfo().username;
console.log(map[username]);
module.exports = {
	path: map[username],//在此处可以直接更改为自己要生成包的位置
	apkVer: '1.0',//需跟apk版本保持一致
	bundleName: 'index.jsbundle', //需跟原生中保持一致
	maxGenNum: 2, // 增量包生成最大数量
}
  1. 更新字体文件

需在pkg.sh同级目录下创建resource,并将ttf文件存放于该目录下

注意

不同的业务的ttf命名需不同
  1. node_modules同级目录下执行脚本pkg.sh
# platform 平台 ,android或ios,不设置则默认两端都进行生成包操作
# type 更新类型,increment或all,默认为increment,如果type设置了,则说明要更换更新类型,否则默认是去生成包;该属性默认是在生成完好包之后再进行该操作
sh pkg.sh platform type

注意

+ 首次运行,因为只生成一个包,故会提示没有新包,不会生成增量包;
+ 运行之后需要在android和ios两个工程中均放置一份解压后的包,android放在`assets/rn/`下,ios放于`项目名/rn/`下(目录若需调整,需要修改原生代码,建议不修改),每次app大版本变化时需要进行该操作;
+ 之后在同一sdk版本下继续运行该脚本时,会进行增量包生成。
+ 生成包之后,需要上传到服务器,用于原生更新时请求,此时的地址就是上面需要在原生中配置的sourceUrl
+ 如果想更换更新方式,可以执行带type参数的脚本,此时会选择将全量还是增量更新的config拷贝到platform/config(此config才是真正的更新config),默认使用的增量模式
  1. 在原生修改几处
  • 启动文件名字为index.js
  • bundle名字为index.jsbundle
  • 请求地址为config请求地址(如demo中,请求地址为https://raw.githubusercontent.com/fegos/fego-rn-update/master/demo/increment/android/, ios平台只需替换地址中的android即可,ios无需加末尾的‘/’)

android:在MainActivity中修改

ios:在AppDelegate中修改

注意

+ android和ios需要统一启动文件名称,均为index.js,否则需要修改全量打包脚本;
+ bundle名字也需要两端统一为index.jsbundle,原生和js端均可配置,一致即可
  1. js端调用
import FegoRNUpdate from 'fego-rn-update'

class App extends Component {
	render() {
		return (
			<View style={styles.container}>
				<TouchableHighlight
					underlayColor="transparent"
					onPress={() => {
						FegoRNUpdate.hotReload();
					}}>
					<Text style={styles.btnText}>热更新测试</Text>
				</TouchableHighlight>
			</View>
		);
	}
}

const styles = StyleSheet.create({
	container: {
		flex: 1,
		justifyContent: 'center',
		alignItems: 'center',
		backgroundColor: '#F5FCFF',
	},
	btnText: {
		color: 'blue',
		fontSize: 16
	}
});

欢迎贡献

有任何疑问或问题欢迎在 github issues里提问